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/>.
30 #include <xcb/shape.h>
38 #include "window-table.h"
40 static void managed_destroy_notify( struct gwm_window
*window
,
41 xcb_destroy_notify_event_t
*ev
) {
43 assert( window
->type
== WINDOW_MANAGED
);
45 /* This isn't the preferred notification mechanism, but it can
46 happen: if a client has a window in the Iconic state and either
47 terminates abnormally or doesn't send a synthetic UnmapNotify
48 (as specified in ICCCM 2.0, section 4.1.4), then we might not
49 find out about it until we receive a DestroyNotify. */
50 unmanage_window( window
);
53 static void managed_unmap_notify( struct gwm_window
*window
,
54 xcb_unmap_notify_event_t
*ev
) {
56 assert( window
->type
== WINDOW_MANAGED
);
58 /* A transition to the Withdrawn state (ICCCM 2.0, section 4.1.4). */
59 unmanage_window( window
);
62 static void managed_reparent_notify( struct gwm_window
*window
,
63 xcb_reparent_notify_event_t
*ev
) {
65 assert( window
->type
== WINDOW_MANAGED
);
67 if( ev
->parent
!= window
->u
.managed
.frame
->w
)
68 /* Handle an obscure case: a client window being reparented away from
69 our frame while iconified. Much like managed_destroy_notify. */
70 unmanage_window( window
->u
.frame
.child
);
73 #define WM_HINTS_INPUT 0x1
74 #define WM_HINTS_STATE 0x2
76 #define WM_HINTS_FLAGS_OFF 0
77 #define WM_HINTS_INPUT_OFF 1
78 #define WM_HINTS_STATE_OFF 2
79 #define WM_HINTS_MIN_SIZE 3 /* ignore hint properties smaller than this */
81 #define WM_HINTS_STATE_NORMAL 1
82 #define WM_HINTS_STATE_ICONIC 3
84 #define WM_NORMAL_HINTS_USER_POSITION 0x001
85 #define WM_NORMAL_HINTS_PROGRAM_POSITION 0x004
86 #define WM_NORMAL_HINTS_MIN_SIZE 0x010
87 #define WM_NORMAL_HINTS_MAX_SIZE 0x020
88 #define WM_NORMAL_HINTS_SIZE_INC 0x040
89 #define WM_NORMAL_HINTS_ASPECT 0x080
90 #define WM_NORMAL_HINTS_BASE_SIZE 0x100
91 #define WM_NORMAL_HINTS_WIN_GRAVITY 0x200
93 #define WM_NORMAL_HINTS_FLAGS_OFF 0
94 #define WM_NORMAL_HINTS_MIN_SIZE_OFF 5
95 #define WM_NORMAL_HINTS_MAX_SIZE_OFF 7
96 #define WM_NORMAL_HINTS_SIZE_INC_OFF 9
97 #define WM_NORMAL_HINTS_ASPECT_OFF 11
98 #define WM_NORMAL_HINTS_BASE_SIZE_OFF 15
99 #define WM_NORMAL_HINTS_WIN_GRAVITY_OFF 17
101 extern void managed_property_change( struct gwm_window
*window
, int prop
,
102 xcb_get_property_reply_t
*p
) {
106 struct xcb_screen_t
*screen
;
109 assert( window
->type
== WINDOW_MANAGED
);
112 case PROP_WM_COLORMAP_WINDOWS
:
113 /* WM_COLORMAP_WINDOWS property (see ICCCM 2.0, section 4.1.8). */
114 window
->u
.managed
.cmap_window
= XCB_NONE
;
116 if( p
->format
== 32 && p
->value_len
>= 2 ) {
117 p32
= xcb_get_property_value( p
);
119 if( p32
[ 0 ] == window
->w
)
120 /* The window itself is given the highest priority, which
121 is what we would do anyway -- ignore the list. */
124 for( i
= 1; i
< p
->value_len
; i
++ )
125 if( p32
[ i
] == window
->w
) {
126 /* The window is explicitly given a lower priority
127 than some other one. Remember whichever is
128 considered more important. */
129 window
->u
.managed
.cmap_window
= p32
[ i
];
138 /* WM_HINTS property (see ICCCM 2.0, section 4.1.2.4). */
139 window
->u
.managed
.hints
&= ~HINT_ICONIC
;
140 window
->u
.managed
.hints
|= HINT_INPUT
;
142 if( p
->value_len
< WM_HINTS_MIN_SIZE
|| p
->format
!= 32 )
145 p32
= xcb_get_property_value( p
);
147 if( ( p32
[ WM_HINTS_FLAGS_OFF
] & WM_HINTS_INPUT
) &&
148 !p32
[ WM_HINTS_INPUT_OFF
] )
149 window
->u
.managed
.hints
&= ~HINT_INPUT
;
151 if( ( p32
[ WM_HINTS_FLAGS_OFF
] & WM_HINTS_STATE
) &&
152 p32
[ WM_HINTS_STATE_OFF
] == WM_HINTS_STATE_ICONIC
)
153 window
->u
.managed
.hints
|= HINT_ICONIC
;
158 /* WM_NAME property (see ICCCM 2.0, section 4.1.2.1). */
159 if( window
->u
.managed
.name
)
160 free( window
->u
.managed
.name
);
162 if( p
->value_len
&& p
->format
== 8 )
163 window
->u
.managed
.name
= to_utf8(
164 p
->type
== atoms
[ ATOM_COMPOUND_TEXT
] ? ENCODING_COMPOUND
:
165 ENCODING_LATIN_1
, xcb_get_property_value( p
), p
->value_len
);
167 window
->u
.managed
.name
= NULL
;
169 if( window
->u
.managed
.state
== STATE_NORMAL
)
170 queue_window_update( window
->u
.managed
.frame
, 0, 0,
171 window
->u
.managed
.frame
->u
.frame
.width
,
172 FRAME_TITLE_HEIGHT
, FALSE
);
176 case PROP_WM_NORMAL_HINTS
:
177 /* WM_NORMAL_HINTS property (see ICCCM 2.0, section 4.1.2.3). */
178 window
->u
.managed
.hints
&= ~HINT_POSITION
;
179 window
->u
.managed
.min_width
= FRAME_TITLE_HEIGHT
;
180 window
->u
.managed
.min_height
= FRAME_TITLE_HEIGHT
>> 1;
181 window
->u
.managed
.max_width
= 0x7FFF;
182 window
->u
.managed
.max_height
= 0x7FFF;
183 window
->u
.managed
.width_inc
= 1;
184 window
->u
.managed
.height_inc
= 1;
185 window
->u
.managed
.min_aspect_x
= 0;
186 window
->u
.managed
.min_aspect_y
= 1;
187 window
->u
.managed
.max_aspect_x
= 1;
188 window
->u
.managed
.max_aspect_y
= 0;
189 window
->u
.managed
.base_width
= 0;
190 window
->u
.managed
.base_height
= 0;
191 window
->u
.managed
.win_gravity
= XCB_GRAVITY_NORTH_WEST
;
193 if( p
->value_len
< 1 || p
->format
!= 32 ) {
198 p32
= xcb_get_property_value( p
);
199 value_len
= p
->value_len
;
202 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_USER_POSITION
)
203 window
->u
.managed
.hints
|= HINT_USER_POSITION
;
204 else if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] &
205 WM_NORMAL_HINTS_PROGRAM_POSITION
)
206 window
->u
.managed
.hints
|= HINT_PROGRAM_POSITION
;
208 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_MIN_SIZE
&&
209 value_len
>= WM_NORMAL_HINTS_MIN_SIZE_OFF
+ 1 ) {
210 window
->u
.managed
.min_width
= p32
[ WM_NORMAL_HINTS_MIN_SIZE_OFF
];
211 window
->u
.managed
.min_height
=
212 p32
[ WM_NORMAL_HINTS_MIN_SIZE_OFF
+ 1 ];
215 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_MAX_SIZE
&&
216 value_len
>= WM_NORMAL_HINTS_MAX_SIZE_OFF
+ 1 ) {
217 window
->u
.managed
.max_width
= p32
[ WM_NORMAL_HINTS_MAX_SIZE_OFF
];
218 window
->u
.managed
.max_height
=
219 p32
[ WM_NORMAL_HINTS_MAX_SIZE_OFF
+ 1 ];
222 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_SIZE_INC
&&
223 value_len
>= WM_NORMAL_HINTS_SIZE_INC_OFF
+ 1 ) {
224 window
->u
.managed
.width_inc
= p32
[ WM_NORMAL_HINTS_SIZE_INC_OFF
];
225 window
->u
.managed
.height_inc
=
226 p32
[ WM_NORMAL_HINTS_SIZE_INC_OFF
+ 1 ];
229 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_ASPECT
&&
230 value_len
>= WM_NORMAL_HINTS_ASPECT_OFF
+ 3 ) {
231 window
->u
.managed
.min_aspect_x
= p32
[ WM_NORMAL_HINTS_ASPECT_OFF
];
232 window
->u
.managed
.min_aspect_y
=
233 p32
[ WM_NORMAL_HINTS_ASPECT_OFF
+ 1 ];
234 window
->u
.managed
.max_aspect_x
=
235 p32
[ WM_NORMAL_HINTS_ASPECT_OFF
+ 2 ];
236 window
->u
.managed
.max_aspect_y
=
237 p32
[ WM_NORMAL_HINTS_ASPECT_OFF
+ 3 ];
240 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_BASE_SIZE
&&
241 value_len
>= WM_NORMAL_HINTS_BASE_SIZE_OFF
+ 1 ) {
242 window
->u
.managed
.base_width
=
243 p32
[ WM_NORMAL_HINTS_BASE_SIZE_OFF
];
244 window
->u
.managed
.base_height
=
245 p32
[ WM_NORMAL_HINTS_BASE_SIZE_OFF
+ 1 ];
247 if( !( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] &
248 WM_NORMAL_HINTS_MIN_SIZE
) ) {
249 window
->u
.managed
.min_width
= window
->u
.managed
.base_width
;
250 window
->u
.managed
.min_width
= window
->u
.managed
.base_width
;
254 if( p32
[ WM_NORMAL_HINTS_FLAGS_OFF
] & WM_NORMAL_HINTS_WIN_GRAVITY
&&
255 value_len
>= WM_NORMAL_HINTS_WIN_GRAVITY_OFF
)
256 window
->u
.managed
.win_gravity
=
257 p32
[ WM_NORMAL_HINTS_WIN_GRAVITY_OFF
];
260 screen
= screens
[ window
->screen
];
262 if( window
->u
.managed
.min_width
< FRAME_TITLE_HEIGHT
)
263 window
->u
.managed
.min_width
= FRAME_TITLE_HEIGHT
;
264 if( window
->u
.managed
.min_width
> screen
->width_in_pixels
)
265 window
->u
.managed
.min_width
= screen
->width_in_pixels
;
267 if( window
->u
.managed
.min_height
< FRAME_TITLE_HEIGHT
>> 1 )
268 window
->u
.managed
.min_height
= FRAME_TITLE_HEIGHT
>> 1;
269 if( window
->u
.managed
.min_height
> screen
->height_in_pixels
)
270 window
->u
.managed
.min_height
= screen
->height_in_pixels
;
272 if( window
->u
.managed
.max_width
< window
->u
.managed
.min_width
)
273 window
->u
.managed
.max_width
= window
->u
.managed
.min_width
;
274 if( window
->u
.managed
.max_width
> 0x7FFF )
275 window
->u
.managed
.max_width
= 0x7FFF;
277 if( window
->u
.managed
.max_height
< window
->u
.managed
.min_height
)
278 window
->u
.managed
.max_height
= window
->u
.managed
.min_height
;
279 if( window
->u
.managed
.max_height
> 0x7FFF )
280 window
->u
.managed
.max_height
= 0x7FFF;
282 if( window
->u
.managed
.min_aspect_x
< 0 ||
283 window
->u
.managed
.min_aspect_y
< 1 ||
284 window
->u
.managed
.max_aspect_x
< 1 ||
285 window
->u
.managed
.max_aspect_x
< 0 ||
286 window
->u
.managed
.min_aspect_x
* window
->u
.managed
.max_aspect_y
>
287 window
->u
.managed
.min_aspect_y
* window
->u
.managed
.max_aspect_x
) {
288 window
->u
.managed
.min_aspect_x
= 0;
289 window
->u
.managed
.min_aspect_y
= 1;
290 window
->u
.managed
.max_aspect_x
= 1;
291 window
->u
.managed
.max_aspect_y
= 0;
294 if( window
->u
.managed
.base_width
< 0 ||
295 window
->u
.managed
.base_width
> window
->u
.managed
.min_width
||
296 window
->u
.managed
.base_height
< 0 ||
297 window
->u
.managed
.base_height
> window
->u
.managed
.min_height
) {
298 window
->u
.managed
.base_width
= 0;
299 window
->u
.managed
.base_height
= 0;
302 if( window
->u
.managed
.win_gravity
< XCB_GRAVITY_NORTH_WEST
||
303 window
->u
.managed
.win_gravity
> XCB_GRAVITY_STATIC
)
304 window
->u
.managed
.win_gravity
= XCB_GRAVITY_NORTH_WEST
;
306 if( window
->u
.managed
.state
== STATE_NORMAL
) {
307 /* Recalculate constraints. */
308 int width
= window
->u
.managed
.frame
->u
.frame
.width
-
309 ( FRAME_BORDER_WIDTH
<< 1 );
310 int height
= window
->u
.managed
.frame
->u
.frame
.height
-
311 FRAME_TITLE_HEIGHT
- FRAME_BORDER_WIDTH
;
312 int old_width
, old_height
;
317 apply_size_constraints( window
, &width
, &height
);
319 if( width
!= old_width
|| height
!= old_height
) {
320 xcb_configure_request_event_t ev
;
322 ev
.response_type
= XCB_CONFIGURE_REQUEST
;
323 ev
.parent
= window
->u
.managed
.frame
->w
;
324 ev
.window
= window
->w
;
327 ev
.value_mask
= XCB_CONFIG_WINDOW_WIDTH
|
328 XCB_CONFIG_WINDOW_HEIGHT
;
330 frame_handlers
[ XCB_CONFIGURE_REQUEST
](
331 window
->u
.managed
.frame
, (xcb_generic_event_t
*) &ev
);
337 case PROP_WM_PROTOCOLS
:
338 /* WM_PROTOCOLS property (see ICCCM 2.0, section 4.1.2.7). */
339 /* FIXME Look for _NET_WM_SYNC_REQUEST. */
340 window
->u
.managed
.protocols
= 0;
342 if( p
->format
== 32 ) {
343 p32
= xcb_get_property_value( p
);
345 for( i
= 0; i
< p
->value_len
; i
++ )
346 if( p32
[ i
] == atoms
[ ATOM_WM_DELETE_WINDOW
] )
347 window
->u
.managed
.protocols
|= PROTOCOL_DELETE_WINDOW
;
348 else if( p32
[ i
] == atoms
[ ATOM_WM_TAKE_FOCUS
] )
349 window
->u
.managed
.protocols
|= PROTOCOL_TAKE_FOCUS
;
352 if( window
->u
.managed
.state
== STATE_NORMAL
) {
353 uint32_t n
= window
->u
.managed
.protocols
& PROTOCOL_DELETE_WINDOW
?
354 XCB_NONE
: cursors
[ CURSOR_DESTROY
];
355 xcb_change_window_attributes( c
, window
->u
.managed
.frame
->
356 u
.frame
.button
->w
, XCB_CW_CURSOR
,
364 struct managed_get_property
{
365 struct gwm_window
*window
;
369 static void handle_managed_get_property( unsigned int sequence
, void *reply
,
370 xcb_generic_error_t
*error
,
371 union callback_param cp
) {
373 struct managed_get_property
*p
= cp
.p
;
376 /* Ignore Window errors, since the window might have been destroyed
378 if( error
->error_code
!= XCB_WINDOW
)
385 managed_property_change( p
->window
, p
->prop
, reply
);
393 static void managed_property_notify( struct gwm_window
*window
,
394 xcb_property_notify_event_t
*ev
) {
397 for( i
= 0; i
< NUM_PROPS
; i
++ )
398 if( ev
->atom
== prop_atoms
[ i
] ) {
399 struct managed_get_property
*p
= xmalloc( sizeof *p
);
400 union callback_param cp
;
406 handle_async_reply( xcb_get_property( c
, FALSE
, window
->w
,
409 PROP_SIZE
).sequence
,
410 handle_managed_get_property
, cp
);
415 static void managed_colormap_notify( struct gwm_window
*window
,
416 xcb_colormap_notify_event_t
*ev
) {
419 window
->u
.managed
.cmap
= ev
->colormap
;
421 if( window
->u
.managed
.frame
== focus_frame
)
422 /* Gah... the protocol doesn't specify a timestamp on
423 ColormapNotify events. We'll have to make do with the
424 most recent timestamp we've received instead. */
425 install_window_colormap( window
->screen
, window
, latest_timestamp
);
430 extern void match_managed_shape( struct gwm_window
*window
, int shaped
) {
433 xcb_rectangle_t rect
;
436 rect
.width
= window
->u
.managed
.frame
->u
.frame
.width
;
437 rect
.height
= FRAME_TITLE_HEIGHT
;
438 xcb_shape_rectangles( c
, XCB_SHAPE_SO_SET
, XCB_SHAPE_SK_BOUNDING
, 0,
439 window
->u
.managed
.frame
->w
, 0, 0, 1, &rect
);
441 xcb_shape_combine( c
, XCB_SHAPE_SO_UNION
, XCB_SHAPE_SK_BOUNDING
,
442 XCB_SHAPE_SK_BOUNDING
, window
->u
.managed
.frame
->w
,
443 FRAME_BORDER_WIDTH
, FRAME_TITLE_HEIGHT
, window
->w
);
445 xcb_shape_mask( c
, XCB_SHAPE_SO_SET
, XCB_SHAPE_SK_BOUNDING
,
446 window
->u
.managed
.frame
->w
, 0, 0, XCB_NONE
);
449 static void managed_shape_notify( struct gwm_window
*window
,
450 xcb_shape_notify_event_t
*ev
) {
452 match_managed_shape( window
, ev
->shaped
);
456 event_handler managed_handlers
[] = {
460 NULL
, /* KeyRelease */
461 NULL
, /* ButtonPress */
462 NULL
, /* ButtonRelease */
463 NULL
, /* MotionNotify */
464 NULL
, /* EnterNotify */
465 NULL
, /* LeaveNotify */
468 NULL
, /* KeymapNotify */
470 NULL
, /* GraphicsExpose */
471 NULL
, /* NoExposure */
472 NULL
, /* VisibilityNotify */
473 NULL
, /* CreateNotify */
474 (event_handler
) managed_destroy_notify
,
475 (event_handler
) managed_unmap_notify
,
476 NULL
, /* MapNotify */
477 NULL
, /* MapRequest */
478 (event_handler
) managed_reparent_notify
,
479 NULL
, /* ConfigureNotify */
480 NULL
, /* ConfigureRequest */
481 NULL
, /* GravityNotify */
482 NULL
, /* ResizeRequest */
483 NULL
, /* CirculateNotify */
484 NULL
, /* CirculateRequest */
485 (event_handler
) managed_property_notify
,
486 NULL
, /* SelectionClear */
487 NULL
, /* SelectionRequest */
488 NULL
, /* SelectionNotify */
489 (event_handler
) managed_colormap_notify
,
490 NULL
, /* ClientMessage */
491 NULL
, /* MappingNotify */
492 NULL
, /* (synthetic) */
494 (event_handler
) managed_shape_notify
496 NULL
/* ShapeNotify */