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/>.
44 #include <xcb/shape.h>
47 #include <xcb/xcbext.h>
52 #include "decorate-core.h"
58 #include "window-table.h"
61 xcb_timestamp_t latest_timestamp
;
63 xcb_atom_t atoms
[ NUM_ATOMS
];
65 static const char *atom_names
[ NUM_ATOMS
] = {
72 "WM_COLORMAP_WINDOWS",
79 xcb_atom_t prop_atoms
[ NUM_PROPS
];
80 xcb_atom_t prop_types
[ NUM_PROPS
];
82 static const xcb_extension_t
*extensions
[ EXTENSIONS_SIZE
] = {
88 int have_extension
[ EXTENSIONS_SIZE
];
89 uint8_t extension_event
[ EXTENSIONS_SIZE
],
90 extension_error
[ EXTENSIONS_SIZE
];
93 xcb_screen_t
**screens
;
94 struct gwm_screen
*gwm_screens
;
96 xcb_cursor_t cursors
[ NUM_CURSORS
];
98 struct gwm_window
*fake_window
, *focus_frame
;
101 static int flag_replace
, flag_force
;
103 static int flag_debug
;
106 static volatile int signal_caught
;
108 static void ( *update_window
)( struct gwm_window
*window
);
110 static FORMAT( printf
, 1, 2 ) void warning( char *format
, ... ) {
114 va_start( val
, format
);
116 fprintf( stderr
, "%s: warning: ", argv0
);
117 vfprintf( stderr
, format
, val
);
118 putc( '\n', stderr
);
123 static FORMAT( printf
, 1, 2 ) NORETURN
void fatal( char *format
, ... ) {
127 va_start( val
, format
);
129 fprintf( stderr
, "%s: ", argv0
);
130 vfprintf( stderr
, format
, val
);
131 putc( '\n', stderr
);
135 exit( 1 ); /* don't attempt an orderly shutdown */
138 extern MALLOC
void *xmalloc( size_t size
) {
142 /* It would be nice to clean up gracefully on allocation errors.
143 Unfortunately, doing so would require transmitting many requests
144 to the X server, which would require further allocations which
145 are likely to fail. Simply aborting with an error message is
146 therefore probably about as effective, and much simpler. */
148 if( !( p
= malloc( size
) ) ) {
157 extern void *xrealloc( void *p
, size_t size
) {
159 if( !( p
= realloc( p
, size
) ) ) {
168 extern MALLOC
void *xcalloc( size_t number
, size_t size
) {
172 if( !( p
= calloc( number
, size
) ) ) {
181 static void catch_signal( int n
) {
187 sa
.sa_handler
= SIG_DFL
;
188 sigemptyset( &sa
.sa_mask
);
190 sigaction( SIGHUP
, &sa
, NULL
);
191 sigaction( SIGINT
, &sa
, NULL
);
192 sigaction( SIGTERM
, &sa
, NULL
);
195 static void alarm_signal( int n
) {
200 static void child_signal( int n
) {
202 while( waitpid( -1, 0, WNOHANG
) > 0 )
206 static struct async_callback
{
207 unsigned int sequence
;
208 void ( *callback
)( unsigned int sequence
, void *reply
,
209 xcb_generic_error_t
*error
, union callback_param p
);
210 union callback_param p
;
211 struct async_callback
*next
;
212 } *queue_head
, *queue_tail
;
214 static void check_async_callbacks( void ) {
217 xcb_generic_error_t
*error
;
219 while( queue_head
&& xcb_poll_for_reply( c
, queue_head
->sequence
,
221 struct async_callback
*entry
= queue_head
;
223 if( !( queue_head
= entry
->next
) )
226 entry
->callback( entry
->sequence
, reply
, error
, entry
->p
);
232 extern void sync_with_callback( unsigned int sequence
) {
234 while( queue_head
&& (int) sequence
- (int) queue_head
->sequence
>= 0 ) {
235 struct async_callback
*entry
= queue_head
;
236 xcb_generic_error_t
*error
;
237 void *reply
= xcb_wait_for_reply( c
, entry
->sequence
, &error
);
239 if( !( queue_head
= entry
->next
) )
242 entry
->callback( entry
->sequence
, reply
, error
, entry
->p
);
249 static void show_window( char *label
, xcb_window_t w
) {
251 /* We don't want to use lookup_window here, since mere debugging
252 output shouldn't trigger the side effect of finishing incomplete
254 struct gwm_window
*window
= table_lookup( &windows
, w
);
256 printf( " %s %08X (", label
, w
);
259 fputs( "None", stdout
);
261 struct gwm_window
*client
;
262 switch( window
->type
) {
264 printf( "root, screen %d", window
->screen
);
273 fputs( "frame, ", stdout
);
274 client
= window
->u
.frame
.child
;
278 fputs( "button, ", stdout
);
279 client
= window
->u
.button
.frame
->u
.frame
.child
;
283 fputs( "fake", stdout
);
287 case WINDOW_FEEDBACK
:
288 printf( "feedback, screen %d", window
->screen
);
292 case WINDOW_INCOMPLETE
:
293 fputs( "incomplete", stdout
);
299 fputs( client
->u
.managed
.name
? client
->u
.managed
.name
: "unnamed",
302 fputs( "unknown", stdout
);
308 static void show_atom( char *label
, xcb_atom_t atom
) {
310 static const char *predefined_names
[ 69 ] = {
311 "None", "PRIMARY", "SECONDARY", "ARC", "ATOM", "BITMAP", "CARDINAL",
312 "COLORMAP", "CURSOR", "CUT_BUFFER0", "CUT_BUFFER1", "CUT_BUFFER2",
313 "CUT_BUFFER3", "CUT_BUFFER4", "CUT_BUFFER5", "CUT_BUFFER6",
314 "CUT_BUFFER7", "DRAWABLE", "FONT", "INTEGER", "PIXMAP", "POINT",
315 "RECTANGLE", "RESOURCE_MANAGER", "RGB_COLOR_MAP", "RGB_BEST_MAP",
316 "RGB_BLUE_MAP", "RGB_DEFAULT_MAP", "RGB_GRAY_MAP", "RGB_GREEN_MAP",
317 "RGB_RED_MAP", "STRING", "VISUALID", "WINDOW", "WM_COMMAND",
318 "WM_HINTS", "WM_CLIENT_MACHINE", "WM_ICON_NAME", "WM_ICON_SIZE",
319 "WM_NAME", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_ZOOM_HINTS",
320 "MIN_SPACE", "NORM_SPACE", "MAX_SPACE", "END_SPACE", "SUPERSCRIPT_X",
321 "SUPERSCRIPT_Y", "SUBSCRIPT_X", "SUBSCRIPT_Y", "UNDERLINE_POSITION",
322 "UNDERLINE_THICKNESS", "STRIKEOUT_ASCENT", "STRIKEOUT_DESCENT",
323 "ITALIC_ANGLE", "X_HEIGHT", "QUAD_WIDTH", "WEIGHT", "POINT_SIZE",
324 "RESOLUTION", "COPYRIGHT", "NOTICE", "FONT_NAME", "FAMILY_NAME",
325 "FULL_NAME", "CAP_HEIGHT", "WM_CLASS", "WM_TRANSIENT_FOR"
330 name
= predefined_names
[ atom
];
334 for( i
= 0; i
< NUM_ATOMS
; i
++ )
335 if( atoms
[ i
] == atom
) {
336 name
= atom_names
[ i
];
344 printf( " %s %d (%s)\n", label
, atom
, name
);
348 extern void show_error( xcb_generic_error_t
*error
) {
350 xcb_value_error_t
*verror
= (xcb_value_error_t
*) error
;
352 static const char *type_strings
[ XCB_IMPLEMENTATION
+ 1 ] = {
353 "Unknown", "Request", "Value", "Window", "Pixmap", "Atom", "Cursor",
354 "Font", "Match", "Drawable", "Access", "Alloc", "Colormap", "GContext",
355 "IDChoice", "Name", "Length", "Implementation"
357 const char *type
, *bad_value_str
;
358 char num
[ 32 ], bad_num
[ 32 ];
360 if( error
->error_code
<= XCB_IMPLEMENTATION
)
361 type
= type_strings
[ error
->error_code
];
363 sprintf( num
, "%d", error
->error_code
);
367 switch( error
->error_code
) {
378 sprintf( bad_num
, ", val %X", verror
->bad_value
);
379 bad_value_str
= bad_num
;
387 warning( "unexpected error (type %s, sequence %d; opcode %d %d%s)", type
,
388 error
->sequence
, verror
->major_opcode
, verror
->minor_opcode
,
391 warning( "unexpected error (type %d, sequence %d; opcode %d %d)",
392 error
->error_code
, error
->sequence
,
393 verror
->major_opcode
, verror
->minor_opcode
);
398 static void show_event( xcb_generic_event_t
*generic
) {
400 static const char *event_names
[ XCB_MAPPING_NOTIFY
+ 1 ] = {
401 "Error", "Reply", "KeyPress", "KeyRelease", "ButtonPress",
402 "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify",
403 "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExposure",
404 "NoExposure", "VisibilityNotify", "CreateNotify", "DestroyNotify",
405 "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify",
406 "ConfigureNotify", "ConfigureRequest", "GravityNotify",
407 "ResizeRequest", "CirculateNotify", "CirculateRequest",
408 "PropertyNotify", "SelectionClear", "SelectionRequest",
409 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify"
412 xcb_key_press_event_t kbm
; /* KeyPress, KeyRelease, ButtonPress,
413 ButtonRelease, MotionNotify */
414 xcb_enter_notify_event_t el
; /* EnterNotify, LeaveNotify */
415 xcb_focus_in_event_t f
; /* FocusIn, FocusOut */
416 xcb_keymap_notify_event_t km
; /* KeymapNotify */
417 xcb_expose_event_t e
; /* Expose */
418 xcb_graphics_exposure_event_t g
; /* GraphicsExposure */
419 xcb_no_exposure_event_t n
; /* NoExposure */
420 xcb_visibility_notify_event_t v
; /* VisibilityNotify */
421 xcb_create_notify_event_t c
; /* CreateNotify */
422 xcb_destroy_notify_event_t d
; /* DestroyNotify */
423 xcb_unmap_notify_event_t u
; /* UnmapNotify */
424 xcb_map_notify_event_t m
; /* MapNotify */
425 xcb_map_request_event_t mr
; /* MapRequest */
426 xcb_reparent_notify_event_t r
; /* ReparentNotify */
427 xcb_configure_notify_event_t con
; /* ConfigureNotify */
428 xcb_configure_request_event_t cr
; /* ConfigureRequest */
429 xcb_gravity_notify_event_t gn
; /* GravityNotify */
430 xcb_resize_request_event_t rr
; /* ResizeRequest */
431 xcb_circulate_notify_event_t cir
; /* CirculateNotify */
432 xcb_circulate_request_event_t cirr
; /* CirculateRequest */
433 xcb_property_notify_event_t p
; /* PropertyNotify */
434 xcb_selection_clear_event_t sc
; /* SelectionClear */
435 xcb_selection_request_event_t sr
; /* SelectionRequest */
436 xcb_selection_notify_event_t sn
; /* SelectionNotify */
437 xcb_colormap_notify_event_t cm
; /* ColormapNotify */
438 xcb_client_message_event_t cli
; /* ClientMessage */
439 xcb_mapping_notify_event_t map
; /* MappingNotify */
440 } *ev
= (union event
*) generic
;
441 int type
= generic
->response_type
& 0x7F;
444 if( !( generic
->response_type
& ~SEND_EVENT_MASK
) ) {
445 show_error( (xcb_generic_error_t
*) generic
);
450 printf( "Event %d (%s%s)\n", generic
->response_type
,
451 type
<= XCB_MAPPING_NOTIFY
? event_names
[ type
] : "Unknown",
452 generic
->response_type
& SEND_EVENT_MASK
? ", synthetic" : "" );
454 if( type
< XCB_KEY_PRESS
|| type
> XCB_MAPPING_NOTIFY
)
457 if( type
!= XCB_KEYMAP_NOTIFY
)
458 printf( " Sequence %X\n", generic
->full_sequence
);
462 case XCB_KEY_RELEASE
:
463 case XCB_BUTTON_PRESS
:
464 case XCB_BUTTON_RELEASE
:
465 case XCB_MOTION_NOTIFY
:
466 show_window( "Root", ev
->kbm
.root
);
467 show_window( "Event", ev
->kbm
.event
);
468 show_window( "Child", ev
->kbm
.child
);
469 printf( " Same-screen %s\n"
471 " Event x/y %d, %d\n"
475 ev
->kbm
.same_screen
? "Yes" : "No",
476 ev
->kbm
.root_x
, ev
->kbm
.root_y
,
477 ev
->kbm
.event_x
, ev
->kbm
.event_y
,
478 ev
->kbm
.detail
, ev
->kbm
.state
, ev
->kbm
.time
);
481 case XCB_ENTER_NOTIFY
:
482 case XCB_LEAVE_NOTIFY
:
483 show_window( "Root", ev
->el
.root
);
484 show_window( "Event", ev
->el
.event
);
485 show_window( "Child", ev
->el
.child
);
486 printf( " Same-screen %s\n"
488 " Event x/y %d, %d\n"
494 ev
->el
.same_screen_focus
& 2 ? "Yes" : "No",
495 ev
->el
.root_x
, ev
->el
.root_y
,
496 ev
->el
.event_x
, ev
->el
.event_y
,
497 ev
->el
.mode
, ev
->el
.detail
, ev
->el
.same_screen_focus
& 1,
498 ev
->el
.state
, ev
->el
.time
);
503 show_window( "Event", ev
->f
.event
);
506 ev
->f
.mode
, ev
->f
.detail
);
508 case XCB_KEYMAP_NOTIFY
:
509 for( i
= 0; i
< 0xF8; i
++ )
510 if( ev
->km
.keys
[ i
>> 3 ] & ( 1 << ( i
& 7 ) ) )
511 printf( " key %d down\n", i
);
516 show_window( "Window", ev
->e
.window
);
517 printf( " x/y %d, %d\n"
518 " width/height %d x %d\n"
520 ev
->e
.x
, ev
->e
.y
, ev
->e
.width
, ev
->e
.height
, ev
->e
.count
);
523 case XCB_GRAPHICS_EXPOSURE
:
524 show_window( "Drawable", ev
->g
.drawable
);
525 printf( " x/y %d, %d\n"
526 " width/height %d x %d\n"
529 ev
->g
.x
, ev
->g
.y
, ev
->g
.width
, ev
->g
.height
, ev
->g
.count
,
530 ev
->g
.major_opcode
, ev
->g
.minor_opcode
);
533 case XCB_NO_EXPOSURE
:
534 show_window( "Drawable", ev
->n
.drawable
);
535 printf( " Opcode %d, %d\n",
536 ev
->n
.major_opcode
, ev
->n
.minor_opcode
);
539 case XCB_VISIBILITY_NOTIFY
:
540 show_window( "Window", ev
->v
.window
);
541 printf( " State %d\n", ev
->v
.state
);
544 case XCB_CREATE_NOTIFY
:
545 show_window( "Parent", ev
->c
.parent
);
546 show_window( "Window", ev
->c
.window
);
547 printf( " x/y %d, %d\n"
548 " width/height %d x %d\n"
550 " Override redirect %s\n",
552 ev
->c
.width
, ev
->c
.height
,
554 ev
->c
.override_redirect
? "Yes" : "No" );
557 case XCB_DESTROY_NOTIFY
:
558 show_window( "Event", ev
->d
.event
);
559 show_window( "Window", ev
->d
.window
);
562 case XCB_UNMAP_NOTIFY
:
563 show_window( "Event", ev
->u
.event
);
564 show_window( "Window", ev
->u
.window
);
565 printf( " From configure: %s\n",
566 ev
->u
.from_configure
? "Yes" : "No" );
570 show_window( "Event", ev
->m
.event
);
571 show_window( "Window", ev
->m
.window
);
572 printf( " Override redirect: %s\n",
573 ev
->m
.override_redirect
? "Yes" : "No" );
576 case XCB_MAP_REQUEST
:
577 show_window( "Parent", ev
->mr
.parent
);
578 show_window( "Window", ev
->mr
.window
);
581 case XCB_REPARENT_NOTIFY
:
582 show_window( "Event", ev
->r
.event
);
583 show_window( "Window", ev
->r
.window
);
584 show_window( "Parent", ev
->r
.parent
);
585 printf( " x/y %d, %d\n"
586 " Override redirect %s\n",
588 ev
->r
.override_redirect
? "Yes" : "No" );
591 case XCB_CONFIGURE_NOTIFY
:
592 show_window( "Event", ev
->con
.event
);
593 show_window( "Window", ev
->con
.window
);
594 printf( " x/y %d, %d\n"
595 " width/height %d x %d\n"
596 " Border width %d\n",
597 ev
->con
.x
, ev
->con
.y
,
598 ev
->con
.width
, ev
->con
.height
,
599 ev
->con
.border_width
);
600 show_window( "Above sibling", ev
->con
.above_sibling
);
601 printf( " Override redirect %s\n",
602 ev
->con
.override_redirect
? "Yes" : "No" );
605 case XCB_CONFIGURE_REQUEST
:
606 show_window( "Parent", ev
->cr
.parent
);
607 show_window( "Window", ev
->cr
.window
);
608 printf( " x/y %d, %d\n"
609 " width/height %d x %d\n"
610 " Border width %d\n",
612 ev
->cr
.width
, ev
->cr
.height
,
613 ev
->cr
.border_width
);
614 show_window( "Sibling", ev
->cr
.sibling
);
615 printf( " Stack mode %d\n"
616 " Value mask %04X\n",
617 ev
->cr
.stack_mode
, ev
->cr
.value_mask
);
620 case XCB_GRAVITY_NOTIFY
:
621 show_window( "Event", ev
->gn
.event
);
622 show_window( "Window", ev
->gn
.window
);
623 printf( " x/y %d, %d\n",
624 ev
->gn
.x
, ev
->gn
.y
);
627 case XCB_RESIZE_REQUEST
:
628 show_window( "Window", ev
->rr
.window
);
629 printf( " width/height %d x %d\n",
630 ev
->rr
.width
, ev
->rr
.height
);
633 case XCB_CIRCULATE_NOTIFY
:
634 show_window( "Event", ev
->cir
.event
);
635 show_window( "Window", ev
->cir
.window
);
636 printf( " Place %d\n", ev
->cir
.place
);
639 case XCB_CIRCULATE_REQUEST
:
640 show_window( "Parent", ev
->cirr
.event
);
641 show_window( "Window", ev
->cirr
.window
);
642 printf( " Place %d\n", ev
->cirr
.place
);
645 case XCB_PROPERTY_NOTIFY
:
646 show_window( "Window", ev
->p
.window
);
647 show_atom( "Atom", ev
->p
.atom
);
648 printf( " State %d\n"
650 ev
->p
.state
, ev
->p
.time
);
653 case XCB_SELECTION_CLEAR
:
654 show_window( "Owner", ev
->sc
.owner
);
655 show_atom( "Selection", ev
->sc
.selection
);
656 printf( " Time %d\n", ev
->sc
.time
);
659 case XCB_SELECTION_REQUEST
:
660 show_window( "Owner", ev
->sr
.owner
);
661 show_atom( "Selection", ev
->sr
.selection
);
662 show_atom( "Target", ev
->sr
.target
);
663 show_atom( "Property", ev
->sr
.property
);
664 show_window( "Requestor", ev
->sr
.requestor
);
665 printf( " Time %d\n", ev
->sr
.time
);
668 case XCB_SELECTION_NOTIFY
:
669 show_window( "Requestor", ev
->sn
.requestor
);
670 show_atom( "Selection", ev
->sn
.selection
);
671 show_atom( "Target", ev
->sn
.target
);
672 show_atom( "Property", ev
->sn
.property
);
673 printf( " Time %d\n", ev
->sn
.time
);
676 case XCB_COLORMAP_NOTIFY
:
677 show_window( "Window", ev
->cm
.window
);
678 printf( " Colormap %d\n"
682 ev
->cm
._new
? "Yes" : "No",
686 case XCB_CLIENT_MESSAGE
:
687 show_window( "Window", ev
->cli
.window
);
688 show_atom( "Type", ev
->cli
.type
);
689 printf( " Format %d\n", ev
->cli
.format
);
691 switch( ev
->cli
.format
) {
693 for( i
= 0; i
< 20; i
++ )
694 printf( " %2d %02X\n", i
, ev
->cli
.data
.data8
[ i
] );
698 for( i
= 0; i
< 10; i
++ )
699 printf( " %d %04X\n", i
, ev
->cli
.data
.data16
[ i
] );
703 for( i
= 0; i
< 5; i
++ )
704 printf( " %d %08X\n", i
, ev
->cli
.data
.data32
[ i
] );
710 case XCB_MAPPING_NOTIFY
:
711 printf( " Request %d\n", ev
->map
.request
);
712 if( ev
->map
.request
== XCB_MAPPING_KEYBOARD
)
713 printf( " First keycode %d\n"
715 ev
->map
.first_keycode
, ev
->map
.count
);
721 extern xcb_generic_event_t
*wait_for_event( void ) {
723 xcb_generic_event_t
*ev
;
724 sigset_t sigs
, oldsigs
;
725 int fd
= xcb_get_file_descriptor( c
);
733 if( xcb_connection_has_error( c
) )
736 ev
= xcb_poll_for_event( c
);
739 if( flag_debug
&& ev
)
743 check_async_callbacks(); /* must poll for events first, since
744 polling replies won't dequeue them */
748 if( update_windows
.used
) {
749 struct gwm_window
*update
= update_windows
.values
[ 0 ];
751 update_window( update
);
752 window_update_done( update
);
759 sigemptyset( &sigs
);
760 sigaddset( &sigs
, SIGHUP
);
761 sigaddset( &sigs
, SIGINT
);
762 sigaddset( &sigs
, SIGTERM
);
764 sigprocmask( SIG_BLOCK
, &sigs
, &oldsigs
);
766 if( signal_caught
) {
767 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
776 if( ppoll( &pfd
, 1, NULL
, &oldsigs
) < 0 && errno
!= EINTR
)
782 if( pselect( fd
+ 1, &fds
, NULL
, NULL
, NULL
, &oldsigs
) < 0 &&
787 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
791 extern void handle_async_reply( unsigned int sequence
,
792 void ( *callback
)( unsigned int sequence
,
794 xcb_generic_error_t
*error
,
795 union callback_param p
),
796 union callback_param p
) {
798 struct async_callback
*entry
= xmalloc( sizeof *entry
);
800 entry
->sequence
= sequence
;
801 entry
->callback
= callback
;
806 queue_tail
= queue_tail
->next
= entry
;
808 queue_head
= queue_tail
= entry
;
811 static void error_callback( unsigned int sequence
, void *reply
,
812 xcb_generic_error_t
*error
,
813 union callback_param p
) {
815 unsigned ignore
= p
.l
;
821 return; /* no error */
823 if( !( ignore
& ( 1 << ( error
->error_code
- 1 ) ) ) )
824 show_error( error
); /* an error we didn't expect */
829 extern void handle_error_reply( xcb_void_cookie_t cookie
, unsigned ignore
) {
831 union callback_param p
;
835 handle_async_reply( cookie
.sequence
, error_callback
, p
);
838 static void install_colormap( int screen
, xcb_colormap_t cmap
,
839 xcb_timestamp_t t
) {
841 if( (int) t
- (int) gwm_screens
[ screen
].cmap_time
> 0 ) {
842 gwm_screens
[ screen
].cmap_time
= t
;
844 if( gwm_screens
[ screen
].cmap
!= cmap
)
845 xcb_install_colormap( c
, gwm_screens
[ screen
].cmap
= cmap
);
849 struct cmap_callback
{
854 static void handle_get_window_attributes( unsigned int sequence
, void *reply
,
855 xcb_generic_error_t
*error
,
856 union callback_param cp
) {
858 struct cmap_callback
*cc
= cp
.p
;
861 /* Ignore Window errors, since we were given the ID by a client
862 and we don't trust them to get it right. */
863 if( error
->error_code
!= XCB_WINDOW
)
870 xcb_get_window_attributes_reply_t
*r
= reply
;
872 install_colormap( cc
->screen
, r
->colormap
? r
->colormap
:
873 screens
[ cc
->screen
]->default_colormap
, cc
->t
);
881 /* Install a window's colormap on a screen, or the default colormap if
883 extern void install_window_colormap( int screen
, struct gwm_window
*window
,
884 xcb_timestamp_t t
) {
886 assert( !window
|| window
->type
== WINDOW_MANAGED
);
888 if( window
&& window
->u
.managed
.cmap_window
) {
889 union callback_param cp
;
890 struct cmap_callback
*cc
= xmalloc( sizeof *cc
);
895 handle_async_reply( xcb_get_window_attributes(
896 c
, window
->u
.managed
.cmap_window
).sequence
,
897 handle_get_window_attributes
, cp
);
899 install_colormap( screen
, window
&& window
->u
.managed
.cmap
?
900 window
->u
.managed
.cmap
:
901 screens
[ screen
]->default_colormap
, t
);
904 /* Return TRUE iff an event is the only button pressed (and so would
905 initiate a passive grab). */
906 extern CONST
int initial_press( xcb_button_press_event_t
*ev
) {
908 return !( ev
->state
& 0x1F00 );
911 /* Return TRUE iff an event is the release of the last button (and so would
912 terminate a passive grab). */
913 extern CONST
int final_release( xcb_button_release_event_t
*ev
) {
915 return ( ev
->state
& 0x1F00 ) == ( 0x80 << ev
->detail
);
918 extern void translate_child_to_frame( int *fx
, int *fy
, int *fwidth
,
919 int *fheight
, int cx
, int cy
,
920 int cwidth
, int cheight
,
921 int cborder
, int win_gravity
) {
923 *fwidth
= cwidth
+ FRAME_BORDER_WIDTH
* 2;
924 *fheight
= cheight
+ FRAME_BORDER_WIDTH
+ FRAME_TITLE_HEIGHT
;
926 switch( win_gravity
) {
927 case XCB_GRAVITY_NORTH_WEST
:
928 case XCB_GRAVITY_WEST
:
929 case XCB_GRAVITY_SOUTH_WEST
:
934 case XCB_GRAVITY_NORTH
:
935 case XCB_GRAVITY_CENTER
:
936 case XCB_GRAVITY_SOUTH
:
937 case XCB_GRAVITY_STATIC
:
938 *fx
= cx
+ cborder
- FRAME_BORDER_WIDTH
- FRAME_X_BORDER
;
941 case XCB_GRAVITY_NORTH_EAST
:
942 case XCB_GRAVITY_EAST
:
943 case XCB_GRAVITY_SOUTH_EAST
:
944 *fx
= cx
+ ( ( cborder
- FRAME_BORDER_WIDTH
- FRAME_X_BORDER
) << 1 );
948 switch( win_gravity
) {
949 case XCB_GRAVITY_NORTH_WEST
:
950 case XCB_GRAVITY_NORTH
:
951 case XCB_GRAVITY_NORTH_EAST
:
956 case XCB_GRAVITY_WEST
:
957 case XCB_GRAVITY_CENTER
:
958 case XCB_GRAVITY_EAST
:
959 *fy
= cy
+ cborder
- ( ( FRAME_BORDER_WIDTH
+
960 FRAME_TITLE_HEIGHT
) >> 1 ) - FRAME_X_BORDER
;
963 case XCB_GRAVITY_SOUTH_WEST
:
964 case XCB_GRAVITY_SOUTH
:
965 case XCB_GRAVITY_SOUTH_EAST
:
966 *fy
= cy
+ ( cborder
<< 1 ) - FRAME_TITLE_HEIGHT
- FRAME_BORDER_WIDTH
-
967 ( FRAME_X_BORDER
<< 1 );
970 case XCB_GRAVITY_STATIC
:
971 *fy
= cy
+ cborder
- FRAME_TITLE_HEIGHT
- FRAME_X_BORDER
;
976 extern void translate_frame_to_child( int *cx
, int *cy
, int fx
, int fy
,
977 int cborder
, int win_gravity
) {
979 switch( win_gravity
) {
980 case XCB_GRAVITY_NORTH_WEST
:
981 case XCB_GRAVITY_WEST
:
982 case XCB_GRAVITY_SOUTH_WEST
:
987 case XCB_GRAVITY_NORTH
:
988 case XCB_GRAVITY_CENTER
:
989 case XCB_GRAVITY_SOUTH
:
990 case XCB_GRAVITY_STATIC
:
991 *cx
= fx
- cborder
+ FRAME_BORDER_WIDTH
+ FRAME_X_BORDER
;
994 case XCB_GRAVITY_NORTH_EAST
:
995 case XCB_GRAVITY_EAST
:
996 case XCB_GRAVITY_SOUTH_EAST
:
997 *cx
= fx
+ ( ( FRAME_BORDER_WIDTH
+ FRAME_X_BORDER
- cborder
) << 1 );
1001 switch( win_gravity
) {
1002 case XCB_GRAVITY_NORTH_WEST
:
1003 case XCB_GRAVITY_NORTH
:
1004 case XCB_GRAVITY_NORTH_EAST
:
1009 case XCB_GRAVITY_WEST
:
1010 case XCB_GRAVITY_CENTER
:
1011 case XCB_GRAVITY_EAST
:
1012 *cy
= fy
- cborder
+ ( ( FRAME_BORDER_WIDTH
+
1013 FRAME_TITLE_HEIGHT
) >> 1 ) + FRAME_X_BORDER
;
1016 case XCB_GRAVITY_SOUTH_WEST
:
1017 case XCB_GRAVITY_SOUTH
:
1018 case XCB_GRAVITY_SOUTH_EAST
:
1019 *cy
= fy
- ( cborder
<< 1 ) + FRAME_TITLE_HEIGHT
+ FRAME_BORDER_WIDTH
+
1020 ( FRAME_X_BORDER
<< 1 );
1023 case XCB_GRAVITY_STATIC
:
1024 *cy
= fy
- cborder
+ FRAME_TITLE_HEIGHT
+ FRAME_X_BORDER
;
1029 extern void apply_size_constraints( struct gwm_window
*window
, int *width
,
1032 int eff_base_width
= window
->u
.managed
.base_width
?
1033 window
->u
.managed
.base_width
: window
->u
.managed
.min_width
,
1034 eff_base_height
= window
->u
.managed
.base_height
?
1035 window
->u
.managed
.base_height
: window
->u
.managed
.min_height
;
1037 /* Apply the minimum and maximum constraints. These are already known
1038 to be compatible. */
1039 if( *width
< window
->u
.managed
.min_width
)
1040 *width
= window
->u
.managed
.min_width
;
1042 if( *height
< window
->u
.managed
.min_height
)
1043 *height
= window
->u
.managed
.min_height
;
1045 if( *width
> window
->u
.managed
.max_width
)
1046 *width
= window
->u
.managed
.max_width
;
1048 if( *height
> window
->u
.managed
.max_height
)
1049 *height
= window
->u
.managed
.max_height
;
1051 /* Now round down each dimension to an integer multiple of increments.
1052 Rounding down cannot violate the maximum constraint, and since
1053 eff_base_* >= min_*, it will not reduce below the minimum constraint. */
1054 *width
-= ( *width
- eff_base_width
) % window
->u
.managed
.width_inc
;
1055 *height
-= ( *height
- eff_base_height
) % window
->u
.managed
.height_inc
;
1057 if( window
->u
.managed
.min_aspect_x
* *height
>
1058 window
->u
.managed
.min_aspect_y
* *width
) {
1059 /* Minimum aspect ratio violated. Attempt to either increase the
1060 width or decrease the height (whichever is a smaller change), but
1061 don't do either if it would go outside the min/max bounds.
1062 Both division operations are safe (min_aspect_y is always
1063 positive, and min_aspect_x must be positive if there is a
1064 violation). Note that an exact solution might not be possible
1065 (e.g. certain cases where the aspect ratio and increments are
1069 min_x
= ( window
->u
.managed
.min_aspect_x
* *height
+
1070 ( window
->u
.managed
.min_aspect_y
- 1 ) ) /
1071 window
->u
.managed
.min_aspect_y
+ window
->u
.managed
.width_inc
- 1;
1072 min_x
-= ( min_x
- eff_base_width
) % window
->u
.managed
.width_inc
;
1074 max_y
= window
->u
.managed
.min_aspect_y
* *width
/
1075 window
->u
.managed
.min_aspect_x
;
1076 max_y
-= ( max_y
- eff_base_height
) % window
->u
.managed
.height_inc
;
1078 if( min_x
- *width
< *height
- max_y
) {
1079 /* The width change is smaller: prefer it if possible. */
1080 if( min_x
>= window
->u
.managed
.min_width
)
1082 else if( max_y
< window
->u
.managed
.max_height
)
1085 /* The height change is smaller: prefer it if possible. */
1086 if( max_y
< window
->u
.managed
.max_height
)
1088 else if( min_x
>= window
->u
.managed
.min_width
)
1093 if( window
->u
.managed
.max_aspect_x
* *height
<
1094 window
->u
.managed
.max_aspect_y
* *width
) {
1095 /* Maximum aspect ratio violated. Much like the case above... */
1098 min_y
= ( window
->u
.managed
.max_aspect_y
* *width
+
1099 ( window
->u
.managed
.max_aspect_x
- 1 ) ) /
1100 window
->u
.managed
.max_aspect_x
+ window
->u
.managed
.height_inc
- 1;
1101 min_y
-= ( min_y
- eff_base_height
) % window
->u
.managed
.height_inc
;
1103 max_x
= window
->u
.managed
.max_aspect_x
* *height
/
1104 window
->u
.managed
.max_aspect_y
;
1105 max_x
-= ( max_x
- eff_base_width
) % window
->u
.managed
.width_inc
;
1107 if( min_y
- *height
< *width
- max_x
) {
1108 /* The height change is smaller: prefer it if possible. */
1109 if( min_y
>= window
->u
.managed
.min_height
)
1111 else if( max_x
< window
->u
.managed
.max_width
)
1114 /* The width change is smaller: prefer it if possible. */
1115 if( max_x
< window
->u
.managed
.max_width
)
1117 else if( min_y
>= window
->u
.managed
.min_height
)
1123 static void stop_listening( struct gwm_window
*window
) {
1127 /* Ignore Window errors on the child, because it might be destroyed
1128 before we are notified. */
1131 handle_error_reply( xcb_change_window_attributes_checked(
1132 c
, window
->w
, XCB_CW_EVENT_MASK
, &value
),
1136 if( have_extension
[ EXT_SHAPE
] )
1137 handle_error_reply( xcb_shape_select_input_checked( c
, window
->w
,
1143 static void set_managed_state( struct gwm_window
*window
, int state
) {
1145 uint32_t values
[ 2 ];
1147 assert( window
->type
== WINDOW_MANAGED
);
1149 /* Place a WM_STATE property on the client window (ICCCM 2.0, section
1151 values
[ 0 ] = window
->u
.managed
.state
= state
;
1152 values
[ 1 ] = XCB_NONE
;
1153 xcb_change_property( c
, XCB_PROP_MODE_REPLACE
, window
->w
,
1154 atoms
[ ATOM_WM_STATE
], atoms
[ ATOM_WM_STATE
],
1158 static void place_window( struct gwm_window
*window
, int *x
, int *y
,
1159 int width
, int height
) {
1161 int sx
= screens
[ window
->screen
]->width_in_pixels
- width
;
1162 int sy
= screens
[ window
->screen
]->height_in_pixels
- height
;
1163 unsigned long long hash
;
1168 hash
= window
->w
* 0x9B4A36D1;
1176 hash
= window
->w
* 0x9B4A36D1;
1182 static void start_managing_window( struct gwm_window
*window
,
1183 xcb_get_window_attributes_reply_t
*attr
,
1184 xcb_get_geometry_reply_t
*geom
,
1186 xcb_shape_query_extents_reply_t
*shape
,
1188 xcb_get_property_reply_t
*props
[],
1192 uint32_t values
[ 4 ];
1193 struct gwm_window
*frame
, *button
;
1194 xcb_window_t w
= window
->w
;
1196 frame
= add_window( xcb_generate_id( c
) );
1197 button
= add_window( xcb_generate_id( c
) );
1198 window
->screen
= lookup_window( geom
->root
)->screen
;
1199 window
->type
= WINDOW_MANAGED
;
1200 window
->u
.managed
.frame
= frame
;
1201 window
->u
.managed
.border_width
= geom
->border_width
;
1202 window
->u
.managed
.cmap
= attr
->colormap
;
1203 window
->u
.managed
.hints
= 0;
1204 window
->u
.managed
.name
= NULL
;
1205 window
->u
.managed
.state
= STATE_WITHDRAWN
;
1206 frame
->screen
= window
->screen
;
1207 frame
->type
= WINDOW_FRAME
;
1208 frame
->u
.frame
.child
= window
;
1209 frame
->u
.frame
.button
= button
;
1210 button
->screen
= window
->screen
;
1211 button
->type
= WINDOW_BUTTON
;
1212 button
->u
.button
.frame
= frame
;
1214 for( i
= 0; i
< NUM_PROPS
; i
++ )
1215 managed_property_change( window
, i
, props
[ i
] );
1217 translate_child_to_frame( &frame
->u
.frame
.x
, &frame
->u
.frame
.y
,
1218 &frame
->u
.frame
.width
, &frame
->u
.frame
.height
,
1219 geom
->x
, geom
->y
, geom
->width
, geom
->height
,
1221 window
->u
.managed
.win_gravity
);
1223 if( map_request
&& !( window
->u
.managed
.hints
& HINT_POSITION
) ) {
1226 place_window( window
, &x
, &y
,
1227 frame
->u
.frame
.width
, frame
->u
.frame
.height
);
1229 frame
->u
.frame
.x
= x
;
1230 frame
->u
.frame
.y
= y
;
1233 /* Don't create frames entirely off-screen; ensure that at least 8 pixels
1234 are within the root. */
1235 if( frame
->u
.frame
.x
> screens
[ frame
->screen
]->width_in_pixels
- 8 &&
1236 frame
->u
.frame
.x
+ frame
->u
.frame
.width
>
1237 screens
[ frame
->screen
]->width_in_pixels
)
1238 frame
->u
.frame
.x
= screens
[ frame
->screen
]->width_in_pixels
- 8;
1239 else if( frame
->u
.frame
.x
< 0 &&
1240 frame
->u
.frame
.x
+ frame
->u
.frame
.width
< 8 )
1241 frame
->u
.frame
.x
= 8 - frame
->u
.frame
.width
;
1243 if( frame
->u
.frame
.y
> screens
[ frame
->screen
]->height_in_pixels
- 8 &&
1244 frame
->u
.frame
.y
+ frame
->u
.frame
.height
>
1245 screens
[ frame
->screen
]->height_in_pixels
)
1246 frame
->u
.frame
.y
= screens
[ frame
->screen
]->height_in_pixels
- 8;
1247 else if( frame
->u
.frame
.y
< 0 &&
1248 frame
->u
.frame
.y
+ frame
->u
.frame
.height
< 8 )
1249 frame
->u
.frame
.y
= 8 - frame
->u
.frame
.height
;
1251 values
[ 0 ] = gwm_screens
[ frame
->screen
].pixels
[
1252 COL_FRAME_INACTIVE
]; /* background pixel */
1253 values
[ 1 ] = gwm_screens
[ frame
->screen
].pixels
[
1254 COL_BORDER
]; /* border pixel */
1255 values
[ 2 ] = XCB_GRAVITY_NORTH_WEST
; /* bit gravity */
1256 values
[ 3 ] = TRUE
; /* override redirect */
1257 values
[ 4 ] = XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
|
1258 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_POINTER_MOTION_HINT
|
1259 XCB_EVENT_MASK_BUTTON_MOTION
| XCB_EVENT_MASK_EXPOSURE
|
1260 XCB_EVENT_MASK_STRUCTURE_NOTIFY
| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
;
1262 xcb_create_window( c
, XCB_COPY_FROM_PARENT
, frame
->w
, geom
->root
,
1263 frame
->u
.frame
.x
, frame
->u
.frame
.y
,
1264 frame
->u
.frame
.width
, frame
->u
.frame
.height
, 1,
1265 XCB_WINDOW_CLASS_INPUT_OUTPUT
, XCB_COPY_FROM_PARENT
,
1266 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
|
1267 XCB_CW_BIT_GRAVITY
| XCB_CW_OVERRIDE_REDIRECT
|
1268 XCB_CW_EVENT_MASK
, values
);
1270 values
[ 0 ] = gwm_screens
[ frame
->screen
].pixels
[
1271 COL_BUTTON_INACTIVE
]; /* background pixel */
1272 values
[ 1 ] = gwm_screens
[ frame
->screen
].pixels
[
1273 COL_BORDER
]; /* border pixel */
1274 values
[ 2 ] = TRUE
; /* override redirect */
1275 values
[ 3 ] = XCB_EVENT_MASK_EXPOSURE
;
1276 xcb_create_window( c
, XCB_COPY_FROM_PARENT
, button
->w
, frame
->w
,
1277 2, 1, FRAME_BUTTON_SIZE
, FRAME_BUTTON_SIZE
, 1,
1278 XCB_WINDOW_CLASS_INPUT_OUTPUT
, XCB_COPY_FROM_PARENT
,
1279 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
|
1280 XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
, values
);
1282 xcb_grab_button( c
, FALSE
, button
->w
, XCB_EVENT_MASK_BUTTON_PRESS
|
1283 XCB_EVENT_MASK_BUTTON_RELEASE
|
1284 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
,
1285 XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
, XCB_NONE
,
1286 XCB_NONE
, XCB_BUTTON_INDEX_ANY
, 0x8000 );
1288 xcb_map_window( c
, button
->w
);
1290 xcb_change_save_set( c
, XCB_SET_MODE_INSERT
, w
);
1292 if( !map_request
) {
1293 /* If the window was mapped already, then reparenting it will
1294 generate an UnmapNotify. To avoid race conditions in discarding
1295 this unwanted event, temporarily reduce our event mask. */
1296 xcb_grab_server( c
);
1298 values
[ 0 ] = XCB_EVENT_MASK_PROPERTY_CHANGE
|
1299 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1300 xcb_change_window_attributes( c
, w
, XCB_CW_EVENT_MASK
, values
);
1303 xcb_reparent_window( c
, w
, frame
->w
, FRAME_BORDER_WIDTH
,
1304 FRAME_TITLE_HEIGHT
);
1305 if( !map_request
) {
1306 values
[ 0 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
1307 XCB_EVENT_MASK_PROPERTY_CHANGE
|
1308 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1309 xcb_change_window_attributes( c
, w
, XCB_CW_EVENT_MASK
, values
);
1311 xcb_ungrab_server( c
);
1314 if( geom
->border_width
) {
1316 xcb_configure_window( c
, w
, XCB_CONFIG_WINDOW_BORDER_WIDTH
, values
);
1320 if( have_extension
[ EXT_SHAPE
] && shape
&& shape
->bounding_shaped
)
1321 match_managed_shape( window
, TRUE
);
1324 set_managed_state( window
, map_request
&&
1325 ( window
->u
.managed
.hints
& HINT_ICONIC
) ?
1326 STATE_ICONIC
: STATE_NORMAL
);
1328 if( window
->u
.managed
.state
== STATE_NORMAL
) {
1330 xcb_map_window( c
, w
);
1332 xcb_map_window( c
, frame
->w
);
1335 /* An ugly hack to get the button cursor properly set now that
1336 the button has been created. */
1337 managed_property_change( window
, PROP_WM_PROTOCOLS
,
1338 props
[ PROP_WM_PROTOCOLS
] );
1342 struct gwm_window
*window
; /* type WINDOW_INCOMPLETE */
1344 xcb_get_geometry_cookie_t geometry_cookie
;
1346 xcb_shape_query_extents_cookie_t shape_cookie
;
1348 xcb_get_property_cookie_t prop_cookies
[ NUM_PROPS
];
1351 static void handle_manage_window( unsigned int sequence
, void *reply
,
1352 xcb_generic_error_t
*error
,
1353 union callback_param cp
) {
1355 struct new_window
*nw
= cp
.p
;
1357 xcb_get_window_attributes_reply_t
*attr
= reply
;
1358 xcb_get_geometry_reply_t
*geom
= xcb_get_geometry_reply(
1359 c
, nw
->geometry_cookie
, NULL
);
1361 xcb_shape_query_extents_reply_t
*shape
= have_extension
[ EXT_SHAPE
] ?
1362 xcb_shape_query_extents_reply( c
, nw
->shape_cookie
, NULL
) : NULL
;
1364 xcb_get_property_reply_t
*props
[ NUM_PROPS
];
1367 for( i
= 0, have_all_props
= TRUE
; i
< NUM_PROPS
; i
++ )
1368 if( !( props
[ i
] = xcb_get_property_reply( c
, nw
->prop_cookies
[ i
],
1370 have_all_props
= FALSE
;
1373 show_error( error
);
1378 if( attr
&& geom
&& have_all_props
&&
1379 attr
->_class
== XCB_WINDOW_CLASS_INPUT_OUTPUT
&&
1380 ( nw
->map_request
|| ( attr
->map_state
== XCB_MAP_STATE_VIEWABLE
&&
1381 !attr
->override_redirect
) ) )
1382 start_managing_window( nw
->window
, attr
, geom
,
1386 props
, nw
->map_request
);
1388 stop_listening( nw
->window
);
1389 forget_window( nw
->window
);
1403 for( i
= 0; i
< NUM_PROPS
; i
++ )
1410 extern void manage_window( xcb_window_t w
, int map_request
) {
1412 struct new_window
*nw
= xmalloc( sizeof *nw
);
1413 uint32_t values
[ 2 ];
1415 union callback_param cp
;
1418 if( !( nw
->window
= add_window( w
) ) ) {
1419 /* The window already exists. This can happen if a client attempts
1420 more than one MapWindow before we've handle the first one, and
1421 so we receive multiple MapRequests. */
1426 /* Our request for notification of events concerning the client
1427 window must be atomic with our query of its state (otherwise
1428 there will exist some interval where we will be unaware of updates,
1429 or receive notifications for updates which were already present in
1430 the originally queried state). Unfortunately this means that we're
1431 forced to receive events before we even know whether we're interested
1432 in managing the window. If we subsequently discover that we
1433 didn't want the events after all, we will call stop_listening()
1434 (below), and any corresponding events for the unknown window
1435 will be silently ignored. */
1436 xcb_grab_server( c
);
1438 nw
->map_request
= map_request
;
1439 nw
->geometry_cookie
= xcb_get_geometry( c
, w
);
1441 if( have_extension
[ EXT_SHAPE
] )
1442 nw
->shape_cookie
= xcb_shape_query_extents( c
, w
);
1444 for( i
= 0; i
< NUM_PROPS
; i
++ )
1445 nw
->prop_cookies
[ i
] = xcb_get_property( c
, FALSE
, w
, prop_atoms
[ i
],
1449 seq
= xcb_get_window_attributes( c
, w
).sequence
;
1451 values
[ 0 ] = XCB_GRAVITY_NORTH_WEST
;
1452 values
[ 1 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
1453 XCB_EVENT_MASK_PROPERTY_CHANGE
|
1454 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1455 xcb_change_window_attributes( c
, w
, XCB_CW_WIN_GRAVITY
| XCB_CW_EVENT_MASK
,
1459 if( have_extension
[ EXT_SHAPE
] )
1460 xcb_shape_select_input( c
, w
, TRUE
);
1463 xcb_ungrab_server( c
);
1466 handle_async_reply( seq
, handle_manage_window
, cp
);
1468 nw
->window
->type
= WINDOW_INCOMPLETE
;
1469 nw
->window
->u
.incomplete
.sequence
= seq
;
1472 extern void unmanage_window( struct gwm_window
*window
) {
1474 xcb_window_t w
= window
->w
;
1475 struct gwm_window
*frame
= window
->u
.managed
.frame
;
1476 uint32_t border_width
= window
->u
.managed
.border_width
;
1479 if( focus_frame
== frame
)
1482 stop_listening( window
);
1484 /* Ignore Window errors on the child, because it might be destroyed
1485 before we are notified. */
1487 handle_error_reply( xcb_change_save_set_checked(
1488 c
, XCB_SET_MODE_DELETE
, w
), ERR_MASK_WINDOW
);
1490 /* Reparent the window back to the root; delete its WM_STATE
1491 property (see ICCCM 4.1.4); and map it if it was iconic. */
1492 translate_frame_to_child( &x
, &y
, frame
->u
.frame
.x
, frame
->u
.frame
.y
,
1493 border_width
, window
->u
.managed
.win_gravity
);
1494 handle_error_reply( xcb_reparent_window_checked(
1495 c
, w
, screens
[ window
->screen
]->root
,
1496 x
, y
), ERR_MASK_WINDOW
);
1498 handle_error_reply( xcb_delete_property_checked(
1499 c
, w
, atoms
[ ATOM_WM_STATE
] ), ERR_MASK_WINDOW
);
1501 handle_error_reply( xcb_configure_window_checked(
1502 c
, w
, XCB_CONFIG_WINDOW_BORDER_WIDTH
,
1503 &border_width
), ERR_MASK_WINDOW
);
1505 if( window
->u
.managed
.state
== STATE_ICONIC
)
1506 handle_error_reply( xcb_map_window_checked( c
, w
), ERR_MASK_WINDOW
);
1508 xcb_destroy_window( c
, frame
->w
);
1510 if( window
->u
.managed
.name
)
1511 free( window
->u
.managed
.name
);
1513 forget_window( window
);
1514 forget_window( frame
->u
.frame
.button
);
1515 forget_window( frame
);
1518 extern void iconic_to_normal( struct gwm_window
*window
) {
1520 assert( window
->type
== WINDOW_MANAGED
);
1522 set_managed_state( window
, STATE_NORMAL
);
1523 xcb_map_window( c
, window
->w
);
1524 xcb_map_window( c
, window
->u
.managed
.frame
->w
);
1527 extern void deactivate_focus_frame( void ) {
1534 n
= gwm_screens
[ focus_frame
->screen
].pixels
[ COL_FRAME_INACTIVE
];
1535 xcb_change_window_attributes( c
, focus_frame
->w
, XCB_CW_BACK_PIXEL
, &n
);
1537 queue_window_update( focus_frame
, 0, 0, focus_frame
->u
.frame
.width
,
1538 focus_frame
->u
.frame
.height
, FALSE
);
1541 extern void generic_expose( struct gwm_window
*window
,
1542 xcb_expose_event_t
*ev
) {
1544 queue_window_update( window
, ev
->x
, ev
->y
, ev
->width
, ev
->height
, TRUE
);
1547 xcb_window_t passive_grab
; /* the window which has the pointer automatically
1548 grabbed via a button press, or XCB_NONE */
1550 static void handle_fake_change_property( unsigned int sequence
, void *reply
,
1551 xcb_generic_error_t
*error
,
1552 union callback_param cp
) {
1554 xcb_selection_notify_event_t
*p
= cp
.p
;
1561 p
->property
= XCB_NONE
;
1564 handle_error_reply( xcb_send_event_checked( c
, FALSE
, p
->requestor
, 0,
1565 (char *) p
), ERR_MASK_WINDOW
);
1570 static void fake_selection_request( struct gwm_window
*window
,
1571 xcb_selection_request_event_t
*ev
) {
1573 if( ev
->time
!= XCB_CURRENT_TIME
&& ev
->time
< window
->u
.fake
.timestamp
&&
1574 ev
->target
== atoms
[ ATOM_VERSION
] ) {
1575 /* ICCCM version identification: ICCCM 2.0, section 4.3. */
1576 static const uint32_t values
[ 2 ] = { 2, 0 };
1577 struct xcb_selection_notify_event_t
*p
= xmalloc( sizeof *p
);
1578 union callback_param cp
;
1580 p
->requestor
= ev
->requestor
;
1581 p
->selection
= ev
->selection
;
1582 p
->target
= ev
->target
;
1583 p
->property
= ev
->property
;
1586 handle_async_reply( xcb_change_property_checked(
1587 c
, XCB_PROP_MODE_REPLACE
, ev
->requestor
,
1588 atoms
[ ATOM_VERSION
], INTEGER
, 32, 2,
1589 values
).sequence
, handle_fake_change_property
,
1592 struct xcb_selection_notify_event_t
not;
1594 not.requestor
= ev
->requestor
;
1595 not.selection
= ev
->selection
;
1596 not.target
= ev
->target
;
1597 not.property
= XCB_NONE
;
1598 not.time
= ev
->time
;
1600 handle_error_reply( xcb_send_event_checked( c
, FALSE
, not.requestor
,
1606 static event_handler fake_handlers
[] = {
1609 NULL
, /* KeyPress */
1610 NULL
, /* KeyRelease */
1611 NULL
, /* ButtonPress */
1612 NULL
, /* ButtonRelease */
1613 NULL
, /* MotionNotify */
1614 NULL
, /* EnterNotify */
1615 NULL
, /* LeaveNotify */
1617 NULL
, /* FocusOut */
1618 NULL
, /* KeymapNotify */
1620 NULL
, /* GraphicsExpose */
1621 NULL
, /* NoExposure */
1622 NULL
, /* VisibilityNotify */
1623 NULL
, /* CreateNotify */
1624 NULL
, /* DestroyNotify */
1625 NULL
, /* UnmapNotify */
1626 NULL
, /* MapNotify */
1627 NULL
, /* MapRequest */
1628 NULL
, /* ReparentNotify */
1629 NULL
, /* ConfigureNotify */
1630 NULL
, /* ConfigureRequest */
1631 NULL
, /* GravityNotify */
1632 NULL
, /* ResizeRequest */
1633 NULL
, /* CirculateNotify */
1634 NULL
, /* CirculateRequest */
1635 NULL
, /* PropertyNotify */
1636 NULL
, /* SelectionClear */
1637 (event_handler
) fake_selection_request
, /* SelectionRequest */
1638 NULL
, /* SelectionNotify */
1639 NULL
, /* ColormapNotify */
1640 NULL
, /* ClientMessage */
1641 NULL
, /* MappingNotify */
1642 NULL
, /* (synthetic) */
1643 NULL
/* ShapeNotify */
1646 static event_handler feedback_handlers
[] = {
1649 NULL
, /* KeyPress */
1650 NULL
, /* KeyRelease */
1651 NULL
, /* ButtonPress */
1652 NULL
, /* ButtonRelease */
1653 NULL
, /* MotionNotify */
1654 NULL
, /* EnterNotify */
1655 NULL
, /* LeaveNotify */
1657 NULL
, /* FocusOut */
1658 NULL
, /* KeymapNotify */
1659 (event_handler
) generic_expose
,
1660 NULL
, /* GraphicsExpose */
1661 NULL
, /* NoExposure */
1662 NULL
, /* VisibilityNotify */
1663 NULL
, /* CreateNotify */
1664 NULL
, /* DestroyNotify */
1665 NULL
, /* UnmapNotify */
1666 NULL
, /* MapNotify */
1667 NULL
, /* MapRequest */
1668 NULL
, /* ReparentNotify */
1669 NULL
, /* ConfigureNotify */
1670 NULL
, /* ConfigureRequest */
1671 NULL
, /* GravityNotify */
1672 NULL
, /* ResizeRequest */
1673 NULL
, /* CirculateNotify */
1674 NULL
, /* CirculateRequest */
1675 NULL
, /* PropertyNotify */
1676 NULL
, /* SelectionClear */
1677 NULL
, /* SelectionRequest */
1678 NULL
, /* SelectionNotify */
1679 NULL
, /* ColormapNotify */
1680 NULL
, /* ClientMessage */
1681 NULL
, /* MappingNotify */
1682 NULL
/* (synthetic) */
1685 static const event_handler
*handlers
[] = {
1686 root_handlers
, managed_handlers
, frame_handlers
, button_handlers
,
1687 fake_handlers
, feedback_handlers
, NULL
1690 static void setup_display( void ) {
1693 xcb_intern_atom_cookie_t atom_cookies
[ NUM_ATOMS
];
1694 const xcb_setup_t
*setup
;
1695 xcb_screen_iterator_t iter
;
1696 xcb_intern_atom_cookie_t
*screen_atom_cookies
;
1698 xcb_get_selection_owner_cookie_t
*screen_owner_cookies
;
1699 xcb_window_t
*screen_owners
;
1700 xcb_client_message_event_t msg
;
1701 xcb_void_cookie_t
*cookies
;
1702 xcb_query_tree_cookie_t
*tree_cookies
;
1704 table_init( &windows
);
1705 table_init( &update_windows
);
1707 c
= xcb_connect( NULL
, NULL
);
1709 if( xcb_connection_has_error( c
) )
1710 fatal( "could not connect to server" );
1712 for( i
= 0; i
< NUM_EXTENSIONS
; i
++ )
1713 xcb_prefetch_extension_data( c
, (xcb_extension_t
*) extensions
[ i
] );
1715 for( i
= 0; i
< NUM_ATOMS
; i
++ )
1716 atom_cookies
[ i
] = xcb_intern_atom( c
, 0, strlen( atom_names
[ i
] ),
1719 setup
= xcb_get_setup( c
);
1721 iter
= xcb_setup_roots_iterator( setup
);
1722 num_screens
= iter
.rem
;
1723 screens
= xmalloc( num_screens
* sizeof *screens
);
1724 gwm_screens
= xmalloc( num_screens
* sizeof *gwm_screens
);
1725 screen_atom_cookies
= alloca( num_screens
* sizeof *screen_atom_cookies
);
1726 screen_owners
= alloca( num_screens
* sizeof *screen_owners
);
1727 screen_owner_cookies
= alloca( num_screens
* sizeof *screen_owner_cookies
);
1728 cookies
= alloca( num_screens
* sizeof *cookies
);
1729 tree_cookies
= alloca( num_screens
* sizeof *tree_cookies
);
1731 for( i
= 0; iter
.rem
; i
++, xcb_screen_next( &iter
) ) {
1732 static char wm_atom_str
[ 16 ] = "WM_S";
1733 xcb_depth_iterator_t depth_iter
;
1735 screens
[ i
] = iter
.data
;
1736 screen_atom_cookies
[ i
] =
1737 xcb_intern_atom( c
, 0, 4 + sprintf( wm_atom_str
+ 4, "%d", i
),
1740 for( depth_iter
= xcb_screen_allowed_depths_iterator( screens
[ i
] );
1741 depth_iter
.rem
; xcb_depth_next( &depth_iter
) ) {
1742 xcb_visualtype_iterator_t visual_iter
;
1744 for( visual_iter
= xcb_depth_visuals_iterator( depth_iter
.data
);
1745 visual_iter
.rem
; xcb_visualtype_next( &visual_iter
) ) {
1746 xcb_visualtype_t
*visual
= visual_iter
.data
;
1748 if( screens
[ i
]->root_visual
== visual
->visual_id
) {
1749 gwm_screens
[ i
].root_visual
= visual
;
1754 fatal( "Root visual for screen %d (0x%X) not found", i
,
1755 screens
[ i
]->root_visual
);
1760 fake_window
= add_window( xcb_generate_id( c
) );
1761 fake_window
->screen
= 0;
1762 fake_window
->type
= WINDOW_FAKE
;
1764 n
= XCB_EVENT_MASK_PROPERTY_CHANGE
;
1765 xcb_create_window( c
, 0, fake_window
->w
, screens
[ 0 ]->root
, 0, 0, 1, 1, 0,
1766 XCB_WINDOW_CLASS_INPUT_ONLY
, screens
[ 0 ]->root_visual
,
1767 XCB_CW_EVENT_MASK
, &n
);
1769 /* Obtain a timestamp we can use for our selection acquiry time
1770 (ICCCM 2.0, section 2.1). */
1771 xcb_change_property( c
, XCB_PROP_MODE_APPEND
, fake_window
->w
, WM_HINTS
,
1772 WM_HINTS
, 32, 0, NULL
);
1774 xcb_flush( c
); /* BLOCK */
1776 for( i
= 0; i
< NUM_EXTENSIONS
; i
++ ) {
1777 const xcb_query_extension_reply_t
*r
;
1779 if( ( have_extension
[ i
] =
1780 ( r
= xcb_get_extension_data(
1781 c
, (xcb_extension_t
*) extensions
[ i
] ) ) && r
->present
) ) {
1782 extension_event
[ i
] = r
->first_event
;
1783 extension_error
[ i
] = r
->first_error
;
1787 decorate_core_init();
1788 update_window
= core_update_window
;
1790 for( i
= 0; i
< NUM_ATOMS
; i
++ ) {
1791 xcb_intern_atom_reply_t
*r
=
1792 xcb_intern_atom_reply( c
, atom_cookies
[ i
], NULL
);
1794 atoms
[ i
] = r
->atom
;
1798 prop_atoms
[ PROP_WM_COLORMAP_WINDOWS
] = atoms
[ ATOM_WM_COLORMAP_WINDOWS
];
1799 prop_atoms
[ PROP_WM_HINTS
] = WM_HINTS
;
1800 prop_atoms
[ PROP_WM_NAME
] = WM_NAME
;
1801 prop_atoms
[ PROP_WM_NORMAL_HINTS
] = WM_NORMAL_HINTS
;
1802 prop_atoms
[ PROP_WM_PROTOCOLS
] = atoms
[ ATOM_WM_PROTOCOLS
];
1804 prop_types
[ PROP_WM_COLORMAP_WINDOWS
] = WINDOW
;
1805 prop_types
[ PROP_WM_HINTS
] = WM_HINTS
;
1806 prop_types
[ PROP_WM_NAME
] = XCB_GET_PROPERTY_TYPE_ANY
;
1807 prop_types
[ PROP_WM_NORMAL_HINTS
] = WM_SIZE_HINTS
;
1808 prop_types
[ PROP_WM_PROTOCOLS
] = ATOM
;
1810 for( i
= 0; i
< num_screens
; i
++ ) {
1811 xcb_intern_atom_reply_t
*r
=
1812 xcb_intern_atom_reply( c
, screen_atom_cookies
[ i
], NULL
);
1814 gwm_screens
[ i
].wm_atom
= r
->atom
;
1818 /* Listen for a selection timestamp. */
1821 xcb_generic_event_t
*ev
= wait_for_event();
1824 fatal( "did not receive property notification" );
1826 if( ev
->response_type
& SEND_EVENT_MASK
) {
1829 continue; /* ignore synthetic events */
1830 } else if( ev
->response_type
== XCB_PROPERTY_NOTIFY
) {
1831 xcb_property_notify_event_t
*not =
1832 (xcb_property_notify_event_t
*) ev
;
1834 if( not->window
!= fake_window
->w
||
1835 not->atom
!= WM_HINTS
||
1836 not->state
!= XCB_PROPERTY_NEW_VALUE
) {
1837 warning( "unexpected property notification" );
1842 fake_window
->u
.fake
.timestamp
= not->time
;
1847 } else if( ev
->response_type
!= XCB_MAPPING_NOTIFY
) {
1848 /* Ignore MappingNotify -- we'll query the mappings later. */
1849 warning( "unexpected event while waiting for property "
1861 xcb_change_window_attributes( c
, fake_window
->w
, XCB_CW_EVENT_MASK
, &n
);
1864 int existing_manager
;
1865 int managers_changed
;
1866 sigset_t sigs
, oldsigs
;
1868 for( i
= 0; i
< num_screens
; i
++ )
1869 screen_owner_cookies
[ i
] =
1870 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1872 xcb_flush( c
); /* BLOCK */
1874 existing_manager
= 0;
1875 for( i
= 0; i
< num_screens
; i
++ ) {
1876 xcb_get_selection_owner_reply_t
*r
=
1877 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1880 if( ( screen_owners
[ i
] = r
->owner
) != XCB_NONE
) {
1882 printf( "Screen %d is already managed.\n", i
);
1890 if( existing_manager
&& !flag_replace
) {
1891 printf( "Use \"%s --replace\" to replace any existing "
1892 "window manager(s).\n", argv0
);
1897 if( existing_manager
) {
1900 /* Wait for existing manager(s) to terminate (ICCCM 2.0,
1902 n
= XCB_EVENT_MASK_STRUCTURE_NOTIFY
;
1903 for( i
= 0; i
< num_screens
; i
++ )
1904 if( screen_owners
[ i
] != XCB_NONE
)
1905 xcb_change_window_attributes( c
, screen_owners
[ i
],
1906 XCB_CW_EVENT_MASK
, &n
);
1908 for( i
= 0; i
< num_screens
; i
++ )
1909 screen_owner_cookies
[ i
] =
1910 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1912 xcb_flush( c
); /* BLOCK */
1914 managers_changed
= FALSE
;
1915 for( i
= 0; i
< num_screens
; i
++ ) {
1916 xcb_get_selection_owner_reply_t
*r
=
1917 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1920 if( r
->owner
!= screen_owners
[ i
] )
1921 managers_changed
= TRUE
;
1926 if( managers_changed
) {
1927 /* Argh. A window manager changed during all our bookkeeping.
1928 Undo our notifications, and start again. */
1930 for( i
= 0; i
< num_screens
; i
++ )
1931 if( screen_owners
[ i
] != XCB_NONE
)
1932 xcb_change_window_attributes( c
, screen_owners
[ i
],
1933 XCB_CW_EVENT_MASK
, &n
);
1939 /* Acquire ownership of WM_Sn selections (ICCCM 2.0, section 4.3). */
1940 for( i
= 0; i
< num_screens
; i
++ ) {
1941 xcb_set_selection_owner( c
, fake_window
->w
,
1942 gwm_screens
[ i
].wm_atom
,
1943 fake_window
->u
.fake
.timestamp
);
1944 screen_owner_cookies
[ i
] =
1945 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1948 xcb_flush( c
); /* BLOCK */
1949 sync_with_callback( screen_owner_cookies
[ num_screens
- 1 ].sequence
);
1951 for( i
= 0; i
< num_screens
; i
++ ) {
1952 xcb_get_selection_owner_reply_t
*r
=
1953 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1956 if( r
->owner
!= fake_window
->w
)
1957 fatal( "did not acquire window manager selection" );
1964 /* Listen for DestroyNotify on any selection holders. */
1965 while( existing_manager
) {
1966 static int killed_existing_managers
;
1968 xcb_generic_event_t
*ev
= wait_for_event(); /* BLOCK */
1971 if( flag_force
&& signal_caught
== SIGALRM
&&
1972 !killed_existing_managers
) {
1975 for( i
= 0; i
< num_screens
; i
++ )
1976 if( screen_owners
[ i
] )
1977 xcb_kill_client( c
, screen_owners
[ i
] );
1979 killed_existing_managers
= TRUE
;
1986 fatal( "did not receive destroy notification" );
1989 if( ev
->response_type
& SEND_EVENT_MASK
) {
1992 continue; /* ignore synthetic events */
1993 } else if( ev
->response_type
== XCB_DESTROY_NOTIFY
) {
1994 xcb_destroy_notify_event_t
*not =
1995 (xcb_destroy_notify_event_t
*) ev
;
1997 for( i
= 0; i
< num_screens
; i
++ )
1998 if( not->window
== screen_owners
[ i
] ) {
1999 screen_owners
[ i
] = XCB_NONE
;
2004 } else if( ev
->response_type
== XCB_SELECTION_CLEAR
)
2005 /* We lost a window manager selection before we even
2006 started. Just shut down quietly. */
2008 else if( ev
->response_type
== XCB_MAPPING_NOTIFY
)
2009 /* Ignore MappingNotify. */
2011 else if( !ev
->response_type
) {
2013 xcb_generic_error_t
*error
= (xcb_generic_error_t
*) ev
;
2015 /* Expect Window and Value errors, because old window managers
2016 might die before we sent a corresponding request. */
2017 if( error
->error_code
!= XCB_WINDOW
&&
2018 error
->error_code
!= XCB_VALUE
)
2019 show_error( error
);
2023 warning( "unexpected event while waiting for destroy "
2034 sigfillset( &sigs
);
2035 sigprocmask( SIG_BLOCK
, &sigs
, &oldsigs
);
2038 if( signal_caught
== SIGALRM
)
2041 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
2044 msg
.response_type
= XCB_CLIENT_MESSAGE
;
2047 msg
.type
= atoms
[ ATOM_MANAGER
];
2048 msg
.data
.data32
[ 0 ] = fake_window
->u
.fake
.timestamp
;
2049 /* Data32[ 1 ] varies; will be completed later. */
2050 msg
.data
.data32
[ 2 ] = fake_window
->w
;
2051 msg
.data
.data32
[ 3 ] = 0;
2052 msg
.data
.data32
[ 4 ] = 0;
2054 n
= XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
|
2055 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
;
2057 for( i
= 0; i
< num_screens
; i
++ ) {
2058 /* Send StructureNotify ClientMessage (ICCCM 2.0, section 2.8). */
2059 msg
.window
= screens
[ i
]->root
;
2060 msg
.data
.data32
[ 1 ] = gwm_screens
[ i
].wm_atom
;
2061 xcb_send_event( c
, FALSE
, screens
[ i
]->root
,
2062 XCB_EVENT_MASK_STRUCTURE_NOTIFY
, (char *) &msg
);
2065 xcb_change_window_attributes_checked( c
, screens
[ i
]->root
,
2066 XCB_CW_EVENT_MASK
, &n
);
2068 tree_cookies
[ i
] = xcb_query_tree( c
, screens
[ i
]->root
);
2071 xcb_set_input_focus( c
, XCB_INPUT_FOCUS_NONE
, XCB_INPUT_FOCUS_POINTER_ROOT
,
2072 fake_window
->u
.fake
.timestamp
);
2074 get_keyboard_mapping( setup
->min_keycode
,
2075 setup
->max_keycode
- setup
->min_keycode
+ 1 );
2076 get_modifier_mapping();
2078 xcb_flush( c
); /* BLOCK */
2079 sync_with_callback( cookies
[ num_screens
- 1 ].sequence
);
2081 for( i
= 0; i
< num_screens
; i
++ ) {
2082 xcb_generic_error_t
*error
= xcb_request_check( c
, cookies
[ i
] );
2083 xcb_query_tree_reply_t
*r
;
2084 struct gwm_window
*window
;
2085 xcb_window_t
*children
;
2089 fatal( "could not acquire SubstructureRedirect" );
2091 window
= add_window( screens
[ i
]->root
);
2093 window
->type
= WINDOW_ROOT
;
2095 gwm_screens
[ i
].cmap
= XCB_NONE
;
2096 gwm_screens
[ i
].cmap_time
= fake_window
->u
.fake
.timestamp
;
2098 r
= xcb_query_tree_reply( c
, tree_cookies
[ i
], NULL
);
2099 children
= xcb_query_tree_children( r
);
2100 for( j
= 0; j
< r
->children_len
; j
++ )
2101 manage_window( children
[ j
], FALSE
);
2107 static void handle_mapping_notify( xcb_mapping_notify_event_t
*ev
) {
2109 switch( ev
->request
) {
2110 case XCB_MAPPING_MODIFIER
:
2111 get_modifier_mapping();
2114 case XCB_MAPPING_KEYBOARD
:
2115 get_keyboard_mapping( ev
->first_keycode
, ev
->count
);
2118 case XCB_MAPPING_POINTER
:
2123 static void handle_client_message( xcb_client_message_event_t
*ev
) {
2125 if( ev
->type
== atoms
[ ATOM_WM_CHANGE_STATE
] ) {
2126 struct gwm_window
*window
;
2128 if( ( window
= lookup_window( ev
->window
) ) &&
2129 window
->type
== WINDOW_MANAGED
&&
2130 window
->u
.managed
.state
== STATE_NORMAL
&&
2131 ev
->data
.data32
[ 0 ] == STATE_ICONIC
) {
2134 set_managed_state( window
, STATE_ICONIC
);
2135 /* We need to unmap our child without invoking the normal
2136 UnmapNotify handler response. Unfortunately it's very
2137 difficult to communicate to the handler the distinction
2138 between the resultant UnmapNotify from an unmap here
2139 and an unrelated event. Checking the request sequence
2140 is not robust, because asynchronous events might not be
2141 assigned unique sequence numbers. Instead, we grab
2142 the server and temporarily change our event mask, which
2143 seems a heavyweight approach but does guarantee that
2144 only this event will be ignored. */
2145 xcb_grab_server( c
);
2147 n
= XCB_EVENT_MASK_PROPERTY_CHANGE
|
2148 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
2149 xcb_change_window_attributes( c
, window
->w
, XCB_CW_EVENT_MASK
, &n
);
2151 xcb_unmap_window( c
, window
->w
);
2153 n
= XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
2154 XCB_EVENT_MASK_PROPERTY_CHANGE
|
2155 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
2156 xcb_change_window_attributes( c
, window
->w
, XCB_CW_EVENT_MASK
, &n
);
2158 xcb_ungrab_server( c
);
2160 xcb_unmap_window( c
, window
->u
.managed
.frame
->w
);
2162 } else if( ev
->type
== atoms
[ ATOM_WM_COLORMAP_NOTIFY
] ) {
2163 struct gwm_window
*window
;
2165 if( ( window
= lookup_window( ev
->window
) ) &&
2166 window
->type
== WINDOW_ROOT
&&
2167 !ev
->data
.data32
[ 1 ] )
2168 /* Ignore the case where the client is starting installation:
2169 if it is well-behaved, it will do so only when the pointer
2170 is grabbed (in which case we won't do anything to conflict
2171 with it); if it is not well-behaved, it's not worth trying
2172 to accomodate it. */
2173 install_window_colormap( window
->screen
, focus_frame
?
2174 focus_frame
->u
.frame
.child
: NULL
,
2175 ev
->data
.data32
[ 0 ] );
2179 static void handle_events( void ) {
2181 xcb_generic_event_t
*ev
;
2183 while( ( ev
= wait_for_event() ) ) {
2184 int event_type
= ev
->response_type
;
2185 int event_base_type
= event_type
& 0x7F;
2188 /* Determine event window and time. */
2189 switch( event_base_type
) {
2192 case XCB_KEYMAP_NOTIFY
:
2193 case XCB_CLIENT_MESSAGE
:
2194 case XCB_MAPPING_NOTIFY
:
2199 w
= ( (xcb_key_press_event_t
*) ev
)->event
;
2200 latest_timestamp
= ( (xcb_key_press_event_t
*) ev
)->time
;
2203 case XCB_KEY_RELEASE
:
2204 w
= ( (xcb_key_release_event_t
*) ev
)->event
;
2205 latest_timestamp
= ( (xcb_key_release_event_t
*) ev
)->time
;
2208 case XCB_BUTTON_PRESS
:
2209 w
= ( (xcb_button_press_event_t
*) ev
)->event
;
2210 latest_timestamp
= ( (xcb_button_press_event_t
*) ev
)->time
;
2213 case XCB_BUTTON_RELEASE
:
2214 w
= ( (xcb_button_release_event_t
*) ev
)->event
;
2215 latest_timestamp
= ( (xcb_button_release_event_t
*) ev
)->time
;
2218 case XCB_MOTION_NOTIFY
:
2219 w
= ( (xcb_motion_notify_event_t
*) ev
)->event
;
2220 latest_timestamp
= ( (xcb_motion_notify_event_t
*) ev
)->time
;
2223 case XCB_ENTER_NOTIFY
:
2224 case XCB_LEAVE_NOTIFY
:
2225 w
= ( (xcb_enter_notify_event_t
*) ev
)->event
;
2226 latest_timestamp
= ( (xcb_enter_notify_event_t
*) ev
)->time
;
2231 w
= ( (xcb_focus_in_event_t
*) ev
)->event
;
2235 w
= ( (xcb_expose_event_t
*) ev
)->window
;
2238 case XCB_GRAPHICS_EXPOSURE
:
2239 w
= ( (xcb_graphics_exposure_event_t
*) ev
)->drawable
;
2242 case XCB_NO_EXPOSURE
:
2243 w
= ( (xcb_no_exposure_event_t
*) ev
)->drawable
;
2246 case XCB_VISIBILITY_NOTIFY
:
2247 w
= ( (xcb_visibility_notify_event_t
*) ev
)->window
;
2250 case XCB_CREATE_NOTIFY
:
2251 w
= ( (xcb_create_notify_event_t
*) ev
)->parent
;
2254 case XCB_DESTROY_NOTIFY
:
2255 w
= ( (xcb_destroy_notify_event_t
*) ev
)->event
;
2258 case XCB_UNMAP_NOTIFY
:
2259 w
= ( (xcb_unmap_notify_event_t
*) ev
)->event
;
2262 case XCB_MAP_NOTIFY
:
2263 w
= ( (xcb_map_notify_event_t
*) ev
)->event
;
2266 case XCB_MAP_REQUEST
:
2267 w
= ( (xcb_map_request_event_t
*) ev
)->parent
;
2270 case XCB_REPARENT_NOTIFY
:
2271 w
= ( (xcb_reparent_notify_event_t
*) ev
)->event
;
2274 case XCB_CONFIGURE_NOTIFY
:
2275 w
= ( (xcb_configure_notify_event_t
*) ev
)->event
;
2278 case XCB_CONFIGURE_REQUEST
:
2279 w
= ( (xcb_configure_request_event_t
*) ev
)->parent
;
2282 case XCB_GRAVITY_NOTIFY
:
2283 w
= ( (xcb_gravity_notify_event_t
*) ev
)->event
;
2286 case XCB_RESIZE_REQUEST
:
2287 w
= ( (xcb_resize_request_event_t
*) ev
)->window
;
2290 case XCB_CIRCULATE_NOTIFY
:
2291 case XCB_CIRCULATE_REQUEST
:
2292 w
= ( (xcb_circulate_notify_event_t
*) ev
)->event
;
2295 case XCB_PROPERTY_NOTIFY
:
2296 w
= ( (xcb_property_notify_event_t
*) ev
)->window
;
2297 latest_timestamp
= ( (xcb_property_notify_event_t
*) ev
)->time
;
2300 case XCB_SELECTION_CLEAR
:
2301 w
= ( (xcb_selection_clear_event_t
*) ev
)->owner
;
2302 latest_timestamp
= ( (xcb_selection_clear_event_t
*) ev
)->time
;
2305 case XCB_SELECTION_REQUEST
:
2306 w
= ( (xcb_selection_request_event_t
*) ev
)->owner
;
2307 if( ( (xcb_selection_request_event_t
*) ev
)->time
)
2309 ( (xcb_selection_request_event_t
*) ev
)->time
;
2312 case XCB_SELECTION_NOTIFY
:
2313 w
= ( (xcb_selection_notify_event_t
*) ev
)->requestor
;
2314 if( ( (xcb_selection_notify_event_t
*) ev
)->time
)
2316 ( (xcb_selection_notify_event_t
*) ev
)->time
;
2319 case XCB_COLORMAP_NOTIFY
:
2320 w
= ( (xcb_colormap_notify_event_t
*) ev
)->window
;
2325 if( event_base_type
== extension_event
[ EXT_SHAPE
] ) {
2326 w
= ( (xcb_shape_notify_event_t
*) ev
)->affected_window
;
2327 event_type
= SHAPE_NOTIFY
;
2331 warning( "Unknown event" );
2335 if( ev
->response_type
& 0x80 )
2336 event_type
= SYNTHETIC_EVENT
;
2338 /* Global handling for events (before specific window handler). */
2339 switch( event_type
) {
2341 show_error( (xcb_generic_error_t
*) ev
);
2345 warning( "handle_events: received reply message" );
2348 case XCB_BUTTON_PRESS
:
2349 if( initial_press( (xcb_button_press_event_t
*) ev
)
2354 case XCB_KEYMAP_NOTIFY
:
2357 case XCB_SELECTION_CLEAR
:
2358 /* We've lost the manager selection (see ICCCM 2.0, section
2359 2.8); close down gracefully. */
2363 case XCB_MAPPING_NOTIFY
:
2364 handle_mapping_notify( (xcb_mapping_notify_event_t
*) ev
);
2367 case SYNTHETIC_EVENT
:
2368 if( ev
->response_type
== ( XCB_CLIENT_MESSAGE
| SEND_EVENT_MASK
) )
2369 handle_client_message( (xcb_client_message_event_t
*) ev
);
2373 if( w
!= XCB_NONE
) {
2374 struct gwm_window
*window
;
2375 void ( *handler
)( struct gwm_window
*, xcb_generic_event_t
* );
2377 if( ( window
= lookup_window( w
) ) &&
2378 handlers
[ window
->type
] &&
2379 ( handler
= handlers
[ window
->type
][ event_type
] ) )
2380 handler( window
, ev
);
2383 /* Global handling for events (after specific window handler). */
2384 switch( event_type
) {
2385 case XCB_BUTTON_RELEASE
:
2386 if( final_release( (xcb_button_release_event_t
*) ev
) )
2387 passive_grab
= XCB_NONE
;
2395 static void handle_intern_atom( unsigned int sequence
, void *reply
,
2396 xcb_generic_error_t
*error
,
2397 union callback_param p
) {
2406 static void shutdown_display( void ) {
2410 xcb_intern_atom_cookie_t cookie
;
2411 union callback_param cp
;
2414 for( i
= 0; i
< windows
.used
; i
++ )
2415 if( windows
.values
[ i
]->type
== WINDOW_MANAGED
) {
2416 unmanage_window( windows
.values
[ i
] );
2421 /* The ICCCM says that using CurrentTime for SetInputFocus is naughty
2422 (section 4.2.7). But we don't have much alternative in this case:
2423 if we're shutting down in response to a signal, for instance. */
2424 xcb_set_input_focus( c
, XCB_INPUT_FOCUS_NONE
, XCB_INPUT_FOCUS_POINTER_ROOT
,
2427 decorate_core_done();
2430 for( i
= 0; i
< num_screens
; i
++ )
2431 xcb_change_window_attributes( c
, screens
[ i
]->root
,
2432 XCB_CW_EVENT_MASK
, &n
);
2434 /* A pointless request to generate a reply, so we'll know when we've
2435 processed everything the server will send us. */
2436 cookie
= xcb_intern_atom( c
, TRUE
, 4, "ATOM" );
2438 handle_async_reply( cookie
.sequence
, handle_intern_atom
, cp
);
2442 sync_with_callback( cookie
.sequence
);
2444 xcb_disconnect( c
);
2446 /* Be pedantic about deallocating memory, to assist any tools that are
2447 looking for leaks. */
2448 forget_window( fake_window
);
2451 for( i
= 0; i
< windows
.used
; i
++ )
2452 if( windows
.values
[ i
]->type
== WINDOW_ROOT
) {
2453 forget_window( windows
.values
[ i
] );
2458 /* All windows have now been deallocated -- release the tables, too. */
2459 table_destroy( &windows
);
2460 table_destroy( &update_windows
);
2465 free( gwm_screens
);
2473 static void unknown( char *opt
) {
2475 printf( "%s: unknown option %s\n"
2476 "Usage: %s [OPTION]...\n"
2477 "Try \"%s --help\" for more information.\n",
2478 argv0
, opt
, argv0
, argv0
);
2481 extern int main( int argc
, char *argv
[] ) {
2484 struct sigaction sa
;
2486 static int flag_help
, flag_version
;
2487 static const struct option
{
2492 { "debug", &flag_debug
},
2494 { "force", &flag_force
},
2495 { "help", &flag_help
},
2496 { "replace", &flag_replace
},
2497 { "version", &flag_version
}
2499 #define NUM_OPTIONS ( sizeof options / sizeof *options )
2501 #if DEBUG && HAVE_MTRACE
2507 for( i
= 1; i
< argc
; i
++ )
2508 if( argv
[ i
][ 0 ] == '-' ) {
2509 if( argv
[ i
][ 1 ] == '-' && argv
[ i
][ 2 ] ) {
2510 for( j
= 0; j
< NUM_OPTIONS
; j
++ )
2511 if( !strncmp( argv
[ i
] + 2, options
[ j
].opt
,
2512 strlen( argv
[ i
] + 2 ) ) ) {
2513 *options
[ j
].flag
= 1;
2517 if( j
== NUM_OPTIONS
) {
2518 unknown( argv
[ i
] );
2522 } else if( argv
[ i
][ 1 ] != '-' ) {
2523 for( j
= 0; j
< NUM_OPTIONS
; j
++ )
2524 if( argv
[ i
][ 1 ] == options
[ j
].opt
[ 0 ] &&
2526 *options
[ j
].flag
= 1;
2530 if( j
== NUM_OPTIONS
) {
2531 unknown( argv
[ i
] );
2537 unknown( argv
[ i
] );
2543 printf( "Usage: %s [OPTION]...\n"
2546 " -d, --debug Show all events received\n"
2548 " -f, --force If replacing another window manager, "
2549 "terminate it if necessary\n"
2550 " -h, --help Display this information and exit\n"
2551 " -r, --replace Take over window management if another "
2553 " -v, --version Display version information and exit\n"
2554 "Please send bug reports to <" PACKAGE_BUGREPORT
">.\n",
2560 if( flag_version
) {
2561 puts( PACKAGE_STRING
"\nPlease send bug reports to <"
2562 PACKAGE_BUGREPORT
">." );
2567 sa
.sa_handler
= catch_signal
;
2568 sigemptyset( &sa
.sa_mask
);
2570 sigaction( SIGHUP
, &sa
, NULL
);
2571 sigaction( SIGINT
, &sa
, NULL
);
2572 sigaction( SIGTERM
, &sa
, NULL
);
2574 sa
.sa_handler
= alarm_signal
;
2575 sigaction( SIGALRM
, &sa
, NULL
);
2577 sa
.sa_handler
= child_signal
;
2578 sa
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
2579 sigaction( SIGCHLD
, &sa
, NULL
);
2584 which_signal
= signal_caught
;
2585 signal_caught
= 0; /* acknowledge receipt, so we get normal event
2586 processing during shutdown */
2588 if( xcb_connection_has_error( c
) )
2589 /* Don't bother attempting orderly shutdown. */
2590 fatal( "server connection error" );
2594 if( which_signal
) {
2595 raise( which_signal
);