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/>.
35 #include "window-table.h"
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
) {
49 assert( window
->type
== WINDOW_FRAME
);
51 if( window
->u
.frame
.decoration
& DEC_BORDER
) {
52 base
= FRAME_BORDER_WIDTH
;
53 border
= FRAME_X_BORDER
;
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
) {
67 assert( window
->type
== WINDOW_FRAME
);
69 if( window
->u
.frame
.decoration
& DEC_BORDER
) {
70 base
= FRAME_BORDER_WIDTH
;
71 border
= FRAME_X_BORDER
;
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,
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
:
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 );
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
);
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
:
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 );
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
);
170 case XCB_GRAVITY_STATIC
:
171 *fy
= cy
+ cborder
- frame_t( frame
, TRUE
);
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
:
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 );
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
);
202 case XCB_GRAVITY_STATIC
:
203 *cx
= fx
- cborder
+ frame_l( frame
, TRUE
);
207 switch( win_gravity
) {
208 case XCB_GRAVITY_NORTH_WEST
:
209 case XCB_GRAVITY_NORTH
:
210 case XCB_GRAVITY_NORTH_EAST
:
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 );
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
);
229 case XCB_GRAVITY_STATIC
:
230 *cy
= fy
- cborder
+ frame_t( frame
, TRUE
);
235 extern void apply_size_constraints( struct gwm_window
*window
, int *width
,
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
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
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
)
288 else if( max_y
< window
->u
.managed
.max_height
)
291 /* The height change is smaller: prefer it if possible. */
292 if( max_y
< window
->u
.managed
.max_height
)
294 else if( min_x
>= window
->u
.managed
.min_width
)
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... */
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
)
317 else if( max_x
< window
->u
.managed
.max_width
)
320 /* The width change is smaller: prefer it if possible. */
321 if( max_x
< window
->u
.managed
.max_width
)
323 else if( min_y
>= window
->u
.managed
.min_height
)
329 struct gwm_window
*focus_frame
;
331 extern void deactivate_focus_frame( void ) {
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
,
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
352 static enum size_which
{
353 SIZE_LESSER
, SIZE_NONE
, SIZE_GREATER
355 static int dx
, dy
, init_x
, init_y
, moved
;
356 static struct gwm_window
*feedback
;
358 static struct h_edge
{
361 } *t_edges
, *b_edges
;
363 static struct v_edge
{
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
;
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
;
404 l_edges
[ 0 ].x
= screens
[ screen
]->width_in_pixels
;
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 */
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
;
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
;
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
;
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
;
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 ) {
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
,
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
)
499 while( i0
< num_b_edges
&& b_edges
[ i0
].y
<= new_t
)
502 for( i
= i0
; i
< num_b_edges
&& b_edges
[ i
].y
<= new_t
+ EDGE_RESIST
;
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
)
520 while( i1
>= 0 && t_edges
[ i1
].y
>= new_b
)
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
)
542 while( i0
< num_r_edges
&& r_edges
[ i0
].x
<= new_l
)
545 for( i
= i0
; i
< num_r_edges
&& r_edges
[ i
].x
<= new_l
+ EDGE_RESIST
;
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
)
563 while( i1
>= 0 && l_edges
[ i1
].x
>= new_r
)
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
) {
587 } else if( size_x
== SIZE_GREATER
) {
590 } else if( x
< window
->u
.frame
.x
+ ( window
->u
.frame
.width
>> 2 ) ) {
591 /* Start sizing left border. */
592 size_x
= SIZE_LESSER
;
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
;
608 if( size_y
== SIZE_LESSER
) {
611 } else if( size_y
== SIZE_GREATER
) {
614 } else if( y
< window
->u
.frame
.y
+ ( window
->u
.frame
.height
>> 2 ) ) {
615 /* Start sizing top border. */
616 size_y
= SIZE_LESSER
;
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
;
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 +
635 XCB_EVENT_MASK_BUTTON_RELEASE
|
636 XCB_EVENT_MASK_POINTER_MOTION_HINT
|
637 XCB_EVENT_MASK_BUTTON_MOTION
);
639 if( apply_edge_resist
) {
642 edge_resist( old_t
, old_b
, old_l
, old_r
,
643 new_t
, new_b
, new_l
, new_r
, &ex
, &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
,
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 ];
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
);
702 window_size( feedback
, &disp_width
, &disp_height
);
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
,
732 xcb_map_window( c
, feedback
->w
);
736 ( values
[ 0 ] - window
->u
.frame
.child
->u
.managed
.base_width
) /
737 window
->u
.frame
.child
->u
.managed
.width_inc
;
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
,
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
)
759 if( ev
->detail
== 2 ) {
760 union callback_param cp
;
763 action_window_menu( window
, (xcb_generic_event_t
*) ev
, cp
);
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
;
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
);
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
);
792 static void frame_motion_notify( struct gwm_window
*window
,
793 xcb_motion_notify_event_t
*ev
) {
795 uint32_t values
[ 2 ];
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
)
807 /* Ignore single pixel movements. */
808 if( abs( ev
->root_x
- init_x
) > 1 || abs( ev
->root_y
- init_y
) > 1 ) {
811 build_edges( window
->screen
);
816 switch( window_op
) {
817 int old_t
, old_b
, old_l
, old_r
, new_t
, new_b
, new_l
, new_r
;
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
) ) {
832 edge_resist( old_t
, old_b
, old_l
, old_r
,
833 new_t
, new_b
, new_l
, new_r
, &ex
, &ey
);
841 if( new_t
!= old_t
|| new_l
!= old_l
) {
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. */
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,
867 static void frame_button_release( struct gwm_window
*window
,
868 xcb_button_release_event_t
*ev
) {
870 if( !final_release( ev
) || !pointer_demux
)
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
,
884 xcb_destroy_window( c
, feedback
->w
);
885 forget_window( feedback
);
889 pointer_demux
= XCB_NONE
;
892 static void frame_enter_notify( struct gwm_window
*window
,
893 xcb_enter_notify_event_t
*ev
) {
896 if( pointer_demux
|| focus_frame
== window
)
897 /* We have the focus already -- probably an inferior change or
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
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
;
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
,
957 static void frame_destroy_notify( struct gwm_window
*window
,
958 xcb_destroy_notify_event_t
*ev
) {
960 if( window
->w
!= pointer_demux
)
967 xcb_destroy_window( c
, feedback
->w
);
968 forget_window( feedback
);
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
) {
1010 if( ev
->window
!= window
->w
)
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 */
1026 values
[ 2 ] = window
->u
.frame
.width
>> 2;
1031 values
[ 0 ] = window
->u
.frame
.width
>> 2;
1032 values
[ 2 ] = window
->u
.frame
.width
- ( values
[ 0 ] << 1 );
1038 values
[ 2 ] = window
->u
.frame
.width
>> 2;
1039 values
[ 0 ] = window
->u
.frame
.width
- values
[ 2 ];
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 ];
1052 values
[ 3 ] = window
->u
.frame
.height
>> 2;
1058 values
[ 1 ] = window
->u
.frame
.height
>> 2;
1059 values
[ 3 ] = window
->u
.frame
.height
- ( values
[ 1 ] << 1 );
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
] );
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
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
),
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
);
1136 /* Send a synthetic ConfigureNotify indicating the client's
1137 configuration in the root co-ordinate space (ICCCM 2.0,
1139 synthetic_configure_notify( window
);
1142 const event_handler frame_handlers
[] = {
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 */
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
[] = {
1185 NULL
, /* KeyPress */
1186 NULL
, /* KeyRelease */
1187 NULL
, /* ButtonPress */
1188 NULL
, /* ButtonRelease */
1189 NULL
, /* MotionNotify */
1190 NULL
, /* EnterNotify */
1191 NULL
, /* LeaveNotify */
1193 NULL
, /* FocusOut */
1194 NULL
, /* KeymapNotify */
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 */