Cast FONT_NAME to (FcChar8 *).
[gwm.git] / frame.c
blob5e92d583b40d70540c684a8718567bc1cc8dec52
1 /*
2 * frame.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>
30 #include "gwm.h"
32 #include "frame.h"
33 #include "window-table.h"
35 #define EDGE_RESIST 8
37 static enum _window_operation {
38 OP_NONE, OP_MOVE, OP_RESIZE
39 } window_op;
40 static enum size_which {
41 SIZE_LESSER, SIZE_NONE, SIZE_GREATER
42 } size_x, size_y;
43 static int dx, dy, init_x, init_y, moved;
44 static struct gwm_window *feedback;
46 static struct h_edge {
47 int x_min, x_max;
48 int y;
49 } *t_edges, *b_edges;
51 static struct v_edge {
52 int x;
53 int y_min, y_max;
54 } *l_edges, *r_edges;
56 static int num_t_edges, num_b_edges, num_l_edges, num_r_edges;
58 static int h_edge_compare( const void *v0, const void *v1 ) {
60 const struct h_edge *e0 = v0, *e1 = v1;
62 return e0->y - e1->y;
65 static int v_edge_compare( const void *v0, const void *v1 ) {
67 const struct v_edge *e0 = v0, *e1 = v1;
69 return e0->x - e1->x;
72 static void build_edges( int screen ) {
74 struct gwm_window *window, **windowp, **end;
76 assert( !t_edges );
77 assert( !b_edges );
78 assert( !l_edges );
79 assert( !r_edges );
81 t_edges = malloc( ( windows.used + 1 ) * sizeof *t_edges );
82 b_edges = malloc( ( windows.used + 1 ) * sizeof *b_edges );
83 l_edges = malloc( ( windows.used + 1 ) * sizeof *l_edges );
84 r_edges = malloc( ( windows.used + 1 ) * sizeof *r_edges );
86 t_edges[ 0 ].x_min = b_edges[ 0 ].x_min = 0;
87 t_edges[ 0 ].x_max = b_edges[ 0 ].x_max =
88 screens[ screen ]->width_in_pixels;
89 t_edges[ 0 ].y = screens[ screen ]->height_in_pixels;
90 b_edges[ 0 ].y = 0;
92 l_edges[ 0 ].x = screens[ screen ]->width_in_pixels;
93 r_edges[ 0 ].x = 0;
94 l_edges[ 0 ].y_min = r_edges[ 0 ].y_min = 0;
95 l_edges[ 0 ].y_max = r_edges[ 0 ].y_max =
96 screens[ screen ]->height_in_pixels;
98 num_t_edges = num_b_edges = num_l_edges = num_r_edges = 1;
100 end = windows.values + windows.used;
101 for( windowp = windows.values; windowp < end; windowp++ )
102 if( ( window = *windowp )->type == WINDOW_FRAME &&
103 window->screen == screen ) {
104 int t = window->u.frame.y,
105 b = window->u.frame.y + window->u.frame.height +
106 ( FRAME_X_BORDER << 1 ),
107 l = window->u.frame.x,
108 r = window->u.frame.x + window->u.frame.width +
109 ( FRAME_X_BORDER << 1 );
111 if( t >= screens[ screen ]->height_in_pixels || b <= 0 ||
112 l >= screens[ screen ]->width_in_pixels || r <= 0 )
113 continue; /* window is entirely off screen; ignore */
115 if( t >= 0 ) {
116 t_edges[ num_t_edges ].x_min = l;
117 t_edges[ num_t_edges ].x_max = r;
118 t_edges[ num_t_edges ].y = t;
119 num_t_edges++;
122 if( b <= screens[ screen ]->height_in_pixels ) {
123 b_edges[ num_b_edges ].x_min = l;
124 b_edges[ num_b_edges ].x_max = r;
125 b_edges[ num_b_edges ].y = b;
126 num_b_edges++;
129 if( l >= 0 ) {
130 l_edges[ num_l_edges ].x = l;
131 l_edges[ num_l_edges ].y_min = t;
132 l_edges[ num_l_edges ].y_max = b;
133 num_l_edges++;
136 if( r <= screens[ screen ]->width_in_pixels ) {
137 r_edges[ num_r_edges ].x = r;
138 r_edges[ num_r_edges ].y_min = t;
139 r_edges[ num_r_edges ].y_max = b;
140 num_r_edges++;
144 qsort( t_edges, num_t_edges, sizeof *t_edges, h_edge_compare );
145 qsort( b_edges, num_b_edges, sizeof *b_edges, h_edge_compare );
146 qsort( l_edges, num_l_edges, sizeof *l_edges, v_edge_compare );
147 qsort( r_edges, num_r_edges, sizeof *r_edges, v_edge_compare );
150 static void free_edges( void ) {
152 assert( t_edges );
153 assert( b_edges );
154 assert( l_edges );
155 assert( r_edges );
157 free( t_edges );
158 t_edges = NULL;
159 free( b_edges );
160 b_edges = NULL;
161 free( l_edges );
162 l_edges = NULL;
163 free( r_edges );
164 r_edges = NULL;
167 static void edge_resist( int old_t, int old_b, int old_l, int old_r,
168 int new_t, int new_b, int new_l, int new_r,
169 int *ex, int *ey ) {
171 *ex = *ey = 0;
173 if( new_t < old_t ) {
174 /* Trying to move up; look for bottom edges with
175 new_t < y <= new_t + EDGE_RESIST. */
176 int i0 = 0, i1 = num_b_edges - 1, i;
178 while( i1 > i0 + 1 ) {
179 int i_mid = ( i0 + i1 ) >> 1;
181 if( b_edges[ i_mid ].y <= new_t )
182 i0 = i_mid;
183 else
184 i1 = i_mid;
187 while( i0 < num_b_edges && b_edges[ i0 ].y <= new_t )
188 i0++;
190 for( i = i0; i < num_b_edges && b_edges[ i ].y <= new_t + EDGE_RESIST;
191 i++ )
192 if( b_edges[ i ].x_min < new_r && b_edges[ i ].x_max > new_l )
193 *ey = b_edges[ i ].y - new_t;
194 } else if( new_b > old_b ) {
195 /* Trying to move down; look for top edges with
196 new_b > y >= new_b - EDGE_RESIST. */
197 int i0 = 0, i1 = num_t_edges - 1, i;
199 while( i1 > i0 + 1 ) {
200 int i_mid = ( i0 + i1 ) >> 1;
202 if( t_edges[ i_mid ].y >= new_b )
203 i1 = i_mid;
204 else
205 i0 = i_mid;
208 while( i1 >= 0 && t_edges[ i1 ].y >= new_b )
209 i1--;
211 for( i = i1; i >= 0 && t_edges[ i ].y >= new_b - EDGE_RESIST; i-- )
212 if( t_edges[ i ].x_min < new_r && t_edges[ i ].x_max > new_l )
213 *ey = t_edges[ i ].y - new_b;
216 if( new_l < old_l ) {
217 /* Trying to move left; look for right edges with
218 new_l < x <= new_l + EDGE_RESIST. */
219 int i0 = 0, i1 = num_r_edges - 1, i;
221 while( i1 > i0 + 1 ) {
222 int i_mid = ( i0 + i1 ) >> 1;
224 if( r_edges[ i_mid ].x <= new_l )
225 i0 = i_mid;
226 else
227 i1 = i_mid;
230 while( i0 < num_r_edges && r_edges[ i0 ].x <= new_l )
231 i0++;
233 for( i = i0; i < num_r_edges && r_edges[ i ].x <= new_l + EDGE_RESIST;
234 i++ )
235 if( r_edges[ i ].y_min < new_b && r_edges[ i ].y_max > new_t )
236 *ex = r_edges[ i ].x - new_l;
237 } else if( new_r > old_r ) {
238 /* Trying to move right; look for left edges with
239 new_r > x >= new_r - EDGE_RESIST. */
240 int i0 = 0, i1 = num_l_edges - 1, i;
242 while( i1 > i0 + 1 ) {
243 int i_mid = ( i0 + i1 ) >> 1;
245 if( l_edges[ i_mid ].x >= new_r )
246 i1 = i_mid;
247 else
248 i0 = i_mid;
251 while( i1 >= 0 && l_edges[ i1 ].x >= new_r )
252 i1--;
254 for( i = i1; i >= 0 && l_edges[ i ].x >= new_r - EDGE_RESIST; i-- )
255 if( l_edges[ i ].y_min < new_b && l_edges[ i ].y_max > new_t )
256 *ex = l_edges[ i ].x - new_r;
260 static void recalc_size( struct gwm_window *window, int x, int y,
261 int apply_edge_resist, xcb_timestamp_t t ) {
263 int new_width, new_height, child_width, child_height;
264 enum size_which old_size_x = size_x, old_size_y = size_y;
265 int old_t, old_b, old_l, old_r, new_t, new_b, new_l, new_r;
267 old_t = window->u.frame.y;
268 old_b = old_t + window->u.frame.height + ( FRAME_X_BORDER << 1 );
269 old_l = window->u.frame.x;
270 old_r = old_l + window->u.frame.width + ( FRAME_X_BORDER << 1 );
272 if( size_x == SIZE_LESSER ) {
273 new_l = x - dx;
274 new_r = old_r;
275 } else if( size_x == SIZE_GREATER ) {
276 new_l = old_l;
277 new_r = x - dx;
278 } else if( x < window->u.frame.x + ( window->u.frame.width >> 2 ) ) {
279 /* Start sizing left border. */
280 size_x = SIZE_LESSER;
281 new_l = old_l;
282 new_r = old_r;
283 dx = x - old_l;
284 } else if( x > window->u.frame.x + window->u.frame.width -
285 ( window->u.frame.width >> 2 ) ) {
286 /* Start sizing right border. */
287 size_x = SIZE_GREATER;
288 new_l = old_l;
289 new_r = old_r;
290 dx = x - old_r;
291 } else {
292 new_l = old_l;
293 new_r = old_r;
296 if( size_y == SIZE_LESSER ) {
297 new_t = y - dy;
298 new_b = old_b;
299 } else if( size_y == SIZE_GREATER ) {
300 new_t = old_t;
301 new_b = y - dy;
302 } else if( y < window->u.frame.y + ( window->u.frame.height >> 2 ) ) {
303 /* Start sizing top border. */
304 size_y = SIZE_LESSER;
305 new_t = old_t;
306 new_b = old_b;
307 dy = y - old_t;
308 } else if( y > window->u.frame.y + window->u.frame.height -
309 ( window->u.frame.height >> 2 ) ) {
310 /* Start sizing bottom border. */
311 size_y = SIZE_GREATER;
312 new_t = old_t;
313 new_b = old_b;
314 dy = y - old_b;
315 } else {
316 new_t = old_t;
317 new_b = old_b;
320 if( size_x != old_size_x || size_y != old_size_y )
321 xcb_change_active_pointer_grab( c, cursors[ CURSOR_TL + size_y * 3 +
322 size_x ], t,
323 XCB_EVENT_MASK_BUTTON_RELEASE |
324 XCB_EVENT_MASK_POINTER_MOTION_HINT |
325 XCB_EVENT_MASK_BUTTON_MOTION );
327 if( apply_edge_resist ) {
328 int ex, ey;
330 edge_resist( old_t, old_b, old_l, old_r,
331 new_t, new_b, new_l, new_r, &ex, &ey );
333 if( ex > 0 )
334 new_l += ex;
335 else
336 new_r += ex;
338 if( ey > 0 )
339 new_t += ey;
340 else
341 new_b += ey;
344 new_width = new_r - new_l - ( FRAME_X_BORDER << 1 );
345 new_height = new_b - new_t - ( FRAME_X_BORDER << 1 );
347 child_width = new_width - ( FRAME_BORDER_WIDTH << 1 );
348 child_height = new_height - FRAME_TITLE_HEIGHT - FRAME_BORDER_WIDTH;
349 apply_size_constraints( window->u.frame.child, &child_width,
350 &child_height );
351 new_width = child_width + ( FRAME_BORDER_WIDTH << 1 );
352 new_height = child_height + FRAME_TITLE_HEIGHT + FRAME_BORDER_WIDTH;
354 if( size_x == SIZE_LESSER )
355 new_l = new_r - new_width - ( FRAME_X_BORDER << 1 );
357 if( size_y == SIZE_LESSER )
358 new_t = new_b - new_height - ( FRAME_X_BORDER << 1 );
360 if( new_l != window->u.frame.x || new_t != window->u.frame.y ||
361 new_width != window->u.frame.width ||
362 new_height != window->u.frame.height ) {
363 int new_fb_width, new_fb_height;
364 uint32_t values[ 4 ] = { new_l, new_t, new_width, new_height };
366 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
367 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
368 XCB_CONFIG_WINDOW_HEIGHT, values );
370 values[ 0 ] = new_width - ( FRAME_BORDER_WIDTH << 1 );
371 values[ 1 ] = new_height - FRAME_TITLE_HEIGHT - FRAME_BORDER_WIDTH;
372 /* FIXME See _NET_WM_SYNC_REQUEST in the EWMH to avoid resizing the
373 window faster than the client can redraw it. */
374 xcb_configure_window( c, window->u.frame.child->w,
375 XCB_CONFIG_WINDOW_WIDTH |
376 XCB_CONFIG_WINDOW_HEIGHT, values );
378 if( !feedback ) {
379 uint32_t values[ 4 ];
381 feedback = add_window( xcb_generate_id( c ) );
382 feedback->screen = window->screen;
383 feedback->type = WINDOW_FEEDBACK;
385 values[ 0 ] = gwm_screens[ window->screen ].pixels[
386 COL_FEEDBACK_BACK ]; /* background pixel */
387 values[ 1 ] = gwm_screens[ window->screen ].pixels[
388 COL_BORDER ]; /* border pixel */
389 values[ 2 ] = TRUE; /* override redirect */
390 values[ 3 ] = XCB_EVENT_MASK_EXPOSURE;
391 xcb_create_window( c, XCB_COPY_FROM_PARENT, feedback->w,
392 screens[ window->screen ]->root,
393 ( screens[ window->screen ]->width_in_pixels -
394 FEEDBACK_WIDTH - 2 ) >> 1,
395 ( screens[ window->screen ]->height_in_pixels -
396 FEEDBACK_HEIGHT - 2 ) >> 1,
397 FEEDBACK_WIDTH, FEEDBACK_HEIGHT, 1,
398 XCB_WINDOW_CLASS_INPUT_OUTPUT,
399 XCB_COPY_FROM_PARENT,
400 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
401 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
402 values );
404 xcb_map_window( c, feedback->w );
407 new_fb_width =
408 ( values[ 0 ] - window->u.frame.child->u.managed.base_width ) /
409 window->u.frame.child->u.managed.width_inc;
410 new_fb_height =
411 ( values[ 1 ] - window->u.frame.child->u.managed.base_height ) /
412 window->u.frame.child->u.managed.height_inc;
414 if( new_fb_width != feedback->u.feedback.fb_width ||
415 new_fb_height != feedback->u.feedback.fb_height ) {
416 feedback->u.feedback.fb_width = new_fb_width;
417 feedback->u.feedback.fb_height = new_fb_height;
419 queue_window_update( feedback, 0, 0, FEEDBACK_WIDTH,
420 FEEDBACK_HEIGHT, FALSE );
425 static void frame_button_press( struct gwm_window *window,
426 xcb_button_press_event_t *ev ) {
428 if( !initial_press( ev ) || ev->child )
429 return;
431 window_op = ( ev->detail > 1 ) == ( ev->event_y < FRAME_TITLE_HEIGHT ) ?
432 OP_RESIZE : OP_MOVE;
434 init_x = ev->root_x;
435 init_y = ev->root_y;
437 if( window_op == OP_RESIZE ) {
438 size_x = size_y = SIZE_NONE;
439 recalc_size( window, ev->root_x, ev->root_y, FALSE, ev->time );
440 } else {
441 dx = ev->event_x;
442 dy = ev->event_y;
444 xcb_change_active_pointer_grab( c, cursors[ CURSOR_C ], ev->time,
445 XCB_EVENT_MASK_BUTTON_RELEASE |
446 XCB_EVENT_MASK_POINTER_MOTION_HINT |
447 XCB_EVENT_MASK_BUTTON_MOTION );
450 moved = FALSE;
453 static void frame_motion_notify( struct gwm_window *window,
454 xcb_motion_notify_event_t *ev ) {
456 uint32_t values[ 2 ];
458 if( window->w != passive_grab )
459 return;
461 /* Hint to the server that we're ready for further motion events. */
462 xcb_query_pointer_unchecked( c, window->w );
464 if( !ev->same_screen )
465 return;
467 if( !moved ) {
468 /* Ignore single pixel movements. */
469 if( abs( ev->root_x - init_x ) > 1 || abs( ev->root_y - init_y ) > 1 ) {
470 moved = TRUE;
472 build_edges( window->screen );
473 } else
474 return;
477 switch( window_op ) {
478 int old_t, old_b, old_l, old_r, new_t, new_b, new_l, new_r;
480 case OP_MOVE:
481 old_t = window->u.frame.y;
482 old_b = old_t + window->u.frame.height + ( FRAME_X_BORDER << 1 );
483 old_l = window->u.frame.x;
484 old_r = old_l + window->u.frame.width + ( FRAME_X_BORDER << 1 );
485 new_t = ev->root_y - dy - FRAME_X_BORDER;
486 new_b = new_t + window->u.frame.height + ( FRAME_X_BORDER << 1 );
487 new_l = ev->root_x - dx - FRAME_X_BORDER;
488 new_r = new_l + window->u.frame.width + ( FRAME_X_BORDER << 1 );
490 if( !( ev->state & XCB_MOD_MASK_CONTROL ) ) {
491 int ex, ey;
493 edge_resist( old_t, old_b, old_l, old_r,
494 new_t, new_b, new_l, new_r, &ex, &ey );
496 new_t += ey;
497 new_b += ey;
498 new_l += ex;
499 new_r += ex;
502 if( new_t != old_t || new_l != old_l ) {
503 values[ 0 ] = new_l;
504 values[ 1 ] = new_t;
505 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
506 XCB_CONFIG_WINDOW_Y, values );
508 /* We're supposed to send the client a synthetic ConfigureNotify,
509 but if we actually moved the window, then we'll do that from
510 our own ConfigureNotify handler. */
513 break;
515 case OP_RESIZE:
516 recalc_size( window, ev->root_x, ev->root_y,
517 !( ev->state & XCB_MOD_MASK_CONTROL ) &&
518 window->u.frame.child->u.managed.width_inc == 1 &&
519 window->u.frame.child->u.managed.height_inc == 1,
520 ev->time );
521 break;
523 default:
524 break;
528 static void frame_button_release( struct gwm_window *window,
529 xcb_button_release_event_t *ev ) {
531 if( !final_release( ev ) )
532 return;
534 if( moved )
535 free_edges();
536 else if( window_op ) {
537 uint32_t n = XCB_STACK_MODE_OPPOSITE;
539 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_STACK_MODE,
540 &n );
543 window_op = OP_NONE;
544 if( feedback ) {
545 xcb_destroy_window( c, feedback->w );
546 forget_window( feedback );
547 feedback = NULL;
551 static void frame_enter_notify( struct gwm_window *window,
552 xcb_enter_notify_event_t *ev ) {
553 uint32_t n;
555 if( focus_frame == window )
556 /* We have the focus already -- probably an inferior change or
557 ungrab. */
558 return;
560 deactivate_focus_frame();
562 focus_frame = window;
564 n = gwm_screens[ window->screen ].pixels[ COL_FRAME_ACTIVE ];
565 xcb_change_window_attributes( c, window->w, XCB_CW_BACK_PIXEL, &n );
567 queue_window_update( window, 0, 0, window->u.frame.width,
568 window->u.frame.height, FALSE );
570 /* FIXME Defer all this focus stuff in case of multiple enter/leave
571 notifies. */
573 if( window->u.frame.child->u.managed.hints & HINT_INPUT )
574 /* Give the client the focus (ICCCM 2.0, section 4.1.7). */
575 /* Ignore Window errors (which can occur if the client destroys
576 the window before our request arrives) and Match errors (which
577 can occur if the window is unmapped first). */
578 handle_error_reply( xcb_set_input_focus_checked(
579 c, XCB_INPUT_FOCUS_POINTER_ROOT,
580 window->u.frame.child->w, ev->time ),
581 ERR_MASK_WINDOW | ERR_MASK_MATCH );
583 if( window->u.frame.child->u.managed.protocols & PROTOCOL_TAKE_FOCUS ) {
584 /* Tell the client to take the focus (ICCCM 2.0, section 4.1.7). */
585 xcb_client_message_event_t msg;
587 msg.response_type = XCB_CLIENT_MESSAGE;
588 msg.format = 32;
589 msg.sequence = 0;
590 msg.window = window->u.frame.child->w;
591 msg.type = atoms[ ATOM_WM_PROTOCOLS ];
592 msg.data.data32[ 0 ] = atoms[ ATOM_WM_TAKE_FOCUS ];
593 msg.data.data32[ 1 ] = ev->time;
594 msg.data.data32[ 2 ] = 0;
595 msg.data.data32[ 3 ] = 0;
596 msg.data.data32[ 4 ] = 0;
598 handle_error_reply( xcb_send_event_checked(
599 c, FALSE, window->u.frame.child->w,
600 0, (char *) &msg ), ERR_MASK_WINDOW );
603 if( !( window->u.frame.child->u.managed.hints & HINT_INPUT ) &&
604 !( window->u.frame.child->u.managed.protocols & PROTOCOL_TAKE_FOCUS ) )
605 /* The client really doesn't want the focus. */
606 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE,
607 XCB_INPUT_FOCUS_POINTER_ROOT, ev->time );
609 install_window_colormap( window->screen, window->u.frame.child,
610 ev->time );
613 extern void synthetic_configure_notify( struct gwm_window *window ) {
615 xcb_configure_notify_event_t msg;
617 assert( window->type == WINDOW_FRAME );
619 /* Send a synthetic ConfigureNotify (ICCCM 2.0, section 4.2.3). */
620 msg.response_type = XCB_CONFIGURE_NOTIFY;
621 msg.event = msg.window = window->u.frame.child->w;
622 msg.above_sibling = XCB_NONE;
623 msg.x = window->u.frame.x + FRAME_BORDER_WIDTH + FRAME_X_BORDER -
624 window->u.frame.child->u.managed.border_width;
625 msg.y = window->u.frame.y + FRAME_TITLE_HEIGHT + FRAME_X_BORDER -
626 window->u.frame.child->u.managed.border_width;
627 msg.width = window->u.frame.width - ( FRAME_BORDER_WIDTH << 1 );
628 msg.height = window->u.frame.height - FRAME_TITLE_HEIGHT -
629 FRAME_BORDER_WIDTH;
630 msg.border_width = window->u.frame.child->u.managed.border_width;
631 msg.override_redirect = FALSE;
632 xcb_send_event( c, FALSE, window->u.frame.child->w,
633 XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &msg );
636 static void frame_map_request( struct gwm_window *window,
637 xcb_map_request_event_t *ev ) {
639 if( ev->window == window->u.frame.child->w )
640 /* A transition to the Normal state (ICCCM 2.0, section 4.1.4). */
641 iconic_to_normal( window->u.frame.child );
644 static void frame_configure_notify( struct gwm_window *window,
645 xcb_configure_notify_event_t *ev ) {
647 if( ev->window != window->w )
648 return;
650 window->u.frame.x = ev->x;
651 window->u.frame.y = ev->y;
652 window->u.frame.width = ev->width;
653 window->u.frame.height = ev->height;
655 synthetic_configure_notify( window );
658 static void frame_configure_request( struct gwm_window *window,
659 xcb_configure_request_event_t *ev ) {
661 int x, y, child_x, child_y, frame_x, frame_y, frame_width, frame_height;
662 uint32_t values[ 5 ];
664 if( ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH )
665 /* Ignore border width request, but remember what was asked for. */
666 window->u.frame.child->u.managed.border_width = ev->border_width;
668 translate_frame_to_child( &child_x, &child_y, window->u.frame.x,
669 window->u.frame.y,
670 window->u.frame.child->u.managed.border_width,
671 window->u.frame.child->u.managed.win_gravity );
673 x = ev->value_mask & XCB_CONFIG_WINDOW_X ? ev->x : child_x;
674 y = ev->value_mask & XCB_CONFIG_WINDOW_Y ? ev->y : child_y;
676 translate_child_to_frame( &frame_x, &frame_y, &frame_width, &frame_height,
677 x, y, ev->width, ev->height,
678 window->u.frame.child->u.managed.border_width,
679 window->u.frame.child->u.managed.win_gravity );
681 values[ 0 ] = frame_x;
682 values[ 1 ] = frame_y;
683 values[ 2 ] = frame_width;
684 values[ 3 ] = frame_height;
685 values[ 4 ] = ev->stack_mode;
686 if( frame_x != window->u.frame.x || frame_y != window->u.frame.y ||
687 frame_width != window->u.frame.width ||
688 frame_height != window->u.frame.height ||
689 ( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) ) {
690 /* We'll also notify the client of any changes, in the ConfigureNotify
691 handler for the event we expect to receive in response to this
692 request. */
693 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
694 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
695 XCB_CONFIG_WINDOW_HEIGHT |
696 ( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ),
697 values );
699 if( frame_width != window->u.frame.width ||
700 frame_height != window->u.frame.height ) {
701 values[ 0 ] = frame_width - ( FRAME_BORDER_WIDTH << 1 );
702 values[ 1 ] = frame_height - FRAME_TITLE_HEIGHT -
703 FRAME_BORDER_WIDTH;
704 xcb_configure_window( c, ev->window, XCB_CONFIG_WINDOW_WIDTH |
705 XCB_CONFIG_WINDOW_HEIGHT, values );
707 } else
708 /* Send a synthetic ConfigureNotify indicating the client's
709 configuration in the root co-ordinate space (ICCCM 2.0,
710 section 4.1.5). */
711 synthetic_configure_notify( window );
714 event_handler frame_handlers[] = {
715 NULL, /* Error */
716 NULL, /* Reply */
717 NULL, /* KeyPress */
718 NULL, /* KeyRelease */
719 (event_handler) frame_button_press,
720 (event_handler) frame_button_release,
721 (event_handler) frame_motion_notify,
722 (event_handler) frame_enter_notify,
723 NULL, /* LeaveNotify */
724 NULL, /* FocusIn */
725 NULL, /* FocusOut */
726 NULL, /* KeymapNotify */
727 (event_handler) generic_expose,
728 NULL, /* GraphicsExpose */
729 NULL, /* NoExposure */
730 NULL, /* VisibilityNotify */
731 NULL, /* CreateNotify */
732 NULL, /* DestroyNotify */
733 NULL, /* UnmapNotify */
734 NULL, /* MapNotify */
735 (event_handler) frame_map_request,
736 NULL, /* ReparentNotify */
737 (event_handler) frame_configure_notify,
738 (event_handler) frame_configure_request,
739 NULL, /* GravityNotify */
740 NULL, /* ResizeRequest */
741 NULL, /* CirculateNotify */
742 NULL, /* CirculateRequest */
743 NULL, /* PropertyNotify */
744 NULL, /* SelectionClear */
745 NULL, /* SelectionRequest */
746 NULL, /* SelectionNotify */
747 NULL, /* ColormapNotify */
748 NULL, /* ClientMessage */
749 NULL, /* MappingNotify */
750 NULL, /* (synthetic) */
751 NULL /* ShapeNotify */