Add notes and placeholders for future modifications.
[gwm.git] / managed.c
blob2abc12abd6213ecf061ea45dd36e3d4cf8d84e24
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 <xcb/xcb.h>
29 #if USE_SHAPE
30 #include <xcb/shape.h>
31 #endif
33 #include "gwm.h"
35 #include "frame.h"
36 #include "managed.h"
37 #include "utf8.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 ) {
104 uint32_t *p32, n32;
105 int i;
106 struct xcb_screen_t *screen;
107 int value_len;
109 assert( window->type == WINDOW_MANAGED );
111 switch( prop ) {
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. */
122 break;
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 ];
131 break;
135 break;
137 case PROP_WM_HINTS:
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 )
143 break;
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;
155 break;
157 case PROP_WM_NAME:
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 );
166 else
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 );
174 break;
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 ) {
194 n32 = 0;
195 p32 = &n32;
196 value_len = 1;
197 } else {
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 ];
259 /* Sanity check. */
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;
314 old_width = width;
315 old_height = 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;
325 ev.width = width;
326 ev.height = height;
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 );
335 break;
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,
357 &n );
360 break;
364 struct managed_get_property {
365 struct gwm_window *window;
366 int prop;
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;
375 if( error ) {
376 /* Ignore Window errors, since the window might have been destroyed
377 in the meantime. */
378 if( error->error_code != XCB_WINDOW )
379 show_error( error );
381 free( error );
384 if( reply ) {
385 managed_property_change( p->window, p->prop, reply );
387 free( reply );
390 free( p );
393 static void managed_property_notify( struct gwm_window *window,
394 xcb_property_notify_event_t *ev ) {
395 int i;
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;
402 p->window = window;
403 p->prop = i;
405 cp.p = p;
406 handle_async_reply( xcb_get_property( c, FALSE, window->w,
407 prop_atoms[ i ],
408 prop_types[ i ], 0,
409 PROP_SIZE ).sequence,
410 handle_managed_get_property, cp );
411 return;
415 static void managed_colormap_notify( struct gwm_window *window,
416 xcb_colormap_notify_event_t *ev ) {
418 if( ev->_new ) {
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 );
429 #if USE_SHAPE
430 extern void match_managed_shape( struct gwm_window *window, int shaped ) {
432 if( shaped ) {
433 xcb_rectangle_t rect;
435 rect.x = rect.y = 0;
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 );
444 } else
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 );
454 #endif
456 event_handler managed_handlers[] = {
457 NULL, /* Error */
458 NULL, /* Reply */
459 NULL, /* KeyPress */
460 NULL, /* KeyRelease */
461 NULL, /* ButtonPress */
462 NULL, /* ButtonRelease */
463 NULL, /* MotionNotify */
464 NULL, /* EnterNotify */
465 NULL, /* LeaveNotify */
466 NULL, /* FocusIn */
467 NULL, /* FocusOut */
468 NULL, /* KeymapNotify */
469 NULL, /* Expose */
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) */
493 #if USE_SHAPE
494 (event_handler) managed_shape_notify
495 #else
496 NULL /* ShapeNotify */
497 #endif