Obtain RGB_COLOR_MAP property length in format units, not bytes.
[gwm.git] / managed.c
blob31d8ba446c5fff88fb5e5577873cde7fef77bcfa
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 extern void iconic_to_normal( struct gwm_window *window ) {
58 assert( window->type == WINDOW_MANAGED );
60 set_managed_state( window, STATE_NORMAL );
61 xcb_map_window( c, window->w );
62 xcb_map_window( c, window->u.managed.frame->w );
65 extern void normal_to_iconic( struct gwm_window *window ) {
67 uint32_t n;
69 assert( window->type == WINDOW_MANAGED );
71 set_managed_state( window, STATE_ICONIC );
73 /* We need to unmap our child without invoking the normal
74 UnmapNotify handler response. Unfortunately it's very
75 difficult to communicate to the handler the distinction between
76 the resultant UnmapNotify from an unmap here and an unrelated
77 event. Checking the request sequence is not robust, because
78 asynchronous events might not be assigned unique sequence
79 numbers. Instead, we grab the server and temporarily change
80 our event mask, which seems a heavyweight approach but does
81 guarantee that only this event will be ignored. */
82 xcb_grab_server( c );
84 n = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE;
85 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
87 xcb_unmap_window( c, window->w );
89 n = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE |
90 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
91 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
93 xcb_ungrab_server( c );
95 xcb_unmap_window( c, window->u.managed.frame->w );
98 static void managed_destroy_notify( struct gwm_window *window,
99 xcb_destroy_notify_event_t *ev ) {
101 assert( window->type == WINDOW_MANAGED );
103 /* This isn't the preferred notification mechanism, but it can
104 happen: if a client has a window in the Iconic state and either
105 terminates abnormally or doesn't send a synthetic UnmapNotify
106 (as specified in ICCCM 2.0, section 4.1.4), then we might not
107 find out about it until we receive a DestroyNotify. */
108 unmanage_window( window );
111 static void managed_unmap_notify( struct gwm_window *window,
112 xcb_unmap_notify_event_t *ev ) {
114 assert( window->type == WINDOW_MANAGED );
116 /* A transition to the Withdrawn state (ICCCM 2.0, section 4.1.4). */
117 unmanage_window( window );
120 static void managed_reparent_notify( struct gwm_window *window,
121 xcb_reparent_notify_event_t *ev ) {
123 assert( window->type == WINDOW_MANAGED );
125 if( ev->parent != window->u.managed.frame->w )
126 /* Handle an obscure case: a client window being reparented away from
127 our frame while iconified. Much like managed_destroy_notify. */
128 unmanage_window( window->u.frame.child );
131 struct managed_get_property {
132 xcb_window_t w;
133 enum gwm_property_type prop;
136 static void handle_managed_get_property( unsigned int sequence, void *reply,
137 xcb_generic_error_t *error,
138 union callback_param cp ) {
140 struct managed_get_property *p = cp.p;
142 if( error ) {
143 /* Ignore Window errors, since the window might have been destroyed
144 in the meantime. */
145 if( error->error_code != XCB_WINDOW )
146 show_error( error );
148 free( error );
151 if( reply ) {
152 struct gwm_window *window = lookup_window( p->w );
154 /* Don't assume the window is valid: it might have been destroyed
155 or forgotten asynchronously. */
156 if( window && window->type == WINDOW_MANAGED )
157 managed_property_change( window, p->prop, reply );
159 free( reply );
162 free( p );
165 extern void async_get_property( struct gwm_window *window,
166 enum gwm_property_type prop ) {
168 struct managed_get_property *p = xmalloc( sizeof *p );
169 union callback_param cp;
171 p->w = window->w;
172 p->prop = prop;
174 cp.p = p;
175 handle_async_reply( xcb_get_property( c, FALSE, window->w,
176 prop_atoms[ prop ],
177 prop_types[ prop ], 0,
178 PROP_SIZE ).sequence,
179 handle_managed_get_property, cp );
182 struct managed_get_geometry {
183 struct gwm_window *window;
184 xcb_pixmap_t icon, mask;
187 static void handle_get_geometry( unsigned int sequence, void *reply,
188 xcb_generic_error_t *error,
189 union callback_param cp ) {
191 struct managed_get_geometry *p = cp.p;
193 if( error ) {
194 /* Ignore Drawable errors, since it was specified by the client. */
195 if( error->error_code != XCB_DRAWABLE )
196 show_error( error );
198 free( error );
200 if( !reply )
201 replace_icons( p->window, 0, NULL, NULL, NULL, &p->icon );
204 if( reply ) {
205 xcb_get_geometry_reply_t *r = reply;
206 int width, height;
207 xcb_pixmap_t icons[ 2 ];
209 width = r->width;
210 height = r->height;
211 icons[ 0 ] = p->icon;
212 icons[ 1 ] = p->mask;
213 replace_icons( p->window, 1, &width, &height, NULL, icons );
215 free( reply );
218 free( p );
221 #define MOTIF_WM_HINTS_DECORATIONS 0x2
223 #define MOTIF_WM_HINTS_FLAGS_OFF 0
224 #define MOTIF_WM_HINTS_DECORATIONS_OFF 2
225 #define MOTIF_WM_HINTS_MIN_SIZE 3 /* ignore hint properties smaller than this */
227 #define MOTIF_WM_HINTS_DEC_ALL 0x1
228 #define MOTIF_WM_HINTS_DEC_BORDER 0x2
229 #define MOTIF_WM_HINTS_DEC_TITLE 0x8
231 #define WM_HINTS_INPUT 0x01
232 #define WM_HINTS_STATE 0x02
233 #define WM_HINTS_ICON 0x04
234 #define WM_HINTS_ICON_MASK 0x20
236 #define WM_HINTS_FLAGS_OFF 0
237 #define WM_HINTS_INPUT_OFF 1
238 #define WM_HINTS_STATE_OFF 2
239 #define WM_HINTS_ICON_OFF 3
240 #define WM_HINTS_MASK_OFF 7
242 #define WM_HINTS_STATE_NORMAL 1
243 #define WM_HINTS_STATE_ICONIC 3
245 #define WM_NORMAL_HINTS_USER_POSITION 0x001
246 #define WM_NORMAL_HINTS_PROGRAM_POSITION 0x004
247 #define WM_NORMAL_HINTS_MIN_SIZE 0x010
248 #define WM_NORMAL_HINTS_MAX_SIZE 0x020
249 #define WM_NORMAL_HINTS_SIZE_INC 0x040
250 #define WM_NORMAL_HINTS_ASPECT 0x080
251 #define WM_NORMAL_HINTS_BASE_SIZE 0x100
252 #define WM_NORMAL_HINTS_WIN_GRAVITY 0x200
254 #define WM_NORMAL_HINTS_FLAGS_OFF 0
255 #define WM_NORMAL_HINTS_MIN_SIZE_OFF 5
256 #define WM_NORMAL_HINTS_MAX_SIZE_OFF 7
257 #define WM_NORMAL_HINTS_SIZE_INC_OFF 9
258 #define WM_NORMAL_HINTS_ASPECT_OFF 11
259 #define WM_NORMAL_HINTS_BASE_SIZE_OFF 15
260 #define WM_NORMAL_HINTS_WIN_GRAVITY_OFF 17
262 extern void managed_property_change( struct gwm_window *window, int prop,
263 xcb_get_property_reply_t *p ) {
265 uint32_t *p32, n32;
266 int i;
267 struct xcb_screen_t *screen;
268 int value_len;
269 int old_decoration;
270 int num_icons;
271 int len;
273 assert( window->type == WINDOW_MANAGED );
275 switch( prop ) {
276 case PROP__MOTIF_WM_HINTS:
277 /* _MOTIF_WM_HINTS property. */
278 old_decoration = window->u.managed.frame->u.frame.decoration;
280 window->u.managed.frame->u.frame.decoration = DEC_DEFAULT;
282 if( p->format == 32 && p->value_len >= MOTIF_WM_HINTS_MIN_SIZE ) {
283 p32 = xcb_get_property_value( p );
285 if( p32[ MOTIF_WM_HINTS_FLAGS_OFF ] &
286 MOTIF_WM_HINTS_DECORATIONS ) {
287 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
288 MOTIF_WM_HINTS_DEC_ALL ) {
289 window->u.managed.frame->u.frame.decoration =
290 DEC_BORDER | DEC_TITLE;
292 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
293 MOTIF_WM_HINTS_DEC_BORDER )
294 window->u.managed.frame->u.frame.decoration &=
295 ~DEC_BORDER;
297 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
298 MOTIF_WM_HINTS_DEC_TITLE )
299 window->u.managed.frame->u.frame.decoration &=
300 ~DEC_TITLE;
301 } else {
302 window->u.managed.frame->u.frame.decoration = 0;
304 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
305 MOTIF_WM_HINTS_DEC_BORDER )
306 window->u.managed.frame->u.frame.decoration |=
307 DEC_BORDER;
309 if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] &
310 MOTIF_WM_HINTS_DEC_TITLE )
311 window->u.managed.frame->u.frame.decoration |=
312 DEC_TITLE;
317 #if USE_SHAPE
318 if( window->u.managed.shaped )
319 /* Never apply borders to shaped windows. */
320 window->u.managed.frame->u.frame.decoration &= ~DEC_BORDER;
321 #endif
323 if( window->u.managed.state == STATE_NORMAL &&
324 window->u.managed.frame->u.frame.decoration != old_decoration ) {
325 int new_decoration = window->u.managed.frame->u.frame.decoration;
326 int x, y, width, height;
327 uint32_t n[ 5 ];
329 window->u.managed.frame->u.frame.decoration = old_decoration;
331 translate_frame_to_child( window->u.managed.frame, &x, &y,
332 window->u.managed.frame->u.frame.x,
333 window->u.managed.frame->u.frame.y,
334 window->u.managed.border_width,
335 window->u.managed.win_gravity );
337 window->u.managed.frame->u.frame.decoration = new_decoration;
339 translate_child_to_frame( window->u.managed.frame, &x, &y,
340 &width, &height, x, y,
341 window->u.managed.frame->u.frame.width -
342 frame_l( window->u.managed.frame,
343 FALSE ) -
344 frame_r( window->u.managed.frame,
345 FALSE ),
346 window->u.managed.frame->u.frame.height -
347 frame_t( window->u.managed.frame,
348 FALSE ) -
349 frame_b( window->u.managed.frame,
350 FALSE ),
351 window->u.managed.border_width,
352 window->u.managed.win_gravity );
354 if( new_decoration & DEC_TITLE )
355 xcb_map_window( c,
356 window->u.managed.frame->u.frame.button->w );
357 else
358 xcb_unmap_window( c,
359 window->u.managed.frame->u.frame.button->w );
361 n[ 0 ] = frame_l( window->u.managed.frame, FALSE );
362 n[ 1 ] = frame_l( window->u.managed.frame, FALSE );
363 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
364 XCB_CONFIG_WINDOW_Y, n );
366 n[ 0 ] = x;
367 n[ 1 ] = y;
368 n[ 2 ] = width;
369 n[ 3 ] = height;
370 n[ 4 ] = frame_xb( window->u.managed.frame );
371 /* We'll also notify the client of any changes, in the
372 ConfigureNotify handler for the event we expect to receive in
373 response to this request. */
374 xcb_configure_window( c, window->u.managed.frame->w,
375 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
376 XCB_CONFIG_WINDOW_WIDTH |
377 XCB_CONFIG_WINDOW_HEIGHT |
378 XCB_CONFIG_WINDOW_BORDER_WIDTH, n );
380 update_frame_extents( window->u.managed.frame );
383 break;
385 case PROP__NET_WM_ICON:
386 /* _NET_WM_ICON property (see EWMH "Application Window Properties". */
387 p32 = xcb_get_property_value( p );
388 len = p->format == 32 ? p->value_len : 0;
390 i = num_icons = 0;
391 while( len > i + 2 && len >= i + 2 + p32[ i ] * p32[ i + 1 ] ) {
392 i += p32[ i ] * p32[ i + 1 ] + 2;
393 num_icons++;
396 if( num_icons ) {
397 int *widths, *heights;
398 uint32_t **icons;
400 widths = alloca( num_icons * sizeof *widths );
401 heights = alloca( num_icons * sizeof *heights );
402 icons = alloca( num_icons * sizeof *icons );
404 i = num_icons = 0;
405 while( len > i + 2 &&
406 len >= i + 2 + ( widths[ num_icons ] = p32[ i ] ) *
407 ( heights[ num_icons ] = p32[ i + 1 ] ) ) {
408 icons[ num_icons ] = p32 + i + 2;
409 i += widths[ num_icons ] * heights[ num_icons ] + 2;
410 num_icons++;
413 replace_icons( window, num_icons, widths, heights, icons, NULL );
414 } else
415 replace_icons( window, 0, NULL, NULL, NULL, NULL );
417 break;
419 case PROP__NET_WM_NAME:
420 /* _NET_WM_NAME property (see EWMH "Application Window Properties". */
421 if( window->u.managed.name )
422 free( window->u.managed.name );
424 if( p->value_len && p->format == 8 ) {
425 window->u.managed.name = (char *) utf8_dup_valid_len(
426 xcb_get_property_value( p ), p->value_len );
427 window->u.managed.net_wm_name = TRUE;
429 if( window->u.managed.state == STATE_NORMAL &&
430 ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) )
431 queue_window_update( window->u.managed.frame, 0, 0,
432 window->u.managed.frame->u.frame.width,
433 frame_t( window->u.managed.frame, FALSE ),
434 FALSE );
435 } else {
436 window->u.managed.name = NULL;
437 window->u.managed.net_wm_name = FALSE;
438 /* We've lost the _NET_WM_NAME property. If the window still
439 has a plain WM_NAME, then fall back to that. */
440 async_get_property( window, PROP_WM_NAME );
443 break;
445 case PROP_WM_COLORMAP_WINDOWS:
446 /* WM_COLORMAP_WINDOWS property (see ICCCM 2.0, section 4.1.8). */
447 window->u.managed.cmap_window = XCB_NONE;
449 if( p->format == 32 && p->value_len >= 2 ) {
450 p32 = xcb_get_property_value( p );
452 if( p32[ 0 ] == window->w )
453 /* The window itself is given the highest priority, which
454 is what we would do anyway -- ignore the list. */
455 break;
457 for( i = 1; i < p->value_len; i++ )
458 if( p32[ i ] == window->w ) {
459 /* The window is explicitly given a lower priority
460 than some other one. Remember whichever is
461 considered more important. */
462 window->u.managed.cmap_window = p32[ i ];
464 break;
468 break;
470 case PROP_WM_HINTS:
471 /* WM_HINTS property (see ICCCM 2.0, section 4.1.2.4). */
472 window->u.managed.hints &= ~HINT_ICONIC;
473 window->u.managed.hints |= HINT_INPUT;
475 if( p->format == 32 ) {
476 xcb_pixmap_t icon = XCB_NONE, mask = XCB_NONE;
478 p32 = xcb_get_property_value( p );
480 if( p->value_len > WM_HINTS_INPUT_OFF &&
481 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_INPUT ) &&
482 !p32[ WM_HINTS_INPUT_OFF ] )
483 window->u.managed.hints &= ~HINT_INPUT;
485 if( p->value_len > WM_HINTS_STATE_OFF &&
486 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_STATE ) &&
487 p32[ WM_HINTS_STATE_OFF ] == WM_HINTS_STATE_ICONIC )
488 window->u.managed.hints |= HINT_ICONIC;
490 if( p->value_len > WM_HINTS_ICON_OFF &&
491 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON ) )
492 icon = p32[ WM_HINTS_ICON_OFF ];
494 if( p->value_len > WM_HINTS_MASK_OFF &&
495 ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON_MASK ) )
496 mask = p32[ WM_HINTS_MASK_OFF ];
498 if( icon ) {
499 struct managed_get_geometry *p = xmalloc( sizeof *p );
500 union callback_param cp;
502 p->window = window;
503 p->icon = icon;
504 p->mask = mask;
505 cp.p = p;
506 handle_async_reply( xcb_get_geometry( c, icon ).sequence,
507 handle_get_geometry, cp );
508 } else
509 replace_icons( window, 0, NULL, NULL, NULL, &icon );
512 break;
514 case PROP_WM_NAME:
515 /* WM_NAME property (see ICCCM 2.0, section 4.1.2.1). */
516 if( window->u.managed.net_wm_name )
517 /* Ignore WM_NAME if _NET_WM_NAME is set. */
518 break;
520 if( window->u.managed.name )
521 free( window->u.managed.name );
523 if( p->value_len && p->format == 8 )
524 window->u.managed.name = to_utf8(
525 p->type == atoms[ ATOM_COMPOUND_TEXT ] ? ENCODING_COMPOUND :
526 ENCODING_LATIN_1, xcb_get_property_value( p ), p->value_len );
527 else
528 window->u.managed.name = NULL;
530 if( window->u.managed.state == STATE_NORMAL &&
531 ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) )
532 queue_window_update( window->u.managed.frame, 0, 0,
533 window->u.managed.frame->u.frame.width,
534 frame_t( window->u.managed.frame, FALSE ),
535 FALSE );
537 break;
539 case PROP_WM_NORMAL_HINTS:
540 /* WM_NORMAL_HINTS property (see ICCCM 2.0, section 4.1.2.3). */
541 window->u.managed.hints &= ~HINT_POSITION;
542 window->u.managed.min_width = frame_t( window->u.managed.frame,
543 FALSE );
544 window->u.managed.min_height = frame_t( window->u.managed.frame,
545 FALSE ) >> 1;
546 window->u.managed.max_width = 0x7FFF;
547 window->u.managed.max_height = 0x7FFF;
548 window->u.managed.width_inc = 1;
549 window->u.managed.height_inc = 1;
550 window->u.managed.min_aspect_x = 0;
551 window->u.managed.min_aspect_y = 1;
552 window->u.managed.max_aspect_x = 1;
553 window->u.managed.max_aspect_y = 0;
554 window->u.managed.base_width = 0;
555 window->u.managed.base_height = 0;
556 window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST;
558 if( p->value_len < 1 || p->format != 32 ) {
559 n32 = 0;
560 p32 = &n32;
561 value_len = 1;
562 } else {
563 p32 = xcb_get_property_value( p );
564 value_len = p->value_len;
567 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_USER_POSITION )
568 window->u.managed.hints |= HINT_USER_POSITION;
569 else if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] &
570 WM_NORMAL_HINTS_PROGRAM_POSITION )
571 window->u.managed.hints |= HINT_PROGRAM_POSITION;
573 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MIN_SIZE &&
574 value_len >= WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ) {
575 window->u.managed.min_width = p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF ];
576 window->u.managed.min_height =
577 p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ];
580 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MAX_SIZE &&
581 value_len >= WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ) {
582 window->u.managed.max_width = p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF ];
583 window->u.managed.max_height =
584 p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ];
587 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_SIZE_INC &&
588 value_len >= WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ) {
589 window->u.managed.width_inc = p32[ WM_NORMAL_HINTS_SIZE_INC_OFF ];
590 window->u.managed.height_inc =
591 p32[ WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ];
594 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_ASPECT &&
595 value_len >= WM_NORMAL_HINTS_ASPECT_OFF + 3 ) {
596 window->u.managed.min_aspect_x = p32[ WM_NORMAL_HINTS_ASPECT_OFF ];
597 window->u.managed.min_aspect_y =
598 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 1 ];
599 window->u.managed.max_aspect_x =
600 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 2 ];
601 window->u.managed.max_aspect_y =
602 p32[ WM_NORMAL_HINTS_ASPECT_OFF + 3 ];
605 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_BASE_SIZE &&
606 value_len >= WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ) {
607 window->u.managed.base_width =
608 p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF ];
609 window->u.managed.base_height =
610 p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ];
612 if( !( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] &
613 WM_NORMAL_HINTS_MIN_SIZE ) ) {
614 window->u.managed.min_width = window->u.managed.base_width;
615 window->u.managed.min_width = window->u.managed.base_width;
619 if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_WIN_GRAVITY &&
620 value_len >= WM_NORMAL_HINTS_WIN_GRAVITY_OFF )
621 window->u.managed.win_gravity =
622 p32[ WM_NORMAL_HINTS_WIN_GRAVITY_OFF ];
624 /* Sanity check. */
625 screen = screens[ window->screen ];
627 if( window->u.managed.min_width < frame_t( window->u.managed.frame,
628 FALSE ) )
629 window->u.managed.min_width = frame_t( window->u.managed.frame,
630 FALSE );
631 if( window->u.managed.min_width > screen->width_in_pixels )
632 window->u.managed.min_width = screen->width_in_pixels;
634 if( window->u.managed.min_height < frame_t( window->u.managed.frame,
635 FALSE ) >> 1 )
636 window->u.managed.min_height = frame_t( window->u.managed.frame,
637 FALSE ) >> 1;
638 if( window->u.managed.min_height > screen->height_in_pixels )
639 window->u.managed.min_height = screen->height_in_pixels;
641 if( window->u.managed.max_width < window->u.managed.min_width )
642 window->u.managed.max_width = window->u.managed.min_width;
643 if( window->u.managed.max_width > 0x7FFF )
644 window->u.managed.max_width = 0x7FFF;
646 if( window->u.managed.max_height < window->u.managed.min_height )
647 window->u.managed.max_height = window->u.managed.min_height;
648 if( window->u.managed.max_height > 0x7FFF )
649 window->u.managed.max_height = 0x7FFF;
651 if( window->u.managed.min_aspect_x < 0 ||
652 window->u.managed.min_aspect_y < 1 ||
653 window->u.managed.max_aspect_x < 1 ||
654 window->u.managed.max_aspect_x < 0 ||
655 window->u.managed.min_aspect_x * window->u.managed.max_aspect_y >
656 window->u.managed.min_aspect_y * window->u.managed.max_aspect_x ) {
657 window->u.managed.min_aspect_x = 0;
658 window->u.managed.min_aspect_y = 1;
659 window->u.managed.max_aspect_x = 1;
660 window->u.managed.max_aspect_y = 0;
663 if( window->u.managed.base_width < 0 ||
664 window->u.managed.base_width > window->u.managed.min_width ||
665 window->u.managed.base_height < 0 ||
666 window->u.managed.base_height > window->u.managed.min_height ) {
667 window->u.managed.base_width = 0;
668 window->u.managed.base_height = 0;
671 if( window->u.managed.win_gravity < XCB_GRAVITY_NORTH_WEST ||
672 window->u.managed.win_gravity > XCB_GRAVITY_STATIC )
673 window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST;
675 if( window->u.managed.state == STATE_NORMAL ) {
676 /* Recalculate constraints. */
677 int width = window->u.managed.frame->u.frame.width -
678 frame_l( window->u.managed.frame, FALSE ) -
679 frame_r( window->u.managed.frame, FALSE );
680 int height = window->u.managed.frame->u.frame.height -
681 frame_t( window->u.managed.frame, FALSE ) -
682 frame_b( window->u.managed.frame, FALSE );
683 int old_width, old_height;
685 old_width = width;
686 old_height = height;
688 apply_size_constraints( window, &width, &height );
690 if( width != old_width || height != old_height ) {
691 xcb_configure_request_event_t ev;
693 ev.response_type = XCB_CONFIGURE_REQUEST;
694 ev.parent = window->u.managed.frame->w;
695 ev.window = window->w;
696 ev.width = width;
697 ev.height = height;
698 ev.value_mask = XCB_CONFIG_WINDOW_WIDTH |
699 XCB_CONFIG_WINDOW_HEIGHT;
701 frame_handlers[ XCB_CONFIGURE_REQUEST ](
702 window->u.managed.frame, (xcb_generic_event_t *) &ev );
706 break;
708 case PROP_WM_PROTOCOLS:
709 /* WM_PROTOCOLS property (see ICCCM 2.0, section 4.1.2.7). */
710 /* FIXME Look for _NET_WM_SYNC_REQUEST. */
711 window->u.managed.protocols = 0;
713 if( p->format == 32 ) {
714 p32 = xcb_get_property_value( p );
716 for( i = 0; i < p->value_len; i++ )
717 if( p32[ i ] == atoms[ ATOM_WM_DELETE_WINDOW ] )
718 window->u.managed.protocols |= PROTOCOL_DELETE_WINDOW;
719 else if( p32[ i ] == atoms[ ATOM_WM_TAKE_FOCUS ] )
720 window->u.managed.protocols |= PROTOCOL_TAKE_FOCUS;
723 if( window->u.managed.state == STATE_NORMAL ) {
724 uint32_t n = window->u.managed.protocols & PROTOCOL_DELETE_WINDOW ?
725 XCB_NONE : cursors[ CURSOR_DESTROY ];
726 xcb_change_window_attributes( c, window->u.managed.frame->
727 u.frame.button->w, XCB_CW_CURSOR,
728 &n );
731 break;
735 static void managed_property_notify( struct gwm_window *window,
736 xcb_property_notify_event_t *ev ) {
738 enum gwm_property_type i;
740 for( i = 0; i < NUM_PROPS; i++ )
741 if( ev->atom == prop_atoms[ i ] ) {
742 async_get_property( window, i );
743 return;
747 static void managed_colormap_notify( struct gwm_window *window,
748 xcb_colormap_notify_event_t *ev ) {
750 if( ev->_new ) {
751 window->u.managed.cmap = ev->colormap;
753 if( window->u.managed.frame == focus_frame )
754 /* Gah... the protocol doesn't specify a timestamp on
755 ColormapNotify events. We'll have to make do with the
756 most recent timestamp we've received instead. */
757 install_window_colormap( window->screen, window, latest_timestamp );
761 #if USE_SHAPE
762 extern void match_managed_shape( struct gwm_window *window ) {
764 if( window->u.managed.shaped ) {
765 xcb_rectangle_t rect;
767 rect.x = rect.y = 0;
768 rect.width = window->u.managed.frame->u.frame.width;
769 rect.height = frame_t( window->u.managed.frame, FALSE );
770 xcb_shape_rectangles( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, 0,
771 window->u.managed.frame->w, 0, 0, 1, &rect );
773 xcb_shape_combine( c, XCB_SHAPE_SO_UNION, XCB_SHAPE_SK_BOUNDING,
774 XCB_SHAPE_SK_BOUNDING, window->u.managed.frame->w,
775 frame_l( window->u.managed.frame, FALSE ),
776 frame_t( window->u.managed.frame, FALSE ),
777 window->w );
778 } else
779 xcb_shape_mask( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING,
780 window->u.managed.frame->w, 0, 0, XCB_NONE );
783 static void managed_shape_notify( struct gwm_window *window,
784 xcb_shape_notify_event_t *ev ) {
786 if( ev->shape_kind == XCB_SHAPE_SK_BOUNDING ) {
787 if( window->u.managed.shaped != ev->shaped &&
788 !( window->u.managed.shaped = ev->shaped ) )
789 /* The client is no longer shaped. Retrieve its original
790 _MOTIF_WM_HINTS, since we might now want to apply a border. */
791 async_get_property( window, PROP__MOTIF_WM_HINTS );
793 match_managed_shape( window );
796 #endif
798 const event_handler managed_handlers[] = {
799 NULL, /* Error */
800 NULL, /* Reply */
801 NULL, /* KeyPress */
802 NULL, /* KeyRelease */
803 NULL, /* ButtonPress */
804 NULL, /* ButtonRelease */
805 NULL, /* MotionNotify */
806 NULL, /* EnterNotify */
807 NULL, /* LeaveNotify */
808 NULL, /* FocusIn */
809 NULL, /* FocusOut */
810 NULL, /* KeymapNotify */
811 NULL, /* Expose */
812 NULL, /* GraphicsExpose */
813 NULL, /* NoExposure */
814 NULL, /* VisibilityNotify */
815 NULL, /* CreateNotify */
816 (event_handler) managed_destroy_notify,
817 (event_handler) managed_unmap_notify,
818 NULL, /* MapNotify */
819 NULL, /* MapRequest */
820 (event_handler) managed_reparent_notify,
821 NULL, /* ConfigureNotify */
822 NULL, /* ConfigureRequest */
823 NULL, /* GravityNotify */
824 NULL, /* ResizeRequest */
825 NULL, /* CirculateNotify */
826 NULL, /* CirculateRequest */
827 (event_handler) managed_property_notify,
828 NULL, /* SelectionClear */
829 NULL, /* SelectionRequest */
830 NULL, /* SelectionNotify */
831 (event_handler) managed_colormap_notify,
832 NULL, /* ClientMessage */
833 NULL, /* MappingNotify */
834 NULL, /* (synthetic) */
835 #if USE_SHAPE
836 (event_handler) managed_shape_notify
837 #else
838 NULL /* ShapeNotify */
839 #endif
842 extern void withdrawn_map_request( struct gwm_window *window,
843 xcb_map_request_event_t *ev ) {
845 manage_window( ev->window, TRUE );
848 extern void withdrawn_configure_request( struct gwm_window *window,
849 xcb_configure_request_event_t *ev ) {
851 int i = 0;
852 uint32_t values[ 7 ];
854 /* Configuring an unmanaged window -- just honour the request as is. */
855 if( ev->value_mask & XCB_CONFIG_WINDOW_X )
856 values[ i++ ] = ev->x;
857 if( ev->value_mask & XCB_CONFIG_WINDOW_Y )
858 values[ i++ ] = ev->y;
859 if( ev->value_mask & XCB_CONFIG_WINDOW_WIDTH )
860 values[ i++ ] = ev->width;
861 if( ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT )
862 values[ i++ ] = ev->height;
863 if( ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH )
864 values[ i++ ] = ev->border_width;
865 if( ev->value_mask & XCB_CONFIG_WINDOW_SIBLING )
866 values[ i++ ] = ev->sibling;
867 if( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE )
868 values[ i++ ] = ev->stack_mode;
870 xcb_configure_window( c, ev->window, ev->value_mask, values );