Obtain RGB_COLOR_MAP property length in format units, not bytes.
[gwm.git] / frame.c
blob244d7b93f71b3cdc5c30f4fe93e6ea5aca79621f
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 "actions.h"
33 #include "frame.h"
34 #include "managed.h"
35 #include "window-table.h"
37 #define EDGE_RESIST 8
39 #define FRAME_X_BORDER 1 /* X border added to all four sides of frame */
40 #define FRAME_BORDER_WIDTH 2 /* pixels added to left, right and bottom;
41 includes padding but excludes X border */
42 #define FRAME_TITLE_HEIGHT 16 /* pixels added to top; includes title
43 bar but excludes X border */
45 extern int frame_t( struct gwm_window *window, int include_x_border ) {
47 int base, border;
49 assert( window->type == WINDOW_FRAME );
51 if( window->u.frame.decoration & DEC_BORDER ) {
52 base = FRAME_BORDER_WIDTH;
53 border = FRAME_X_BORDER;
54 } else
55 base = border = 0;
57 if( window->u.frame.decoration & DEC_TITLE )
58 base = FRAME_TITLE_HEIGHT;
60 return base + ( include_x_border ? border : 0 );
63 static int frame_blr( struct gwm_window *window, int include_x_border ) {
65 int base, border;
67 assert( window->type == WINDOW_FRAME );
69 if( window->u.frame.decoration & DEC_BORDER ) {
70 base = FRAME_BORDER_WIDTH;
71 border = FRAME_X_BORDER;
72 } else
73 base = border = 0;
75 return base + ( include_x_border ? border : 0 );
78 extern int frame_b( struct gwm_window *window, int include_x_border ) {
80 return frame_blr( window, include_x_border );
83 extern int frame_l( struct gwm_window *window, int include_x_border ) {
85 return frame_blr( window, include_x_border );
88 extern int frame_r( struct gwm_window *window, int include_x_border ) {
90 return frame_blr( window, include_x_border );
93 extern int frame_xb( struct gwm_window *window ) {
95 assert( window->type == WINDOW_FRAME );
97 return window->u.frame.decoration & DEC_BORDER ? FRAME_X_BORDER : 0;
100 extern void update_frame_extents( struct gwm_window *window ) {
102 uint32_t extents[ 4 ];
104 extents[ 0 ] = frame_l( window, TRUE );
105 extents[ 1 ] = frame_r( window, TRUE );
106 extents[ 2 ] = frame_t( window, TRUE );
107 extents[ 3 ] = frame_b( window, TRUE );
108 xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->u.frame.child->w,
109 atoms[ ATOM__NET_FRAME_EXTENTS ], CARDINAL, 32,
110 4, extents );
113 extern void translate_child_to_frame( struct gwm_window *frame,
114 int *fx, int *fy, int *fwidth,
115 int *fheight, int cx, int cy,
116 int cwidth, int cheight,
117 int cborder, int win_gravity ) {
119 *fwidth = cwidth + frame_l( frame, FALSE ) + frame_r( frame, FALSE );
120 *fheight = cheight + frame_t( frame, FALSE ) + frame_b( frame, FALSE );
122 switch( win_gravity ) {
123 case XCB_GRAVITY_NORTH_WEST:
124 case XCB_GRAVITY_WEST:
125 case XCB_GRAVITY_SOUTH_WEST:
126 default:
127 *fx = cx;
128 break;
130 case XCB_GRAVITY_NORTH:
131 case XCB_GRAVITY_CENTER:
132 case XCB_GRAVITY_SOUTH:
133 *fx = cx + cborder - ( ( frame_l( frame, TRUE ) +
134 frame_r( frame, TRUE ) ) >> 1 );
135 break;
137 case XCB_GRAVITY_NORTH_EAST:
138 case XCB_GRAVITY_EAST:
139 case XCB_GRAVITY_SOUTH_EAST:
140 *fx = cx + ( cborder << 1 ) - frame_l( frame, TRUE ) -
141 frame_r( frame, TRUE );
142 break;
144 case XCB_GRAVITY_STATIC:
145 *fx = cx + cborder - frame_l( frame, TRUE );
148 switch( win_gravity ) {
149 case XCB_GRAVITY_NORTH_WEST:
150 case XCB_GRAVITY_NORTH:
151 case XCB_GRAVITY_NORTH_EAST:
152 default:
153 *fy = cy;
154 break;
156 case XCB_GRAVITY_WEST:
157 case XCB_GRAVITY_CENTER:
158 case XCB_GRAVITY_EAST:
159 *fy = cy + cborder - ( ( frame_t( frame, TRUE ) +
160 frame_b( frame, TRUE ) ) >> 1 );
161 break;
163 case XCB_GRAVITY_SOUTH_WEST:
164 case XCB_GRAVITY_SOUTH:
165 case XCB_GRAVITY_SOUTH_EAST:
166 *fy = cy + ( cborder << 1 ) - frame_t( frame, TRUE ) -
167 frame_b( frame, TRUE );
168 break;
170 case XCB_GRAVITY_STATIC:
171 *fy = cy + cborder - frame_t( frame, TRUE );
172 break;
176 extern void translate_frame_to_child( struct gwm_window *frame,
177 int *cx, int *cy, int fx, int fy,
178 int cborder, int win_gravity ) {
180 switch( win_gravity ) {
181 case XCB_GRAVITY_NORTH_WEST:
182 case XCB_GRAVITY_WEST:
183 case XCB_GRAVITY_SOUTH_WEST:
184 default:
185 *cx = fx;
186 break;
188 case XCB_GRAVITY_NORTH:
189 case XCB_GRAVITY_CENTER:
190 case XCB_GRAVITY_SOUTH:
191 *cx = fx - cborder + ( ( frame_l( frame, TRUE ) +
192 frame_r( frame, TRUE ) ) >> 1 );
193 break;
195 case XCB_GRAVITY_NORTH_EAST:
196 case XCB_GRAVITY_EAST:
197 case XCB_GRAVITY_SOUTH_EAST:
198 *cx = fx - ( cborder << 1 ) + frame_l( frame, TRUE ) +
199 frame_r( frame, TRUE );
200 break;
202 case XCB_GRAVITY_STATIC:
203 *cx = fx - cborder + frame_l( frame, TRUE );
204 break;
207 switch( win_gravity ) {
208 case XCB_GRAVITY_NORTH_WEST:
209 case XCB_GRAVITY_NORTH:
210 case XCB_GRAVITY_NORTH_EAST:
211 default:
212 *cy = fy;
213 break;
215 case XCB_GRAVITY_WEST:
216 case XCB_GRAVITY_CENTER:
217 case XCB_GRAVITY_EAST:
218 *cy = fy - cborder + ( ( frame_t( frame, TRUE ) +
219 frame_b( frame, TRUE ) ) >> 1 );
220 break;
222 case XCB_GRAVITY_SOUTH_WEST:
223 case XCB_GRAVITY_SOUTH:
224 case XCB_GRAVITY_SOUTH_EAST:
225 *cy = fy - ( cborder << 1 ) + frame_t( frame, TRUE ) +
226 frame_b( frame, TRUE );
227 break;
229 case XCB_GRAVITY_STATIC:
230 *cy = fy - cborder + frame_t( frame, TRUE );
231 break;
235 extern void apply_size_constraints( struct gwm_window *window, int *width,
236 int *height ) {
238 int eff_base_width = window->u.managed.base_width ?
239 window->u.managed.base_width : window->u.managed.min_width,
240 eff_base_height = window->u.managed.base_height ?
241 window->u.managed.base_height : window->u.managed.min_height;
243 /* Apply the minimum and maximum constraints. These are already known
244 to be compatible. */
245 if( *width < window->u.managed.min_width )
246 *width = window->u.managed.min_width;
248 if( *height < window->u.managed.min_height )
249 *height = window->u.managed.min_height;
251 if( *width > window->u.managed.max_width )
252 *width = window->u.managed.max_width;
254 if( *height > window->u.managed.max_height )
255 *height = window->u.managed.max_height;
257 /* Now round down each dimension to an integer multiple of increments.
258 Rounding down cannot violate the maximum constraint, and since
259 eff_base_* >= min_*, it will not reduce below the minimum constraint. */
260 *width -= ( *width - eff_base_width ) % window->u.managed.width_inc;
261 *height -= ( *height - eff_base_height ) % window->u.managed.height_inc;
263 if( window->u.managed.min_aspect_x * *height >
264 window->u.managed.min_aspect_y * *width ) {
265 /* Minimum aspect ratio violated. Attempt to either increase the
266 width or decrease the height (whichever is a smaller change), but
267 don't do either if it would go outside the min/max bounds.
268 Both division operations are safe (min_aspect_y is always
269 positive, and min_aspect_x must be positive if there is a
270 violation). Note that an exact solution might not be possible
271 (e.g. certain cases where the aspect ratio and increments are
272 coprime). */
273 int min_x, max_y;
275 min_x = ( window->u.managed.min_aspect_x * *height +
276 ( window->u.managed.min_aspect_y - 1 ) ) /
277 window->u.managed.min_aspect_y + window->u.managed.width_inc - 1;
278 min_x -= ( min_x - eff_base_width ) % window->u.managed.width_inc;
280 max_y = window->u.managed.min_aspect_y * *width /
281 window->u.managed.min_aspect_x;
282 max_y -= ( max_y - eff_base_height ) % window->u.managed.height_inc;
284 if( min_x - *width < *height - max_y ) {
285 /* The width change is smaller: prefer it if possible. */
286 if( min_x >= window->u.managed.min_width )
287 *width = min_x;
288 else if( max_y < window->u.managed.max_height )
289 *height = max_y;
290 } else {
291 /* The height change is smaller: prefer it if possible. */
292 if( max_y < window->u.managed.max_height )
293 *height = max_y;
294 else if( min_x >= window->u.managed.min_width )
295 *width = min_x;
299 if( window->u.managed.max_aspect_x * *height <
300 window->u.managed.max_aspect_y * *width ) {
301 /* Maximum aspect ratio violated. Much like the case above... */
302 int min_y, max_x;
304 min_y = ( window->u.managed.max_aspect_y * *width +
305 ( window->u.managed.max_aspect_x - 1 ) ) /
306 window->u.managed.max_aspect_x + window->u.managed.height_inc - 1;
307 min_y -= ( min_y - eff_base_height ) % window->u.managed.height_inc;
309 max_x = window->u.managed.max_aspect_x * *height /
310 window->u.managed.max_aspect_y;
311 max_x -= ( max_x - eff_base_width ) % window->u.managed.width_inc;
313 if( min_y - *height < *width - max_x ) {
314 /* The height change is smaller: prefer it if possible. */
315 if( min_y >= window->u.managed.min_height )
316 *height = min_y;
317 else if( max_x < window->u.managed.max_width )
318 *width = max_x;
319 } else {
320 /* The width change is smaller: prefer it if possible. */
321 if( max_x < window->u.managed.max_width )
322 *width = max_x;
323 else if( min_y >= window->u.managed.min_height )
324 *height = min_y;
329 struct gwm_window *focus_frame;
331 extern void deactivate_focus_frame( void ) {
333 uint32_t n;
335 if( !focus_frame )
336 return;
338 if( focus_frame->u.frame.decoration & ( DEC_TITLE | DEC_BORDER ) ) {
339 n = gwm_screens[ focus_frame->screen ].pixels[ COL_FRAME_INACTIVE ];
340 xcb_change_window_attributes( c, focus_frame->w, XCB_CW_BACK_PIXEL,
341 &n );
344 if( focus_frame->u.frame.decoration & DEC_TITLE )
345 queue_window_update( focus_frame, 0, 0, focus_frame->u.frame.width,
346 focus_frame->u.frame.height, FALSE );
349 static enum _window_operation {
350 OP_NONE, OP_MOVE, OP_RESIZE
351 } window_op;
352 static enum size_which {
353 SIZE_LESSER, SIZE_NONE, SIZE_GREATER
354 } size_x, size_y;
355 static int dx, dy, init_x, init_y, moved;
356 static struct gwm_window *feedback;
358 static struct h_edge {
359 int x_min, x_max;
360 int y;
361 } *t_edges, *b_edges;
363 static struct v_edge {
364 int x;
365 int y_min, y_max;
366 } *l_edges, *r_edges;
368 static int num_t_edges, num_b_edges, num_l_edges, num_r_edges;
370 static int h_edge_compare( const void *v0, const void *v1 ) {
372 const struct h_edge *e0 = v0, *e1 = v1;
374 return e0->y - e1->y;
377 static int v_edge_compare( const void *v0, const void *v1 ) {
379 const struct v_edge *e0 = v0, *e1 = v1;
381 return e0->x - e1->x;
384 static void build_edges( int screen ) {
386 struct gwm_window *window, **windowp, **end;
388 assert( !t_edges );
389 assert( !b_edges );
390 assert( !l_edges );
391 assert( !r_edges );
393 t_edges = xmalloc( ( windows.used + 1 ) * sizeof *t_edges );
394 b_edges = xmalloc( ( windows.used + 1 ) * sizeof *b_edges );
395 l_edges = xmalloc( ( windows.used + 1 ) * sizeof *l_edges );
396 r_edges = xmalloc( ( windows.used + 1 ) * sizeof *r_edges );
398 t_edges[ 0 ].x_min = b_edges[ 0 ].x_min = 0;
399 t_edges[ 0 ].x_max = b_edges[ 0 ].x_max =
400 screens[ screen ]->width_in_pixels;
401 t_edges[ 0 ].y = screens[ screen ]->height_in_pixels;
402 b_edges[ 0 ].y = 0;
404 l_edges[ 0 ].x = screens[ screen ]->width_in_pixels;
405 r_edges[ 0 ].x = 0;
406 l_edges[ 0 ].y_min = r_edges[ 0 ].y_min = 0;
407 l_edges[ 0 ].y_max = r_edges[ 0 ].y_max =
408 screens[ screen ]->height_in_pixels;
410 num_t_edges = num_b_edges = num_l_edges = num_r_edges = 1;
412 end = windows.values + windows.used;
413 for( windowp = windows.values; windowp < end; windowp++ )
414 if( ( window = *windowp )->type == WINDOW_FRAME &&
415 window->screen == screen ) {
416 int t = window->u.frame.y,
417 b = window->u.frame.y + window->u.frame.height +
418 ( frame_xb( window ) << 1 ),
419 l = window->u.frame.x,
420 r = window->u.frame.x + window->u.frame.width +
421 ( frame_xb( window ) << 1 );
423 if( t >= screens[ screen ]->height_in_pixels || b <= 0 ||
424 l >= screens[ screen ]->width_in_pixels || r <= 0 )
425 continue; /* window is entirely off screen; ignore */
427 if( t >= 0 ) {
428 t_edges[ num_t_edges ].x_min = l;
429 t_edges[ num_t_edges ].x_max = r;
430 t_edges[ num_t_edges ].y = t;
431 num_t_edges++;
434 if( b <= screens[ screen ]->height_in_pixels ) {
435 b_edges[ num_b_edges ].x_min = l;
436 b_edges[ num_b_edges ].x_max = r;
437 b_edges[ num_b_edges ].y = b;
438 num_b_edges++;
441 if( l >= 0 ) {
442 l_edges[ num_l_edges ].x = l;
443 l_edges[ num_l_edges ].y_min = t;
444 l_edges[ num_l_edges ].y_max = b;
445 num_l_edges++;
448 if( r <= screens[ screen ]->width_in_pixels ) {
449 r_edges[ num_r_edges ].x = r;
450 r_edges[ num_r_edges ].y_min = t;
451 r_edges[ num_r_edges ].y_max = b;
452 num_r_edges++;
456 qsort( t_edges, num_t_edges, sizeof *t_edges, h_edge_compare );
457 qsort( b_edges, num_b_edges, sizeof *b_edges, h_edge_compare );
458 qsort( l_edges, num_l_edges, sizeof *l_edges, v_edge_compare );
459 qsort( r_edges, num_r_edges, sizeof *r_edges, v_edge_compare );
462 static void free_edges( void ) {
464 assert( t_edges );
465 assert( b_edges );
466 assert( l_edges );
467 assert( r_edges );
469 free( t_edges );
470 t_edges = NULL;
471 free( b_edges );
472 b_edges = NULL;
473 free( l_edges );
474 l_edges = NULL;
475 free( r_edges );
476 r_edges = NULL;
479 static void edge_resist( int old_t, int old_b, int old_l, int old_r,
480 int new_t, int new_b, int new_l, int new_r,
481 int *ex, int *ey ) {
483 *ex = *ey = 0;
485 if( new_t < old_t ) {
486 /* Trying to move up; look for bottom edges with
487 new_t < y <= new_t + EDGE_RESIST. */
488 int i0 = 0, i1 = num_b_edges - 1, i;
490 while( i1 > i0 + 1 ) {
491 int i_mid = ( i0 + i1 ) >> 1;
493 if( b_edges[ i_mid ].y <= new_t )
494 i0 = i_mid;
495 else
496 i1 = i_mid;
499 while( i0 < num_b_edges && b_edges[ i0 ].y <= new_t )
500 i0++;
502 for( i = i0; i < num_b_edges && b_edges[ i ].y <= new_t + EDGE_RESIST;
503 i++ )
504 if( b_edges[ i ].x_min < new_r && b_edges[ i ].x_max > new_l )
505 *ey = b_edges[ i ].y - new_t;
506 } else if( new_b > old_b ) {
507 /* Trying to move down; look for top edges with
508 new_b > y >= new_b - EDGE_RESIST. */
509 int i0 = 0, i1 = num_t_edges - 1, i;
511 while( i1 > i0 + 1 ) {
512 int i_mid = ( i0 + i1 ) >> 1;
514 if( t_edges[ i_mid ].y >= new_b )
515 i1 = i_mid;
516 else
517 i0 = i_mid;
520 while( i1 >= 0 && t_edges[ i1 ].y >= new_b )
521 i1--;
523 for( i = i1; i >= 0 && t_edges[ i ].y >= new_b - EDGE_RESIST; i-- )
524 if( t_edges[ i ].x_min < new_r && t_edges[ i ].x_max > new_l )
525 *ey = t_edges[ i ].y - new_b;
528 if( new_l < old_l ) {
529 /* Trying to move left; look for right edges with
530 new_l < x <= new_l + EDGE_RESIST. */
531 int i0 = 0, i1 = num_r_edges - 1, i;
533 while( i1 > i0 + 1 ) {
534 int i_mid = ( i0 + i1 ) >> 1;
536 if( r_edges[ i_mid ].x <= new_l )
537 i0 = i_mid;
538 else
539 i1 = i_mid;
542 while( i0 < num_r_edges && r_edges[ i0 ].x <= new_l )
543 i0++;
545 for( i = i0; i < num_r_edges && r_edges[ i ].x <= new_l + EDGE_RESIST;
546 i++ )
547 if( r_edges[ i ].y_min < new_b && r_edges[ i ].y_max > new_t )
548 *ex = r_edges[ i ].x - new_l;
549 } else if( new_r > old_r ) {
550 /* Trying to move right; look for left edges with
551 new_r > x >= new_r - EDGE_RESIST. */
552 int i0 = 0, i1 = num_l_edges - 1, i;
554 while( i1 > i0 + 1 ) {
555 int i_mid = ( i0 + i1 ) >> 1;
557 if( l_edges[ i_mid ].x >= new_r )
558 i1 = i_mid;
559 else
560 i0 = i_mid;
563 while( i1 >= 0 && l_edges[ i1 ].x >= new_r )
564 i1--;
566 for( i = i1; i >= 0 && l_edges[ i ].x >= new_r - EDGE_RESIST; i-- )
567 if( l_edges[ i ].y_min < new_b && l_edges[ i ].y_max > new_t )
568 *ex = l_edges[ i ].x - new_r;
572 static void recalc_size( struct gwm_window *window, int x, int y,
573 int apply_edge_resist, xcb_timestamp_t t ) {
575 int new_width, new_height, child_width, child_height;
576 enum size_which old_size_x = size_x, old_size_y = size_y;
577 int old_t, old_b, old_l, old_r, new_t, new_b, new_l, new_r;
579 old_t = window->u.frame.y;
580 old_b = old_t + window->u.frame.height + ( frame_xb( window ) << 1 );
581 old_l = window->u.frame.x;
582 old_r = old_l + window->u.frame.width + ( frame_xb( window ) << 1 );
584 if( size_x == SIZE_LESSER ) {
585 new_l = x - dx;
586 new_r = old_r;
587 } else if( size_x == SIZE_GREATER ) {
588 new_l = old_l;
589 new_r = x - dx;
590 } else if( x < window->u.frame.x + ( window->u.frame.width >> 2 ) ) {
591 /* Start sizing left border. */
592 size_x = SIZE_LESSER;
593 new_l = old_l;
594 new_r = old_r;
595 dx = x - old_l;
596 } else if( x > window->u.frame.x + window->u.frame.width -
597 ( window->u.frame.width >> 2 ) ) {
598 /* Start sizing right border. */
599 size_x = SIZE_GREATER;
600 new_l = old_l;
601 new_r = old_r;
602 dx = x - old_r;
603 } else {
604 new_l = old_l;
605 new_r = old_r;
608 if( size_y == SIZE_LESSER ) {
609 new_t = y - dy;
610 new_b = old_b;
611 } else if( size_y == SIZE_GREATER ) {
612 new_t = old_t;
613 new_b = y - dy;
614 } else if( y < window->u.frame.y + ( window->u.frame.height >> 2 ) ) {
615 /* Start sizing top border. */
616 size_y = SIZE_LESSER;
617 new_t = old_t;
618 new_b = old_b;
619 dy = y - old_t;
620 } else if( y > window->u.frame.y + window->u.frame.height -
621 ( window->u.frame.height >> 2 ) ) {
622 /* Start sizing bottom border. */
623 size_y = SIZE_GREATER;
624 new_t = old_t;
625 new_b = old_b;
626 dy = y - old_b;
627 } else {
628 new_t = old_t;
629 new_b = old_b;
632 if( size_x != old_size_x || size_y != old_size_y )
633 xcb_change_active_pointer_grab( c, cursors[ CURSOR_TL + size_y * 3 +
634 size_x ], t,
635 XCB_EVENT_MASK_BUTTON_RELEASE |
636 XCB_EVENT_MASK_POINTER_MOTION_HINT |
637 XCB_EVENT_MASK_BUTTON_MOTION );
639 if( apply_edge_resist ) {
640 int ex, ey;
642 edge_resist( old_t, old_b, old_l, old_r,
643 new_t, new_b, new_l, new_r, &ex, &ey );
645 if( ex > 0 )
646 new_l += ex;
647 else
648 new_r += ex;
650 if( ey > 0 )
651 new_t += ey;
652 else
653 new_b += ey;
656 new_width = new_r - new_l - ( frame_xb( window ) << 1 );
657 new_height = new_b - new_t - ( frame_xb( window ) << 1 );
659 child_width = new_width - frame_l( window, FALSE ) -
660 frame_r( window, FALSE );
661 child_height = new_height - frame_t( window, FALSE ) -
662 frame_b( window, FALSE );
663 apply_size_constraints( window->u.frame.child, &child_width,
664 &child_height );
665 new_width = child_width + frame_l( window, FALSE ) +
666 frame_r( window, FALSE );
667 new_height = child_height + frame_t( window, FALSE ) +
668 frame_b( window, FALSE );
670 if( size_x == SIZE_LESSER )
671 new_l = new_r - new_width - ( frame_xb( window ) << 1 );
673 if( size_y == SIZE_LESSER )
674 new_t = new_b - new_height - ( frame_xb( window ) << 1 );
676 if( new_l != window->u.frame.x || new_t != window->u.frame.y ||
677 new_width != window->u.frame.width ||
678 new_height != window->u.frame.height ) {
679 int disp_width, disp_height;
680 int new_fb_width, new_fb_height;
681 uint32_t values[ 4 ];
683 values[ 0 ] = new_l;
684 values[ 1 ] = new_t;
685 values[ 2 ] = new_width;
686 values[ 3 ] = new_height;
687 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
688 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
689 XCB_CONFIG_WINDOW_HEIGHT, values );
691 values[ 0 ] = new_width - frame_l( window, FALSE ) -
692 frame_r( window, FALSE );
693 values[ 1 ] = new_height - frame_t( window, FALSE ) -
694 frame_b( window, FALSE );
695 /* FIXME See _NET_WM_SYNC_REQUEST in the EWMH to avoid resizing the
696 window faster than the client can redraw it. */
697 xcb_configure_window( c, window->u.frame.child->w,
698 XCB_CONFIG_WINDOW_WIDTH |
699 XCB_CONFIG_WINDOW_HEIGHT, values );
701 if( feedback )
702 window_size( feedback, &disp_width, &disp_height );
703 else {
704 uint32_t values[ 4 ];
706 feedback = add_window( xcb_generate_id( c ) );
707 feedback->screen = window->screen;
708 feedback->type = WINDOW_FEEDBACK;
709 feedback->u.feedback.fb_width =
710 feedback->u.feedback.fb_height = -1;
711 window_size( feedback, &disp_width, &disp_height );
713 values[ 0 ] = gwm_screens[ window->screen ].pixels[
714 COL_FEEDBACK_BACK ]; /* background pixel */
715 values[ 1 ] = gwm_screens[ window->screen ].pixels[
716 COL_BORDER ]; /* border pixel */
717 values[ 2 ] = TRUE; /* override redirect */
718 values[ 3 ] = XCB_EVENT_MASK_EXPOSURE;
719 xcb_create_window( c, XCB_COPY_FROM_PARENT, feedback->w,
720 screens[ window->screen ]->root,
721 ( screens[ window->screen ]->width_in_pixels -
722 disp_width - 2 ) >> 1,
723 ( screens[ window->screen ]->height_in_pixels -
724 disp_height - 2 ) >> 1,
725 disp_width, disp_height, 1,
726 XCB_WINDOW_CLASS_INPUT_OUTPUT,
727 XCB_COPY_FROM_PARENT,
728 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
729 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
730 values );
732 xcb_map_window( c, feedback->w );
735 new_fb_width =
736 ( values[ 0 ] - window->u.frame.child->u.managed.base_width ) /
737 window->u.frame.child->u.managed.width_inc;
738 new_fb_height =
739 ( values[ 1 ] - window->u.frame.child->u.managed.base_height ) /
740 window->u.frame.child->u.managed.height_inc;
742 if( new_fb_width != feedback->u.feedback.fb_width ||
743 new_fb_height != feedback->u.feedback.fb_height ) {
744 feedback->u.feedback.fb_width = new_fb_width;
745 feedback->u.feedback.fb_height = new_fb_height;
747 queue_window_update( feedback, 0, 0, disp_width, disp_height,
748 FALSE );
753 static void frame_button_press( struct gwm_window *window,
754 xcb_button_press_event_t *ev ) {
756 if( pointer_demux || ev->child == window->u.frame.child->w )
757 return;
759 if( ev->detail == 2 ) {
760 union callback_param cp;
762 cp.p = NULL;
763 action_window_menu( window, (xcb_generic_event_t *) ev, cp );
764 return;
767 pointer_demux = window->w;
769 window_op = ( ev->detail > 1 ) ==
770 ( ( window->u.frame.decoration & DEC_TITLE ) &&
771 ev->event_y < frame_t( window, FALSE ) ) ? OP_RESIZE : OP_MOVE;
773 init_x = ev->root_x;
774 init_y = ev->root_y;
776 if( window_op == OP_RESIZE ) {
777 size_x = size_y = SIZE_NONE;
778 recalc_size( window, ev->root_x, ev->root_y, FALSE, ev->time );
779 } else {
780 dx = ev->event_x;
781 dy = ev->event_y;
783 xcb_change_active_pointer_grab( c, cursors[ CURSOR_C ], ev->time,
784 XCB_EVENT_MASK_BUTTON_RELEASE |
785 XCB_EVENT_MASK_POINTER_MOTION_HINT |
786 XCB_EVENT_MASK_BUTTON_MOTION );
789 moved = FALSE;
792 static void frame_motion_notify( struct gwm_window *window,
793 xcb_motion_notify_event_t *ev ) {
795 uint32_t values[ 2 ];
797 if( !pointer_demux )
798 return;
800 /* Hint to the server that we're ready for further motion events. */
801 xcb_query_pointer_unchecked( c, window->w );
803 if( !ev->same_screen )
804 return;
806 if( !moved ) {
807 /* Ignore single pixel movements. */
808 if( abs( ev->root_x - init_x ) > 1 || abs( ev->root_y - init_y ) > 1 ) {
809 moved = TRUE;
811 build_edges( window->screen );
812 } else
813 return;
816 switch( window_op ) {
817 int old_t, old_b, old_l, old_r, new_t, new_b, new_l, new_r;
819 case OP_MOVE:
820 old_t = window->u.frame.y;
821 old_b = old_t + window->u.frame.height + ( frame_xb( window ) << 1 );
822 old_l = window->u.frame.x;
823 old_r = old_l + window->u.frame.width + ( frame_xb( window ) << 1 );
824 new_t = ev->root_y - dy - frame_xb( window );
825 new_b = new_t + window->u.frame.height + ( frame_xb( window ) << 1 );
826 new_l = ev->root_x - dx - frame_xb( window );
827 new_r = new_l + window->u.frame.width + ( frame_xb( window ) << 1 );
829 if( !( ev->state & XCB_MOD_MASK_CONTROL ) ) {
830 int ex, ey;
832 edge_resist( old_t, old_b, old_l, old_r,
833 new_t, new_b, new_l, new_r, &ex, &ey );
835 new_t += ey;
836 new_b += ey;
837 new_l += ex;
838 new_r += ex;
841 if( new_t != old_t || new_l != old_l ) {
842 values[ 0 ] = new_l;
843 values[ 1 ] = new_t;
844 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
845 XCB_CONFIG_WINDOW_Y, values );
847 /* We're supposed to send the client a synthetic ConfigureNotify,
848 but if we actually moved the window, then we'll do that from
849 our own ConfigureNotify handler. */
852 break;
854 case OP_RESIZE:
855 recalc_size( window, ev->root_x, ev->root_y,
856 !( ev->state & XCB_MOD_MASK_CONTROL ) &&
857 window->u.frame.child->u.managed.width_inc == 1 &&
858 window->u.frame.child->u.managed.height_inc == 1,
859 ev->time );
860 break;
862 default:
863 break;
867 static void frame_button_release( struct gwm_window *window,
868 xcb_button_release_event_t *ev ) {
870 if( !final_release( ev ) || !pointer_demux )
871 return;
873 if( moved )
874 free_edges();
875 else if( window_op ) {
876 uint32_t n = XCB_STACK_MODE_OPPOSITE;
878 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_STACK_MODE,
879 &n );
882 window_op = OP_NONE;
883 if( feedback ) {
884 xcb_destroy_window( c, feedback->w );
885 forget_window( feedback );
886 feedback = NULL;
889 pointer_demux = XCB_NONE;
892 static void frame_enter_notify( struct gwm_window *window,
893 xcb_enter_notify_event_t *ev ) {
894 uint32_t n;
896 if( pointer_demux || focus_frame == window )
897 /* We have the focus already -- probably an inferior change or
898 ungrab. */
899 return;
901 deactivate_focus_frame();
903 focus_frame = window;
905 if( window->u.frame.decoration & ( DEC_TITLE | DEC_BORDER ) ) {
906 n = gwm_screens[ window->screen ].pixels[ COL_FRAME_ACTIVE ];
907 xcb_change_window_attributes( c, window->w, XCB_CW_BACK_PIXEL, &n );
910 if( window->u.frame.decoration & DEC_TITLE )
911 queue_window_update( window, 0, 0, window->u.frame.width,
912 window->u.frame.height, FALSE );
914 /* FIXME Defer all this focus stuff in case of multiple enter/leave
915 notifies. */
917 if( window->u.frame.child->u.managed.hints & HINT_INPUT )
918 /* Give the client the focus (ICCCM 2.0, section 4.1.7). */
919 /* Ignore Window errors (which can occur if the client destroys
920 the window before our request arrives) and Match errors (which
921 can occur if the window is unmapped first). */
922 handle_error_reply( xcb_set_input_focus_checked(
923 c, XCB_INPUT_FOCUS_POINTER_ROOT,
924 window->u.frame.child->w, ev->time ),
925 ERR_MASK_WINDOW | ERR_MASK_MATCH );
927 if( window->u.frame.child->u.managed.protocols & PROTOCOL_TAKE_FOCUS ) {
928 /* Tell the client to take the focus (ICCCM 2.0, section 4.1.7). */
929 xcb_client_message_event_t msg;
931 msg.response_type = XCB_CLIENT_MESSAGE;
932 msg.format = 32;
933 msg.sequence = 0;
934 msg.window = window->u.frame.child->w;
935 msg.type = atoms[ ATOM_WM_PROTOCOLS ];
936 msg.data.data32[ 0 ] = atoms[ ATOM_WM_TAKE_FOCUS ];
937 msg.data.data32[ 1 ] = ev->time;
938 msg.data.data32[ 2 ] = 0;
939 msg.data.data32[ 3 ] = 0;
940 msg.data.data32[ 4 ] = 0;
942 handle_error_reply( xcb_send_event_checked(
943 c, FALSE, window->u.frame.child->w,
944 0, (char *) &msg ), ERR_MASK_WINDOW );
947 if( !( window->u.frame.child->u.managed.hints & HINT_INPUT ) &&
948 !( window->u.frame.child->u.managed.protocols & PROTOCOL_TAKE_FOCUS ) )
949 /* The client really doesn't want the focus. */
950 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE,
951 XCB_INPUT_FOCUS_POINTER_ROOT, ev->time );
953 install_window_colormap( window->screen, window->u.frame.child,
954 ev->time );
957 static void frame_destroy_notify( struct gwm_window *window,
958 xcb_destroy_notify_event_t *ev ) {
960 if( window->w != pointer_demux )
961 return;
963 if( moved )
964 free_edges();
966 if( feedback ) {
967 xcb_destroy_window( c, feedback->w );
968 forget_window( feedback );
969 feedback = NULL;
973 static void frame_map_request( struct gwm_window *window,
974 xcb_map_request_event_t *ev ) {
976 if( ev->window == window->u.frame.child->w )
977 /* A transition to the Normal state (ICCCM 2.0, section 4.1.4). */
978 iconic_to_normal( window->u.frame.child );
981 extern void synthetic_configure_notify( struct gwm_window *window ) {
983 xcb_configure_notify_event_t msg;
985 assert( window->type == WINDOW_FRAME );
987 /* Send a synthetic ConfigureNotify (ICCCM 2.0, section 4.2.3). */
988 msg.response_type = XCB_CONFIGURE_NOTIFY;
989 msg.event = msg.window = window->u.frame.child->w;
990 msg.above_sibling = XCB_NONE;
991 msg.x = window->u.frame.x + frame_l( window, TRUE ) -
992 window->u.frame.child->u.managed.border_width;
993 msg.y = window->u.frame.y + frame_t( window, TRUE ) -
994 window->u.frame.child->u.managed.border_width;
995 msg.width = window->u.frame.width - frame_l( window, FALSE ) -
996 frame_r( window, FALSE );
997 msg.height = window->u.frame.height - frame_t( window, FALSE ) -
998 frame_b( window, FALSE );
999 msg.border_width = window->u.frame.child->u.managed.border_width;
1000 msg.override_redirect = FALSE;
1001 xcb_send_event( c, FALSE, window->u.frame.child->w,
1002 XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &msg );
1005 static void frame_configure_notify( struct gwm_window *window,
1006 xcb_configure_notify_event_t *ev ) {
1008 int i;
1010 if( ev->window != window->w )
1011 return;
1013 window->u.frame.x = ev->x;
1014 window->u.frame.y = ev->y;
1015 window->u.frame.width = ev->width;
1016 window->u.frame.height = ev->height;
1018 for( i = 0; i < NUM_BORDER_REGIONS; i++ ) {
1019 uint32_t values[ 4 ]; /* x, y, width and height respectively */
1021 switch( i ) {
1022 case BR_TL:
1023 case BR_L:
1024 case BR_BL:
1025 values[ 0 ] = 0;
1026 values[ 2 ] = window->u.frame.width >> 2;
1027 break;
1029 case BR_T:
1030 case BR_B:
1031 values[ 0 ] = window->u.frame.width >> 2;
1032 values[ 2 ] = window->u.frame.width - ( values[ 0 ] << 1 );
1033 break;
1035 case BR_TR:
1036 case BR_R:
1037 case BR_BR:
1038 values[ 2 ] = window->u.frame.width >> 2;
1039 values[ 0 ] = window->u.frame.width - values[ 2 ];
1040 break;
1043 switch( i ) {
1044 case BR_TL:
1045 case BR_T:
1046 case BR_TR:
1047 if( window->u.frame.decoration & DEC_TITLE ) {
1048 values[ 1 ] = frame_t( window, FALSE );
1049 values[ 3 ] = ( window->u.frame.height >> 2 ) - values[ 1 ];
1050 } else {
1051 values[ 1 ] = 0;
1052 values[ 3 ] = window->u.frame.height >> 2;
1054 break;
1056 case BR_L:
1057 case BR_R:
1058 values[ 1 ] = window->u.frame.height >> 2;
1059 values[ 3 ] = window->u.frame.height - ( values[ 1 ] << 1 );
1060 break;
1062 case BR_BL:
1063 case BR_B:
1064 case BR_BR:
1065 values[ 3 ] = window->u.frame.height >> 2;
1066 values[ 1 ] = window->u.frame.height - values[ 3 ];
1069 if( window->u.frame.decoration & DEC_BORDER &&
1070 values[ 2 ] > 0 && values[ 3 ] > 0 &&
1071 !( i == BR_T && ( window->u.frame.decoration & DEC_TITLE ) ) ) {
1072 xcb_configure_window( c, window->u.frame.border_regions[ i ],
1073 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
1074 XCB_CONFIG_WINDOW_WIDTH |
1075 XCB_CONFIG_WINDOW_HEIGHT, values );
1076 xcb_map_window( c, window->u.frame.border_regions[ i ] );
1077 } else
1078 xcb_unmap_window( c, window->u.frame.border_regions[ i ] );
1081 synthetic_configure_notify( window );
1084 static void frame_configure_request( struct gwm_window *window,
1085 xcb_configure_request_event_t *ev ) {
1087 int x, y, child_x, child_y, frame_x, frame_y, frame_width, frame_height;
1088 uint32_t values[ 5 ];
1090 if( ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH )
1091 /* Ignore border width request, but remember what was asked for. */
1092 window->u.frame.child->u.managed.border_width = ev->border_width;
1094 translate_frame_to_child( window, &child_x, &child_y,
1095 window->u.frame.x, window->u.frame.y,
1096 window->u.frame.child->u.managed.border_width,
1097 window->u.frame.child->u.managed.win_gravity );
1099 x = ev->value_mask & XCB_CONFIG_WINDOW_X ? ev->x : child_x;
1100 y = ev->value_mask & XCB_CONFIG_WINDOW_Y ? ev->y : child_y;
1102 translate_child_to_frame( window, &frame_x, &frame_y,
1103 &frame_width, &frame_height, x, y,
1104 ev->width, ev->height,
1105 window->u.frame.child->u.managed.border_width,
1106 window->u.frame.child->u.managed.win_gravity );
1108 values[ 0 ] = frame_x;
1109 values[ 1 ] = frame_y;
1110 values[ 2 ] = frame_width;
1111 values[ 3 ] = frame_height;
1112 values[ 4 ] = ev->stack_mode;
1113 if( frame_x != window->u.frame.x || frame_y != window->u.frame.y ||
1114 frame_width != window->u.frame.width ||
1115 frame_height != window->u.frame.height ||
1116 ( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) ) {
1117 /* We'll also notify the client of any changes, in the ConfigureNotify
1118 handler for the event we expect to receive in response to this
1119 request. */
1120 xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X |
1121 XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
1122 XCB_CONFIG_WINDOW_HEIGHT |
1123 ( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ),
1124 values );
1126 if( frame_width != window->u.frame.width ||
1127 frame_height != window->u.frame.height ) {
1128 values[ 0 ] = frame_width - frame_l( window, FALSE ) -
1129 frame_r( window, FALSE );
1130 values[ 1 ] = frame_height - frame_t( window, FALSE ) -
1131 frame_b( window, FALSE );
1132 xcb_configure_window( c, ev->window, XCB_CONFIG_WINDOW_WIDTH |
1133 XCB_CONFIG_WINDOW_HEIGHT, values );
1135 } else
1136 /* Send a synthetic ConfigureNotify indicating the client's
1137 configuration in the root co-ordinate space (ICCCM 2.0,
1138 section 4.1.5). */
1139 synthetic_configure_notify( window );
1142 const event_handler frame_handlers[] = {
1143 NULL, /* Error */
1144 NULL, /* Reply */
1145 NULL, /* KeyPress */
1146 NULL, /* KeyRelease */
1147 (event_handler) frame_button_press,
1148 (event_handler) frame_button_release,
1149 (event_handler) frame_motion_notify,
1150 (event_handler) frame_enter_notify,
1151 NULL, /* LeaveNotify */
1152 NULL, /* FocusIn */
1153 NULL, /* FocusOut */
1154 NULL, /* KeymapNotify */
1155 (event_handler) generic_expose,
1156 NULL, /* GraphicsExpose */
1157 NULL, /* NoExposure */
1158 NULL, /* VisibilityNotify */
1159 NULL, /* CreateNotify */
1160 (event_handler) frame_destroy_notify,
1161 NULL, /* UnmapNotify */
1162 NULL, /* MapNotify */
1163 (event_handler) frame_map_request,
1164 NULL, /* ReparentNotify */
1165 (event_handler) frame_configure_notify,
1166 (event_handler) frame_configure_request,
1167 NULL, /* GravityNotify */
1168 NULL, /* ResizeRequest */
1169 NULL, /* CirculateNotify */
1170 NULL, /* CirculateRequest */
1171 NULL, /* PropertyNotify */
1172 NULL, /* SelectionClear */
1173 NULL, /* SelectionRequest */
1174 NULL, /* SelectionNotify */
1175 NULL, /* ColormapNotify */
1176 NULL, /* ClientMessage */
1177 NULL, /* MappingNotify */
1178 NULL, /* (synthetic) */
1179 NULL /* ShapeNotify */
1182 const event_handler childless_handlers[] = {
1183 NULL, /* Error */
1184 NULL, /* Reply */
1185 NULL, /* KeyPress */
1186 NULL, /* KeyRelease */
1187 NULL, /* ButtonPress */
1188 NULL, /* ButtonRelease */
1189 NULL, /* MotionNotify */
1190 NULL, /* EnterNotify */
1191 NULL, /* LeaveNotify */
1192 NULL, /* FocusIn */
1193 NULL, /* FocusOut */
1194 NULL, /* KeymapNotify */
1195 NULL, /* Expose */
1196 NULL, /* GraphicsExpose */
1197 NULL, /* NoExposure */
1198 NULL, /* VisibilityNotify */
1199 NULL, /* CreateNotify */
1200 (event_handler) frame_destroy_notify,
1201 NULL, /* UnmapNotify */
1202 NULL, /* MapNotify */
1203 (event_handler) withdrawn_map_request,
1204 NULL, /* ReparentNotify */
1205 NULL, /* ConfigureNotify */
1206 (event_handler) withdrawn_configure_request,
1207 NULL, /* GravityNotify */
1208 NULL, /* ResizeRequest */
1209 NULL, /* CirculateNotify */
1210 NULL, /* CirculateRequest */
1211 NULL, /* PropertyNotify */
1212 NULL, /* SelectionClear */
1213 NULL, /* SelectionRequest */
1214 NULL, /* SelectionNotify */
1215 NULL, /* ColormapNotify */
1216 NULL, /* ClientMessage */
1217 NULL, /* MappingNotify */
1218 NULL, /* (synthetic) */
1219 NULL /* ShapeNotify */