Add support for the zoomed state to managed windows.
[gwm.git] / managed.c
blobce8be768cbf41aa9643bf02b397dabc2490649e0
1 /*
2 * managed.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <xcb/xcb.h>
30 #if USE_SHAPE
31 #include <xcb/shape.h>
32 #endif
34 #include "gwm.h"
36 #include "frame.h"
37 #include "managed.h"
38 #include "utf8.h"
39 #include "window-table.h"
41 extern void set_managed_state( struct gwm_window *window, int state ) {
43 uint32_t values[ 2 ];
45 assert( window->type == WINDOW_MANAGED );
47 /* Place a WM_STATE property on the client window (ICCCM 2.0, section
48 4.1.3.1). */
49 values[ 0 ] = window->u.managed.state = state;
50 values[ 1 ] = XCB_NONE;
51 xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->w,
52 atoms[ ATOM_WM_STATE ], atoms[ ATOM_WM_STATE ],
53 32, 2, values );
56 struct managed_net_state {
57 xcb_window_t w;
58 xcb_atom_t state;
59 int action;
62 static void handle_set_managed_net_state( unsigned int sequence, void *r,
63 xcb_generic_error_t *error,
64 union callback_param cp ) {
66 struct managed_net_state *p = cp.p;
67 xcb_get_property_reply_t *reply = r;
69 if( error ) {
70 /* Ignore Window errors, since the window might have been destroyed
71 in the meantime. */
72 if( error->error_code != XCB_WINDOW )
73 show_error( error );
75 free( error );
78 if( reply ) {
79 struct gwm_window *window = lookup_window( p->w );
80 xcb_atom_t *state = xcb_get_property_value( reply );
81 int state_len = reply->format == 32 ? reply->value_len : 0;
83 /* Don't assume the window is valid: it might have been destroyed
84 or forgotten asynchronously. */
85 if( window && window->type == WINDOW_MANAGED ) {
86 int len, i, found = FALSE;
88 for( i = 0; i < state_len; i++ )
89 if( state[ i ] == p->state ) {
90 found = TRUE;
91 break;
94 if( found && p->action != NET_STATE_ADD ) {
95 xcb_atom_t *new_state = alloca( ( state_len - 1 ) *
96 sizeof (xcb_atom_t) );
98 for( i = 0, len = 0; i < state_len; i++ )
99 if( state[ i ] != p->state &&
100 ( p->state !=
101 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] ||
102 state[ i ] !=
103 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] ) )
104 new_state[ len++ ] = state[ i ];
106 xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->w,
107 atoms[ ATOM__NET_WM_STATE ], ATOM, 32,
108 len, new_state );
109 } else if( !found && p->action != NET_STATE_REMOVE ) {
110 xcb_atom_t new_state[ 2 ];
112 if( ( new_state[ 0 ] = p->state ) ==
113 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] ) {
114 new_state[ 1 ] = atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ];
115 len = 2;
116 } else
117 len = 1;
119 xcb_change_property( c, XCB_PROP_MODE_APPEND, window->w,
120 atoms[ ATOM__NET_WM_STATE ], ATOM, 32,
121 len, new_state );
125 free( reply );
128 free( p );
131 extern void set_managed_net_state( struct gwm_window *window,
132 xcb_atom_t state, int action ) {
134 struct managed_net_state *p = xmalloc( sizeof *p );
135 union callback_param cp;
137 p->w = window->w;
138 p->state = state;
139 p->action = action;
141 cp.p = p;
142 handle_async_reply( xcb_get_property( c, FALSE, window->w,
143 atoms[ ATOM__NET_WM_STATE ],
144 ATOM, 0, PROP_SIZE ).sequence,
145 handle_set_managed_net_state, cp );
148 extern void iconic_to_normal( struct gwm_window *window ) {
150 assert( window->type == WINDOW_MANAGED );
152 set_managed_state( window, STATE_NORMAL );
153 xcb_map_window( c, window->w );
154 xcb_map_window( c, window->u.managed.frame->w );
157 extern void normal_to_iconic( struct gwm_window *window ) {
159 uint32_t n;
161 assert( window->type == WINDOW_MANAGED );
163 set_managed_state( window, STATE_ICONIC );
165 /* We need to unmap our child without invoking the normal
166 UnmapNotify handler response. Unfortunately it's very
167 difficult to communicate to the handler the distinction between
168 the resultant UnmapNotify from an unmap here and an unrelated
169 event. Checking the request sequence is not robust, because
170 asynchronous events might not be assigned unique sequence
171 numbers. Instead, we grab the server and temporarily change
172 our event mask, which seems a heavyweight approach but does
173 guarantee that only this event will be ignored. */
174 xcb_grab_server( c );
176 n = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE;
177 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
179 xcb_unmap_window( c, window->w );
181 n = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE |
182 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
183 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
185 xcb_ungrab_server( c );
187 xcb_unmap_window( c, window->u.managed.frame->w );
190 static void managed_destroy_notify( struct gwm_window *window,
191 xcb_destroy_notify_event_t *ev ) {
193 assert( window->type == WINDOW_MANAGED );
195 /* This isn't the preferred notification mechanism, but it can
196 happen: if a client has a window in the Iconic state and either
197 terminates abnormally or doesn't send a synthetic UnmapNotify
198 (as specified in ICCCM 2.0, section 4.1.4), then we might not
199 find out about it until we receive a DestroyNotify. */
200 unmanage_window( window );
203 static void managed_unmap_notify( struct gwm_window *window,
204 xcb_unmap_notify_event_t *ev ) {
206 assert( window->type == WINDOW_MANAGED );
208 /* A transition to the Withdrawn state (ICCCM 2.0, section 4.1.4). */
209 unmanage_window( window );
212 static void managed_reparent_notify( struct gwm_window *window,
213 xcb_reparent_notify_event_t *ev ) {
215 assert( window->type == WINDOW_MANAGED );
217 if( ev->parent != window->u.managed.frame->w )
218 /* Handle an obscure case: a client window being reparented away from
219 our frame while iconified. Much like managed_destroy_notify. */
220 unmanage_window( window->u.frame.child );
223 struct managed_get_property {
224 xcb_window_t w;
225 enum gwm_property_type prop;
228 static void handle_managed_get_property( unsigned int sequence, void *reply,
229 xcb_generic_error_t *error,
230 union callback_param cp ) {
232 struct managed_get_property *p = cp.p;
234 if( error ) {
235 /* Ignore Window errors, since the window might have been destroyed
236 in the meantime. */
237 if( error->error_code != XCB_WINDOW )
238 show_error( error );
240 free( error );
243 if( reply ) {
244 struct gwm_window *window = lookup_window( p->w );
246 /* Don't assume the window is valid: it might have been destroyed
247 or forgotten asynchronously. */
248 if( window && window->type == WINDOW_MANAGED )
249 managed_property_change( window, p->prop, reply );
251 free( reply );
254 free( p );
257 extern void async_get_property( struct gwm_window *window,
258 enum gwm_property_type prop ) {
260 struct managed_get_property *p = xmalloc( sizeof *p );
261 union callback_param cp;
263 p->w = window->w;
264 p->prop = prop;
266 cp.p = p;
267 handle_async_reply( xcb_get_property( c, FALSE, window->w,
268 prop_atoms[ prop ],
269 prop_types[ prop ], 0,
270 PROP_SIZE ).sequence,
271 handle_managed_get_property, cp );
274 struct managed_get_geometry {
275 struct gwm_window *window;
276 xcb_pixmap_t icon, mask;
279 static void handle_get_geometry( unsigned int sequence, void *reply,
280 xcb_generic_error_t *error,
281 union callback_param cp ) {
283 struct managed_get_geometry *p = cp.p;
285 if( error ) {
286 /* Ignore Drawable errors, since it was specified by the client. */
287 if( error->error_code != XCB_DRAWABLE )
288 show_error( error );
290 free( error );
292 if( !reply )
293 replace_icons( p->window, 0, NULL, NULL, NULL, &p->icon );
296 if( reply ) {
297 xcb_get_geometry_reply_t *r = reply;
298 int width, height;
299 xcb_pixmap_t icons[ 2 ];
301 width = r->width;
302 height = r->height;
303 icons[ 0 ] = p->icon;
304 icons[ 1 ] = p->mask;
305 replace_icons( p->window, 1, &width, &height, NULL, icons );
307 free( reply );
310 free( p );
313 #define MOTIF_WM_HINTS_DECORATIONS 0x2
315 #define MOTIF_WM_HINTS_FLAGS_OFF 0
316 #define MOTIF_WM_HINTS_DECORATIONS_OFF 2
317 #define MOTIF_WM_HINTS_MIN_SIZE 3 /* ignore hint properties smaller than this */
319 #define MOTIF_WM_HINTS_DEC_ALL 0x1
320 #define MOTIF_WM_HINTS_DEC_BORDER 0x2
321 #define MOTIF_WM_HINTS_DEC_TITLE 0x8
323 #define WM_HINTS_INPUT 0x01
324 #define WM_HINTS_STATE 0x02
325 #define WM_HINTS_ICON 0x04
326 #define WM_HINTS_ICON_MASK 0x20
328 #define WM_HINTS_FLAGS_OFF 0
329 #define WM_HINTS_INPUT_OFF 1
330 #define WM_HINTS_STATE_OFF 2
331 #define WM_HINTS_ICON_OFF 3
332 #define WM_HINTS_MASK_OFF 7
334 #define WM_HINTS_STATE_NORMAL 1
335 #define WM_HINTS_STATE_ICONIC 3
337 #define WM_NORMAL_HINTS_USER_POSITION 0x001
338 #define WM_NORMAL_HINTS_PROGRAM_POSITION 0x004
339 #define WM_NORMAL_HINTS_MIN_SIZE 0x010
340 #define WM_NORMAL_HINTS_MAX_SIZE 0x020
341 #define WM_NORMAL_HINTS_SIZE_INC 0x040
342 #define WM_NORMAL_HINTS_ASPECT 0x080
343 #define WM_NORMAL_HINTS_BASE_SIZE 0x100
344 #define WM_NORMAL_HINTS_WIN_GRAVITY 0x200
346 #define WM_NORMAL_HINTS_FLAGS_OFF 0
347 #define WM_NORMAL_HINTS_MIN_SIZE_OFF 5
348 #define WM_NORMAL_HINTS_MAX_SIZE_OFF 7
349 #define WM_NORMAL_HINTS_SIZE_INC_OFF 9
350 #define WM_NORMAL_HINTS_ASPECT_OFF 11
351 #define WM_NORMAL_HINTS_BASE_SIZE_OFF 15
352 #define WM_NORMAL_HINTS_WIN_GRAVITY_OFF 17
354 static void enter_zoom( struct gwm_window *window ) {
356 int x, y, width, height;
357 uint32_t values[ 4 ];
359 assert( window->type == WINDOW_FRAME );
361 x = window->u.frame.unzoomed_x = window->u.frame.x;
362 y = window->u.frame.unzoomed_y = window->u.frame.y;
363 width = window->u.frame.unzoomed_width = window->u.frame.width;
364 height = window->u.frame.unzoomed_height = window->u.frame.height;
365 window->u.frame.unzoomed_decoration = window->u.frame.decoration;
367 #if USE_RANDR
368 if( have_extension[ EXT_RANDR ] ) {
369 int crtc, best_crtc = 0;
370 long best_overlap = 0, best_size = 0;
372 for( crtc = 0; crtc < gwm_screens[ window->screen ].num_crtcs;
373 crtc++ ) {
374 struct gwm_crtc *c = gwm_screens[ window->screen ].crtcs[ crtc ];
375 int overlap;
377 if( x + width <= c->x || x >= c->x + c->width ||
378 y + height <= c->y || y >= c->y + c->height )
379 overlap = 0;
380 else {
381 int l, r, t, b;
383 l = x < c->x ? c->x : x;
384 r = x + width > c->x + c->width ? c->x + c->width : x + width;
385 t = y < c->y ? c->y : y;
386 b = y + height > c->y + c->height ? c->y + c->height :
387 y + height;
389 overlap = ( r - l ) * ( b - t );
392 if( overlap > best_overlap ||
393 ( overlap == best_overlap &&
394 c->width * c->height > best_size ) ) {
395 best_crtc = crtc;
396 best_overlap = overlap;
397 best_size = c->width * c->height;
401 x = gwm_screens[ window->screen ].crtcs[ best_crtc ]->x;
402 y = gwm_screens[ window->screen ].crtcs[ best_crtc ]->y;
403 width = gwm_screens[ window->screen ].crtcs[ best_crtc ]->width;
404 height = gwm_screens[ window->screen ].crtcs[ best_crtc ]->height;
405 } else
406 #endif
408 x = y = 0;
409 width = screens[ window->screen ]->width_in_pixels;
410 height = screens[ window->screen ]->height_in_pixels;
413 /* If the size we came up with violates the client's min or max size,
414 give up and revert to unzoomed. We'll have to ignore increment and
415 aspect ratio constraints, since we really have no freedom to attempt
416 to satisfy those. */
417 if( width < window->u.frame.child->u.managed.min_width ||
418 width > window->u.frame.child->u.managed.max_width ||
419 height < window->u.frame.child->u.managed.min_height ||
420 height > window->u.frame.child->u.managed.max_height ) {
421 window->u.frame.zoom_state = 0;
422 set_managed_net_state( window->u.frame.child,
423 atoms[ ATOM__NET_WM_STATE_FULLSCREEN ],
424 NET_STATE_REMOVE );
425 set_managed_net_state( window->u.frame.child,
426 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ],
427 NET_STATE_REMOVE );
429 return;
432 if( window->u.frame.decoration & DEC_TITLE )
433 xcb_unmap_window( c, window->u.frame.button->w );
435 window->u.frame.decoration = 0;
437 values[ 0 ] = window->u.frame.x = x;
438 values[ 1 ] = window->u.frame.y = y;
439 values[ 2 ] = window->u.frame.width = width;
440 values[ 3 ] = window->u.frame.height = height;
441 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
442 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
443 XCB_CONFIG_WINDOW_HEIGHT, values );
445 values[ 0 ] = values[ 1 ] = 0;
446 xcb_configure_window( c, window->u.frame.child->w, XCB_CONFIG_WINDOW_X |
447 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
448 XCB_CONFIG_WINDOW_HEIGHT, values );
451 static void leave_zoom( struct gwm_window *window ) {
453 uint32_t values[ 4 ];
454 int client_width, client_height;
456 assert( window->type == WINDOW_FRAME );
458 if( window->u.frame.unzoomed_width > window->u.frame.width - 16 ) {
459 window->u.frame.x += 8;
460 window->u.frame.width -= 16;
461 } else {
462 window->u.frame.x = window->u.frame.unzoomed_x;
463 window->u.frame.width = window->u.frame.unzoomed_width;
466 if( window->u.frame.unzoomed_height > window->u.frame.height - 16 ) {
467 window->u.frame.y += 8;
468 window->u.frame.height -= 16;
469 } else {
470 window->u.frame.y = window->u.frame.unzoomed_y;
471 window->u.frame.height = window->u.frame.unzoomed_height;
474 window->u.frame.decoration = window->u.frame.unzoomed_decoration;
476 client_width = window->u.frame.width - frame_l( window, FALSE ) -
477 frame_r( window, FALSE );
478 client_height = window->u.frame.height - frame_t( window, FALSE ) -
479 frame_b( window, FALSE );
480 apply_size_constraints( window->u.frame.child, &client_width,
481 &client_height );
483 window->u.frame.width = client_width + frame_l( window, FALSE ) +
484 frame_r( window, FALSE );
485 window->u.frame.height = client_height + frame_t( window, FALSE ) +
486 frame_b( window, FALSE );
488 if( window->u.frame.decoration & DEC_TITLE )
489 xcb_map_window( c, window->u.frame.button->w );
491 values[ 0 ] = window->u.frame.x;
492 values[ 1 ] = window->u.frame.y;
493 values[ 2 ] = window->u.frame.width;
494 values[ 3 ] = window->u.frame.height;
495 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
496 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
497 XCB_CONFIG_WINDOW_HEIGHT, values );
499 values[ 0 ] = frame_l( window, FALSE );
500 values[ 1 ] = frame_t( window, FALSE );
501 values[ 2 ] -= frame_l( window, FALSE ) + frame_r( window, FALSE );
502 values[ 3 ] -= frame_t( window, FALSE ) + frame_b( window, FALSE );
503 xcb_configure_window( c, window->u.frame.child->w, XCB_CONFIG_WINDOW_X |
504 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
505 XCB_CONFIG_WINDOW_HEIGHT, values );
508 extern void managed_property_change( struct gwm_window *window, int prop,
509 xcb_get_property_reply_t *p ) {
511 uint32_t *p32, n32;
512 int i;
513 struct xcb_screen_t *screen;
514 int value_len;
515 int dec, old_dec, old_zoom;
516 int num_icons;
517 int len;
519 assert( window->type == WINDOW_MANAGED );
521 switch( prop ) {
522 case PROP__MOTIF_WM_HINTS:
523 /* _MOTIF_WM_HINTS property. */
524 old_dec = window->u.managed.frame->u.frame.decoration;
525 dec = DEC_DEFAULT;
527 if( p->format == 32 && p->value_len >= MOTIF_WM_HINTS_MIN_SIZE ) {
528 p32 = xcb_get_property_value( p );
530 if( p32[ MOTIF_WM_HINTS_FLAGS_OFF ] &
531 MOTIF_WM_HINTS_DECORATIONS ) {
532 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
533 MOTIF_WM_HINTS_DEC_ALL ) {
534 dec = DEC_BORDER | DEC_TITLE;
536 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
537 MOTIF_WM_HINTS_DEC_BORDER )
538 dec &= ~DEC_BORDER;
540 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
541 MOTIF_WM_HINTS_DEC_TITLE )
542 dec &= ~DEC_TITLE;
543 } else {
544 dec = 0;
546 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
547 MOTIF_WM_HINTS_DEC_BORDER )
548 dec |= DEC_BORDER;
550 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
551 MOTIF_WM_HINTS_DEC_TITLE )
552 dec |= DEC_TITLE;
557 #if USE_SHAPE
558 if( window->u.managed.shaped )
559 /* Never apply borders to shaped windows. */
560 dec &= ~DEC_BORDER;
561 #endif
563 if( window->u.managed.frame->u.frame.zoom_state )
564 window->u.managed.frame->u.frame.unzoomed_decoration = dec;
565 else if( window->u.managed.state != STATE_NORMAL )
566 window->u.managed.frame->u.frame.decoration = dec;
567 else if( dec != old_dec ) {
568 int x, y, width, height;
569 uint32_t n[ 5 ];
571 translate_frame_to_child( window->u.managed.frame, &x, &y,
572 window->u.managed.frame->u.frame.x,
573 window->u.managed.frame->u.frame.y,
574 window->u.managed.border_width,
575 window->u.managed.win_gravity );
577 window->u.managed.frame->u.frame.decoration = dec;
579 translate_child_to_frame( window->u.managed.frame, &x, &y,
580 &width, &height, x, y,
581 window->u.managed.frame->u.frame.width -
582 frame_l( window->u.managed.frame,
583 FALSE ) -
584 frame_r( window->u.managed.frame,
585 FALSE ),
586 window->u.managed.frame->u.frame.height -
587 frame_t( window->u.managed.frame,
588 FALSE ) -
589 frame_b( window->u.managed.frame,
590 FALSE ),
591 window->u.managed.border_width,
592 window->u.managed.win_gravity );
594 if( dec & DEC_TITLE )
595 xcb_map_window( c,
596 window->u.managed.frame->u.frame.button->w );
597 else
598 xcb_unmap_window( c,
599 window->u.managed.frame->u.frame.button->w );
601 n[ 0 ] = frame_l( window->u.managed.frame, FALSE );
602 n[ 1 ] = frame_l( window->u.managed.frame, FALSE );
603 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
604 XCB_CONFIG_WINDOW_Y, n );
606 n[ 0 ] = x;
607 n[ 1 ] = y;
608 n[ 2 ] = width;
609 n[ 3 ] = height;
610 n[ 4 ] = frame_xb( window->u.managed.frame );
611 /* We'll also notify the client of any changes, in the
612 ConfigureNotify handler for the event we expect to receive in
613 response to this request. */
614 xcb_configure_window( c, window->u.managed.frame->w,
615 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
616 XCB_CONFIG_WINDOW_WIDTH |
617 XCB_CONFIG_WINDOW_HEIGHT |
618 XCB_CONFIG_WINDOW_BORDER_WIDTH, n );
620 update_frame_extents( window->u.managed.frame );
623 break;
625 case PROP__NET_WM_ICON:
626 /* _NET_WM_ICON property (see EWMH "Application Window Properties". */
627 p32 = xcb_get_property_value( p );
628 len = p->format == 32 ? p->value_len : 0;
630 i = num_icons = 0;
631 while( len > i + 2 && len >= i + 2 + p32[ i ] * p32[ i + 1 ] ) {
632 i += p32[ i ] * p32[ i + 1 ] + 2;
633 num_icons++;
636 if( num_icons ) {
637 int *widths, *heights;
638 uint32_t **icons;
640 widths = alloca( num_icons * sizeof *widths );
641 heights = alloca( num_icons * sizeof *heights );
642 icons = alloca( num_icons * sizeof *icons );
644 i = num_icons = 0;
645 while( len > i + 2 &&
646 len >= i + 2 + ( widths[ num_icons ] = p32[ i ] ) *
647 ( heights[ num_icons ] = p32[ i + 1 ] ) ) {
648 icons[ num_icons ] = p32 + i + 2;
649 i += widths[ num_icons ] * heights[ num_icons ] + 2;
650 num_icons++;
653 replace_icons( window, num_icons, widths, heights, icons, NULL );
654 } else
655 replace_icons( window, 0, NULL, NULL, NULL, NULL );
657 break;
659 case PROP__NET_WM_NAME:
660 /* _NET_WM_NAME property (see EWMH "Application Window Properties". */
661 if( window->u.managed.name )
662 free( window->u.managed.name );
664 if( p->value_len && p->format == 8 ) {
665 window->u.managed.name = (char *) utf8_dup_valid_len(
666 xcb_get_property_value( p ), p->value_len );
667 window->u.managed.net_wm_name = TRUE;
669 if( window->u.managed.state == STATE_NORMAL &&
670 ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) )
671 queue_window_update( window->u.managed.frame, 0, 0,
672 window->u.managed.frame->u.frame.width,
673 frame_t( window->u.managed.frame, FALSE ),
674 FALSE );
675 } else {
676 window->u.managed.name = NULL;
677 window->u.managed.net_wm_name = FALSE;
678 /* We've lost the _NET_WM_NAME property. If the window still
679 has a plain WM_NAME, then fall back to that. */
680 async_get_property( window, PROP_WM_NAME );
683 break;
685 case PROP__NET_WM_STATE:
686 old_zoom = window->u.managed.frame->u.frame.zoom_state;
688 window->u.managed.frame->u.frame.zoom_state = 0;
690 if( p->format == 32 && p->value_len >= 1 ) {
691 p32 = xcb_get_property_value( p );
693 for( i = 0; i < p->value_len; i++ )
694 if( p32[ i ] == atoms[ ATOM__NET_WM_STATE_FULLSCREEN ] )
695 window->u.managed.frame->u.frame.zoom_state |=
696 ZOOM_FULLSCREEN;
697 else if( p32[ i ] ==
698 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] ||
699 p32[ i ] ==
700 atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] )
701 window->u.managed.frame->u.frame.zoom_state |=
702 ZOOM_MAXIMISE;
705 if( !old_zoom && window->u.managed.frame->u.frame.zoom_state )
706 enter_zoom( window->u.managed.frame );
708 if( old_zoom && !window->u.managed.frame->u.frame.zoom_state )
709 leave_zoom( window->u.managed.frame );
711 break;
713 case PROP_WM_COLORMAP_WINDOWS:
714 /* WM_COLORMAP_WINDOWS property (see ICCCM 2.0, section 4.1.8). */
715 window->u.managed.cmap_window = XCB_NONE;
717 if( p->format == 32 && p->value_len >= 2 ) {
718 p32 = xcb_get_property_value( p );
720 if( p32[ 0 ] == window->w )
721 /* The window itself is given the highest priority, which
722 is what we would do anyway -- ignore the list. */
723 break;
725 for( i = 1; i < p->value_len; i++ )
726 if( p32[ i ] == window->w ) {
727 /* The window is explicitly given a lower priority
728 than some other one. Remember whichever is
729 considered more important. */
730 window->u.managed.cmap_window = p32[ i ];
732 break;
736 break;
738 case PROP_WM_HINTS:
739 /* WM_HINTS property (see ICCCM 2.0, section 4.1.2.4). */
740 window->u.managed.hints &= ~HINT_ICONIC;
741 window->u.managed.hints |= HINT_INPUT;
743 if( p->format == 32 ) {
744 xcb_pixmap_t icon = XCB_NONE, mask = XCB_NONE;
746 p32 = xcb_get_property_value( p );
748 if( p->value_len > WM_HINTS_INPUT_OFF &&
749 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_INPUT ) &&
750 !p32[ WM_HINTS_INPUT_OFF ] )
751 window->u.managed.hints &= ~HINT_INPUT;
753 if( p->value_len > WM_HINTS_STATE_OFF &&
754 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_STATE ) &&
755 p32[ WM_HINTS_STATE_OFF ] == WM_HINTS_STATE_ICONIC )
756 window->u.managed.hints |= HINT_ICONIC;
758 if( p->value_len > WM_HINTS_ICON_OFF &&
759 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON ) )
760 icon = p32[ WM_HINTS_ICON_OFF ];
762 if( p->value_len > WM_HINTS_MASK_OFF &&
763 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON_MASK ) )
764 mask = p32[ WM_HINTS_MASK_OFF ];
766 if( icon ) {
767 struct managed_get_geometry *p = xmalloc( sizeof *p );
768 union callback_param cp;
770 p->window = window;
771 p->icon = icon;
772 p->mask = mask;
773 cp.p = p;
774 handle_async_reply( xcb_get_geometry( c, icon ).sequence,
775 handle_get_geometry, cp );
776 } else
777 replace_icons( window, 0, NULL, NULL, NULL, &icon );
780 break;
782 case PROP_WM_NAME:
783 /* WM_NAME property (see ICCCM 2.0, section 4.1.2.1). */
784 if( window->u.managed.net_wm_name )
785 /* Ignore WM_NAME if _NET_WM_NAME is set. */
786 break;
788 if( window->u.managed.name )
789 free( window->u.managed.name );
791 if( p->value_len && p->format == 8 )
792 window->u.managed.name = to_utf8(
793 p->type == atoms[ ATOM_COMPOUND_TEXT ] ? ENCODING_COMPOUND :
794 ENCODING_LATIN_1, xcb_get_property_value( p ), p->value_len );
795 else
796 window->u.managed.name = NULL;
798 if( window->u.managed.state == STATE_NORMAL &&
799 ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) )
800 queue_window_update( window->u.managed.frame, 0, 0,
801 window->u.managed.frame->u.frame.width,
802 frame_t( window->u.managed.frame, FALSE ),
803 FALSE );
805 break;
807 case PROP_WM_NORMAL_HINTS:
808 /* WM_NORMAL_HINTS property (see ICCCM 2.0, section 4.1.2.3). */
809 window->u.managed.hints &= ~HINT_POSITION;
810 window->u.managed.min_width = frame_t( window->u.managed.frame,
811 FALSE );
812 window->u.managed.min_height = frame_t( window->u.managed.frame,
813 FALSE ) >> 1;
814 window->u.managed.max_width = 0x7FFF;
815 window->u.managed.max_height = 0x7FFF;
816 window->u.managed.width_inc = 1;
817 window->u.managed.height_inc = 1;
818 window->u.managed.min_aspect_x = 0;
819 window->u.managed.min_aspect_y = 1;
820 window->u.managed.max_aspect_x = 1;
821 window->u.managed.max_aspect_y = 0;
822 window->u.managed.base_width = 0;
823 window->u.managed.base_height = 0;
824 window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST;
826 if( p->value_len < 1 || p->format != 32 ) {
827 n32 = 0;
828 p32 = &n32;
829 value_len = 1;
830 } else {
831 p32 = xcb_get_property_value( p );
832 value_len = p->value_len;
835 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_USER_POSITION )
836 window->u.managed.hints |= HINT_USER_POSITION;
837 else if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] &
838 WM_NORMAL_HINTS_PROGRAM_POSITION )
839 window->u.managed.hints |= HINT_PROGRAM_POSITION;
841 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MIN_SIZE &&
842 value_len >= WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ) {
843 window->u.managed.min_width = p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF ];
844 window->u.managed.min_height =
845 p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ];
848 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MAX_SIZE &&
849 value_len >= WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ) {
850 window->u.managed.max_width = p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF ];
851 window->u.managed.max_height =
852 p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ];
855 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_SIZE_INC &&
856 value_len >= WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ) {
857 window->u.managed.width_inc = p32[ WM_NORMAL_HINTS_SIZE_INC_OFF ];
858 window->u.managed.height_inc =
859 p32[ WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ];
861 if( window->u.managed.width_inc < 1 )
862 window->u.managed.width_inc = 1;
863 if( window->u.managed.height_inc < 1 )
864 window->u.managed.height_inc = 1;
867 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_ASPECT &&
868 value_len >= WM_NORMAL_HINTS_ASPECT_OFF + 3 ) {
869 window->u.managed.min_aspect_x = p32[ WM_NORMAL_HINTS_ASPECT_OFF ];
870 window->u.managed.min_aspect_y =
871 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 1 ];
872 window->u.managed.max_aspect_x =
873 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 2 ];
874 window->u.managed.max_aspect_y =
875 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 3 ];
878 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_BASE_SIZE &&
879 value_len >= WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ) {
880 window->u.managed.base_width =
881 p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF ];
882 window->u.managed.base_height =
883 p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ];
885 if( !( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] &
886 WM_NORMAL_HINTS_MIN_SIZE ) ) {
887 window->u.managed.min_width = window->u.managed.base_width;
888 window->u.managed.min_width = window->u.managed.base_width;
892 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_WIN_GRAVITY &&
893 value_len >= WM_NORMAL_HINTS_WIN_GRAVITY_OFF )
894 window->u.managed.win_gravity =
895 p32[ WM_NORMAL_HINTS_WIN_GRAVITY_OFF ];
897 /* Sanity check. */
898 screen = screens[ window->screen ];
900 if( window->u.managed.min_width < frame_t( window->u.managed.frame,
901 FALSE ) )
902 window->u.managed.min_width = frame_t( window->u.managed.frame,
903 FALSE );
904 if( window->u.managed.min_width > screen->width_in_pixels )
905 window->u.managed.min_width = screen->width_in_pixels;
907 if( window->u.managed.min_height < frame_t( window->u.managed.frame,
908 FALSE ) >> 1 )
909 window->u.managed.min_height = frame_t( window->u.managed.frame,
910 FALSE ) >> 1;
911 if( window->u.managed.min_height > screen->height_in_pixels )
912 window->u.managed.min_height = screen->height_in_pixels;
914 if( window->u.managed.max_width < window->u.managed.min_width )
915 window->u.managed.max_width = window->u.managed.min_width;
916 if( window->u.managed.max_width > 0x7FFF )
917 window->u.managed.max_width = 0x7FFF;
919 if( window->u.managed.max_height < window->u.managed.min_height )
920 window->u.managed.max_height = window->u.managed.min_height;
921 if( window->u.managed.max_height > 0x7FFF )
922 window->u.managed.max_height = 0x7FFF;
924 if( window->u.managed.min_aspect_x < 0 ||
925 window->u.managed.min_aspect_y < 1 ||
926 window->u.managed.max_aspect_x < 1 ||
927 window->u.managed.max_aspect_x < 0 ||
928 window->u.managed.min_aspect_x * window->u.managed.max_aspect_y >
929 window->u.managed.min_aspect_y * window->u.managed.max_aspect_x ) {
930 window->u.managed.min_aspect_x = 0;
931 window->u.managed.min_aspect_y = 1;
932 window->u.managed.max_aspect_x = 1;
933 window->u.managed.max_aspect_y = 0;
936 if( window->u.managed.base_width < 0 ||
937 window->u.managed.base_width > window->u.managed.min_width ||
938 window->u.managed.base_height < 0 ||
939 window->u.managed.base_height > window->u.managed.min_height ) {
940 window->u.managed.base_width = 0;
941 window->u.managed.base_height = 0;
944 if( window->u.managed.win_gravity < XCB_GRAVITY_NORTH_WEST ||
945 window->u.managed.win_gravity > XCB_GRAVITY_STATIC )
946 window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST;
948 if( window->u.managed.state == STATE_NORMAL ) {
949 /* Recalculate constraints. */
950 int width = window->u.managed.frame->u.frame.width -
951 frame_l( window->u.managed.frame, FALSE ) -
952 frame_r( window->u.managed.frame, FALSE );
953 int height = window->u.managed.frame->u.frame.height -
954 frame_t( window->u.managed.frame, FALSE ) -
955 frame_b( window->u.managed.frame, FALSE );
956 int old_width, old_height;
958 old_width = width;
959 old_height = height;
961 apply_size_constraints( window, &width, &height );
963 if( width != old_width || height != old_height ) {
964 xcb_configure_request_event_t ev;
966 ev.response_type = XCB_CONFIGURE_REQUEST;
967 ev.parent = window->u.managed.frame->w;
968 ev.window = window->w;
969 ev.width = width;
970 ev.height = height;
971 ev.value_mask = XCB_CONFIG_WINDOW_WIDTH |
972 XCB_CONFIG_WINDOW_HEIGHT;
974 frame_handlers[ XCB_CONFIGURE_REQUEST ](
975 window->u.managed.frame, (xcb_generic_event_t *) &ev );
979 break;
981 case PROP_WM_PROTOCOLS:
982 /* WM_PROTOCOLS property (see ICCCM 2.0, section 4.1.2.7). */
983 /* FIXME Look for _NET_WM_SYNC_REQUEST. */
984 window->u.managed.protocols = 0;
986 if( p->format == 32 ) {
987 p32 = xcb_get_property_value( p );
989 for( i = 0; i < p->value_len; i++ )
990 if( p32[ i ] == atoms[ ATOM_WM_DELETE_WINDOW ] )
991 window->u.managed.protocols |= PROTOCOL_DELETE_WINDOW;
992 else if( p32[ i ] == atoms[ ATOM_WM_TAKE_FOCUS ] )
993 window->u.managed.protocols |= PROTOCOL_TAKE_FOCUS;
996 if( window->u.managed.state == STATE_NORMAL ) {
997 uint32_t n = window->u.managed.protocols & PROTOCOL_DELETE_WINDOW ?
998 XCB_NONE : cursors[ CURSOR_DESTROY ];
999 xcb_change_window_attributes( c, window->u.managed.frame->
1000 u.frame.button->w, XCB_CW_CURSOR,
1001 &n );
1004 break;
1008 static void managed_property_notify( struct gwm_window *window,
1009 xcb_property_notify_event_t *ev ) {
1011 enum gwm_property_type i;
1013 for( i = 0; i < NUM_PROPS; i++ )
1014 if( ev->atom == prop_atoms[ i ] ) {
1015 async_get_property( window, i );
1016 return;
1020 static void managed_colormap_notify( struct gwm_window *window,
1021 xcb_colormap_notify_event_t *ev ) {
1023 if( ev->_new ) {
1024 window->u.managed.cmap = ev->colormap;
1026 if( window->u.managed.frame == focus_frame )
1027 /* Gah... the protocol doesn't specify a timestamp on
1028 ColormapNotify events. We'll have to make do with the
1029 most recent timestamp we've received instead. */
1030 install_window_colormap( window->screen, window, latest_timestamp );
1034 #if USE_SHAPE
1035 extern void match_managed_shape( struct gwm_window *window ) {
1037 if( window->u.managed.shaped ) {
1038 xcb_rectangle_t rect;
1040 rect.x = rect.y = 0;
1041 rect.width = window->u.managed.frame->u.frame.width;
1042 rect.height = frame_t( window->u.managed.frame, FALSE );
1043 xcb_shape_rectangles( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, 0,
1044 window->u.managed.frame->w, 0, 0, 1, &rect );
1046 xcb_shape_combine( c, XCB_SHAPE_SO_UNION, XCB_SHAPE_SK_BOUNDING,
1047 XCB_SHAPE_SK_BOUNDING, window->u.managed.frame->w,
1048 frame_l( window->u.managed.frame, FALSE ),
1049 frame_t( window->u.managed.frame, FALSE ),
1050 window->w );
1051 } else
1052 xcb_shape_mask( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING,
1053 window->u.managed.frame->w, 0, 0, XCB_NONE );
1056 static void managed_shape_notify( struct gwm_window *window,
1057 xcb_shape_notify_event_t *ev ) {
1059 if( ev->shape_kind == XCB_SHAPE_SK_BOUNDING ) {
1060 if( window->u.managed.shaped != ev->shaped &&
1061 !( window->u.managed.shaped = ev->shaped ) )
1062 /* The client is no longer shaped. Retrieve its original
1063 _MOTIF_WM_HINTS, since we might now want to apply a border. */
1064 async_get_property( window, PROP__MOTIF_WM_HINTS );
1066 match_managed_shape( window );
1069 #endif
1071 const event_handler managed_handlers[ NUM_EXTENDED_EVENTS ] = {
1072 NULL, /* Error */
1073 NULL, /* Reply */
1074 NULL, /* KeyPress */
1075 NULL, /* KeyRelease */
1076 NULL, /* ButtonPress */
1077 NULL, /* ButtonRelease */
1078 NULL, /* MotionNotify */
1079 NULL, /* EnterNotify */
1080 NULL, /* LeaveNotify */
1081 NULL, /* FocusIn */
1082 NULL, /* FocusOut */
1083 NULL, /* KeymapNotify */
1084 NULL, /* Expose */
1085 NULL, /* GraphicsExpose */
1086 NULL, /* NoExposure */
1087 NULL, /* VisibilityNotify */
1088 NULL, /* CreateNotify */
1089 (event_handler) managed_destroy_notify,
1090 (event_handler) managed_unmap_notify,
1091 NULL, /* MapNotify */
1092 NULL, /* MapRequest */
1093 (event_handler) managed_reparent_notify,
1094 NULL, /* ConfigureNotify */
1095 NULL, /* ConfigureRequest */
1096 NULL, /* GravityNotify */
1097 NULL, /* ResizeRequest */
1098 NULL, /* CirculateNotify */
1099 NULL, /* CirculateRequest */
1100 (event_handler) managed_property_notify,
1101 NULL, /* SelectionClear */
1102 NULL, /* SelectionRequest */
1103 NULL, /* SelectionNotify */
1104 (event_handler) managed_colormap_notify,
1105 NULL, /* ClientMessage */
1106 NULL, /* MappingNotify */
1107 NULL, /* (synthetic) */
1108 NULL, /* RRNotify */
1109 #if USE_SHAPE
1110 (event_handler) managed_shape_notify
1111 #else
1112 NULL /* ShapeNotify */
1113 #endif
1116 extern void withdrawn_map_request( struct gwm_window *window,
1117 xcb_map_request_event_t *ev ) {
1119 manage_window( ev->window, TRUE );
1122 extern void withdrawn_configure_request( struct gwm_window *window,
1123 xcb_configure_request_event_t *ev ) {
1125 int i = 0;
1126 uint32_t values[ 7 ];
1128 /* Configuring an unmanaged window -- just honour the request as is. */
1129 if( ev->value_mask & XCB_CONFIG_WINDOW_X )
1130 values[ i++ ] = ev->x;
1131 if( ev->value_mask & XCB_CONFIG_WINDOW_Y )
1132 values[ i++ ] = ev->y;
1133 if( ev->value_mask & XCB_CONFIG_WINDOW_WIDTH )
1134 values[ i++ ] = ev->width;
1135 if( ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT )
1136 values[ i++ ] = ev->height;
1137 if( ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH )
1138 values[ i++ ] = ev->border_width;
1139 if( ev->value_mask & XCB_CONFIG_WINDOW_SIBLING )
1140 values[ i++ ] = ev->sibling;
1141 if( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE )
1142 values[ i++ ] = ev->stack_mode;
1144 xcb_configure_window( c, ev->window, ev->value_mask, values );