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/>.
48 #include <xcb/composite.h>
51 #include <xcb/damage.h>
54 #include <xcb/render.h>
57 #include <xcb/shape.h>
60 #include <xcb/xcbext.h>
62 #include <xcb/xfixes.h>
68 #include "decorate-core.h"
70 #include "decorate-render.h"
78 #include "window-table.h"
81 xcb_timestamp_t latest_timestamp
;
83 xcb_atom_t atoms
[ NUM_ATOMS
];
87 _NET_NUMBER_OF_DESKTOPS (1),
88 _NET_DESKTOP_GEOMETRY,
89 _NET_DESKTOP_VIEWPORT (0,0),
90 _NET_CURRENT_DESKTOP (0),
92 _NET_SUPPORTING_WM_CHECK properties to root windows. */
93 static const char *const atom_names
[ NUM_ATOMS
] = {
102 "WM_COLORMAP_NOTIFY",
103 "WM_COLORMAP_WINDOWS",
110 xcb_atom_t prop_atoms
[ NUM_PROPS
];
111 xcb_atom_t prop_types
[ NUM_PROPS
];
113 static const xcb_extension_t
*const extensions
[ EXTENSIONS_SIZE
] = {
131 int have_extension
[ EXTENSIONS_SIZE
];
132 uint8_t extension_event
[ EXTENSIONS_SIZE
],
133 extension_error
[ EXTENSIONS_SIZE
];
136 xcb_screen_t
**screens
;
137 struct gwm_screen
*gwm_screens
;
139 xcb_cursor_t cursors
[ NUM_CURSORS
];
141 struct gwm_window
*fake_window
;
144 static int flag_replace
, flag_force
;
146 static int flag_debug
;
149 volatile int signal_caught
;
151 static void ( *update_window
)( struct gwm_window
*window
);
152 void ( *window_size
)( struct gwm_window
*window
, int *width
, int *height
);
153 void ( *replace_icons
)( struct gwm_window
*window
, int num_icons
,
154 int *widths
, int *heights
, uint32_t **icons
,
155 xcb_pixmap_t
*bitmaps
);
157 extern FORMAT( printf
, 1, 2 ) void warning( char *format
, ... ) {
161 va_start( val
, format
);
163 fprintf( stderr
, "%s: warning: ", argv0
);
164 vfprintf( stderr
, format
, val
);
165 putc( '\n', stderr
);
170 extern FORMAT( printf
, 1, 2 ) NORETURN
void fatal( char *format
, ... ) {
174 va_start( val
, format
);
176 fprintf( stderr
, "%s: ", argv0
);
177 vfprintf( stderr
, format
, val
);
178 putc( '\n', stderr
);
182 exit( 1 ); /* don't attempt an orderly shutdown */
185 extern MALLOC
void *xmalloc( size_t size
) {
189 /* It would be nice to clean up gracefully on allocation errors.
190 Unfortunately, doing so would require transmitting many requests
191 to the X server, which would require further allocations which
192 are likely to fail. Simply aborting with an error message is
193 therefore probably about as effective, and much simpler. */
195 if( !( p
= malloc( size
) ) ) {
204 extern void *xrealloc( void *p
, size_t size
) {
206 if( !( p
= realloc( p
, size
) ) ) {
215 extern MALLOC
void *xcalloc( size_t number
, size_t size
) {
219 if( !( p
= calloc( number
, size
) ) ) {
228 static void catch_signal( int n
) {
234 sa
.sa_handler
= SIG_DFL
;
235 sigemptyset( &sa
.sa_mask
);
237 sigaction( SIGHUP
, &sa
, NULL
);
238 sigaction( SIGINT
, &sa
, NULL
);
239 sigaction( SIGTERM
, &sa
, NULL
);
242 static void alarm_signal( int n
) {
247 static void child_signal( int n
) {
249 while( waitpid( -1, 0, WNOHANG
) > 0 )
253 static struct async_callback
{
254 unsigned int sequence
;
255 void ( *callback
)( unsigned int sequence
, void *reply
,
256 xcb_generic_error_t
*error
, union callback_param p
);
257 union callback_param p
;
258 struct async_callback
*next
;
259 } *queue_head
, *queue_tail
;
261 static void check_async_callbacks( void ) {
264 xcb_generic_error_t
*error
;
266 while( queue_head
&& xcb_poll_for_reply( c
, queue_head
->sequence
,
268 struct async_callback
*entry
= queue_head
;
270 if( !( queue_head
= entry
->next
) )
273 entry
->callback( entry
->sequence
, reply
, error
, entry
->p
);
279 extern void sync_with_callback( unsigned int sequence
) {
281 while( queue_head
&& sequence
- queue_head
->sequence
< UINT_MAX
>> 1 ) {
282 struct async_callback
*entry
= queue_head
;
283 xcb_generic_error_t
*error
;
284 void *reply
= xcb_wait_for_reply( c
, entry
->sequence
, &error
);
286 if( !( queue_head
= entry
->next
) )
289 entry
->callback( entry
->sequence
, reply
, error
, entry
->p
);
296 static void show_window( char *label
, xcb_window_t w
) {
298 /* We don't want to use lookup_window here, since mere debugging
299 output shouldn't trigger the side effect of finishing incomplete
301 struct gwm_window
*window
= table_lookup( &windows
, w
);
303 printf( " %s %08X (", label
, w
);
306 fputs( "None", stdout
);
308 struct gwm_window
*client
;
309 switch( window
->type
) {
311 printf( "root, screen %d", window
->screen
);
320 fputs( "frame, ", stdout
);
321 client
= window
->u
.frame
.child
;
325 fputs( "button, ", stdout
);
326 client
= window
->u
.button
.frame
->u
.frame
.child
;
330 fputs( "menu", stdout
);
334 case WINDOW_MENUITEM
:
335 fputs( "menu item", stdout
);
340 fputs( "fake", stdout
);
344 case WINDOW_FEEDBACK
:
345 printf( "feedback, screen %d", window
->screen
);
349 case WINDOW_INCOMPLETE
:
350 fputs( "incomplete", stdout
);
354 case WINDOW_CHILDLESS
:
355 fputs( "childless", stdout
);
361 fputs( client
->u
.managed
.name
? client
->u
.managed
.name
: "unnamed",
364 fputs( "unknown", stdout
);
370 static void show_atom( char *label
, xcb_atom_t atom
) {
372 static const char *const predefined_names
[ 69 ] = {
373 "None", "PRIMARY", "SECONDARY", "ARC", "ATOM", "BITMAP", "CARDINAL",
374 "COLORMAP", "CURSOR", "CUT_BUFFER0", "CUT_BUFFER1", "CUT_BUFFER2",
375 "CUT_BUFFER3", "CUT_BUFFER4", "CUT_BUFFER5", "CUT_BUFFER6",
376 "CUT_BUFFER7", "DRAWABLE", "FONT", "INTEGER", "PIXMAP", "POINT",
377 "RECTANGLE", "RESOURCE_MANAGER", "RGB_COLOR_MAP", "RGB_BEST_MAP",
378 "RGB_BLUE_MAP", "RGB_DEFAULT_MAP", "RGB_GRAY_MAP", "RGB_GREEN_MAP",
379 "RGB_RED_MAP", "STRING", "VISUALID", "WINDOW", "WM_COMMAND",
380 "WM_HINTS", "WM_CLIENT_MACHINE", "WM_ICON_NAME", "WM_ICON_SIZE",
381 "WM_NAME", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_ZOOM_HINTS",
382 "MIN_SPACE", "NORM_SPACE", "MAX_SPACE", "END_SPACE", "SUPERSCRIPT_X",
383 "SUPERSCRIPT_Y", "SUBSCRIPT_X", "SUBSCRIPT_Y", "UNDERLINE_POSITION",
384 "UNDERLINE_THICKNESS", "STRIKEOUT_ASCENT", "STRIKEOUT_DESCENT",
385 "ITALIC_ANGLE", "X_HEIGHT", "QUAD_WIDTH", "WEIGHT", "POINT_SIZE",
386 "RESOLUTION", "COPYRIGHT", "NOTICE", "FONT_NAME", "FAMILY_NAME",
387 "FULL_NAME", "CAP_HEIGHT", "WM_CLASS", "WM_TRANSIENT_FOR"
392 name
= predefined_names
[ atom
];
396 for( i
= 0; i
< NUM_ATOMS
; i
++ )
397 if( atoms
[ i
] == atom
) {
398 name
= atom_names
[ i
];
406 printf( " %s %d (%s)\n", label
, atom
, name
);
410 extern void show_error( xcb_generic_error_t
*error
) {
412 xcb_value_error_t
*verror
= (xcb_value_error_t
*) error
;
414 static const char *const type_strings
[ XCB_IMPLEMENTATION
+ 1 ] = {
415 "Unknown", "Request", "Value", "Window", "Pixmap", "Atom", "Cursor",
416 "Font", "Match", "Drawable", "Access", "Alloc", "Colormap", "GContext",
417 "IDChoice", "Name", "Length", "Implementation"
419 const char *type
, *bad_value_str
;
420 char num
[ 32 ], bad_num
[ 32 ];
422 if( error
->error_code
<= XCB_IMPLEMENTATION
)
423 type
= type_strings
[ error
->error_code
];
425 sprintf( num
, "%d", error
->error_code
);
429 switch( error
->error_code
) {
440 sprintf( bad_num
, ", val %X", verror
->bad_value
);
441 bad_value_str
= bad_num
;
450 if( have_extension
[ EXT_RENDER
] &&
451 error
->error_code
>= extension_error
[ EXT_RENDER
] &&
452 error
->error_code
<= extension_error
[ EXT_RENDER
] +
454 static const char *const render_strings
[ XCB_RENDER_GLYPH
+ 1 ] = {
455 "PictFormat", "Picture", "PictOp", "GlyphSet", "Glyph"
458 type
= render_strings
[ error
->error_code
-
459 extension_error
[ EXT_RENDER
] ];
463 warning( "unexpected error (type %s, sequence %d; opcode %d %d%s)", type
,
464 error
->sequence
, verror
->major_opcode
, verror
->minor_opcode
,
467 warning( "unexpected error (type %d, sequence %d; opcode %d %d)",
468 error
->error_code
, error
->sequence
,
469 verror
->major_opcode
, verror
->minor_opcode
);
474 static void show_event( xcb_generic_event_t
*generic
) {
476 static const char *const event_names
[ XCB_MAPPING_NOTIFY
+ 1 ] = {
477 "Error", "Reply", "KeyPress", "KeyRelease", "ButtonPress",
478 "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify",
479 "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExposure",
480 "NoExposure", "VisibilityNotify", "CreateNotify", "DestroyNotify",
481 "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify",
482 "ConfigureNotify", "ConfigureRequest", "GravityNotify",
483 "ResizeRequest", "CirculateNotify", "CirculateRequest",
484 "PropertyNotify", "SelectionClear", "SelectionRequest",
485 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify"
488 xcb_key_press_event_t kbm
; /* KeyPress, KeyRelease, ButtonPress,
489 ButtonRelease, MotionNotify */
490 xcb_enter_notify_event_t el
; /* EnterNotify, LeaveNotify */
491 xcb_focus_in_event_t f
; /* FocusIn, FocusOut */
492 xcb_keymap_notify_event_t km
; /* KeymapNotify */
493 xcb_expose_event_t e
; /* Expose */
494 xcb_graphics_exposure_event_t g
; /* GraphicsExposure */
495 xcb_no_exposure_event_t n
; /* NoExposure */
496 xcb_visibility_notify_event_t v
; /* VisibilityNotify */
497 xcb_create_notify_event_t c
; /* CreateNotify */
498 xcb_destroy_notify_event_t d
; /* DestroyNotify */
499 xcb_unmap_notify_event_t u
; /* UnmapNotify */
500 xcb_map_notify_event_t m
; /* MapNotify */
501 xcb_map_request_event_t mr
; /* MapRequest */
502 xcb_reparent_notify_event_t r
; /* ReparentNotify */
503 xcb_configure_notify_event_t con
; /* ConfigureNotify */
504 xcb_configure_request_event_t cr
; /* ConfigureRequest */
505 xcb_gravity_notify_event_t gn
; /* GravityNotify */
506 xcb_resize_request_event_t rr
; /* ResizeRequest */
507 xcb_circulate_notify_event_t cir
; /* CirculateNotify */
508 xcb_circulate_request_event_t cirr
; /* CirculateRequest */
509 xcb_property_notify_event_t p
; /* PropertyNotify */
510 xcb_selection_clear_event_t sc
; /* SelectionClear */
511 xcb_selection_request_event_t sr
; /* SelectionRequest */
512 xcb_selection_notify_event_t sn
; /* SelectionNotify */
513 xcb_colormap_notify_event_t cm
; /* ColormapNotify */
514 xcb_client_message_event_t cli
; /* ClientMessage */
515 xcb_mapping_notify_event_t map
; /* MappingNotify */
516 } *ev
= (union event
*) generic
;
517 int type
= generic
->response_type
& 0x7F;
520 /* FIXME handle extensions */
522 if( !( generic
->response_type
& ~SEND_EVENT_MASK
) ) {
523 show_error( (xcb_generic_error_t
*) generic
);
528 printf( "Event %d (%s%s)\n", generic
->response_type
,
529 type
<= XCB_MAPPING_NOTIFY
? event_names
[ type
] : "Unknown",
530 generic
->response_type
& SEND_EVENT_MASK
? ", synthetic" : "" );
532 if( type
< XCB_KEY_PRESS
|| type
> XCB_MAPPING_NOTIFY
)
535 if( type
!= XCB_KEYMAP_NOTIFY
)
536 printf( " Sequence %X\n", generic
->full_sequence
);
540 case XCB_KEY_RELEASE
:
541 case XCB_BUTTON_PRESS
:
542 case XCB_BUTTON_RELEASE
:
543 case XCB_MOTION_NOTIFY
:
544 show_window( "Root", ev
->kbm
.root
);
545 show_window( "Event", ev
->kbm
.event
);
546 show_window( "Child", ev
->kbm
.child
);
547 printf( " Same-screen %s\n"
549 " Event x/y %d, %d\n"
553 ev
->kbm
.same_screen
? "Yes" : "No",
554 ev
->kbm
.root_x
, ev
->kbm
.root_y
,
555 ev
->kbm
.event_x
, ev
->kbm
.event_y
,
556 ev
->kbm
.detail
, ev
->kbm
.state
, ev
->kbm
.time
);
559 case XCB_ENTER_NOTIFY
:
560 case XCB_LEAVE_NOTIFY
:
561 show_window( "Root", ev
->el
.root
);
562 show_window( "Event", ev
->el
.event
);
563 show_window( "Child", ev
->el
.child
);
564 printf( " Same-screen %s\n"
566 " Event x/y %d, %d\n"
572 ev
->el
.same_screen_focus
& 2 ? "Yes" : "No",
573 ev
->el
.root_x
, ev
->el
.root_y
,
574 ev
->el
.event_x
, ev
->el
.event_y
,
575 ev
->el
.mode
, ev
->el
.detail
, ev
->el
.same_screen_focus
& 1,
576 ev
->el
.state
, ev
->el
.time
);
581 show_window( "Event", ev
->f
.event
);
584 ev
->f
.mode
, ev
->f
.detail
);
586 case XCB_KEYMAP_NOTIFY
:
587 for( i
= 0; i
< 0xF8; i
++ )
588 if( ev
->km
.keys
[ i
>> 3 ] & ( 1 << ( i
& 7 ) ) )
589 printf( " key %d down\n", i
);
594 show_window( "Window", ev
->e
.window
);
595 printf( " x/y %d, %d\n"
596 " width/height %d x %d\n"
598 ev
->e
.x
, ev
->e
.y
, ev
->e
.width
, ev
->e
.height
, ev
->e
.count
);
601 case XCB_GRAPHICS_EXPOSURE
:
602 show_window( "Drawable", ev
->g
.drawable
);
603 printf( " x/y %d, %d\n"
604 " width/height %d x %d\n"
607 ev
->g
.x
, ev
->g
.y
, ev
->g
.width
, ev
->g
.height
, ev
->g
.count
,
608 ev
->g
.major_opcode
, ev
->g
.minor_opcode
);
611 case XCB_NO_EXPOSURE
:
612 show_window( "Drawable", ev
->n
.drawable
);
613 printf( " Opcode %d, %d\n",
614 ev
->n
.major_opcode
, ev
->n
.minor_opcode
);
617 case XCB_VISIBILITY_NOTIFY
:
618 show_window( "Window", ev
->v
.window
);
619 printf( " State %d\n", ev
->v
.state
);
622 case XCB_CREATE_NOTIFY
:
623 show_window( "Parent", ev
->c
.parent
);
624 show_window( "Window", ev
->c
.window
);
625 printf( " x/y %d, %d\n"
626 " width/height %d x %d\n"
628 " Override redirect %s\n",
630 ev
->c
.width
, ev
->c
.height
,
632 ev
->c
.override_redirect
? "Yes" : "No" );
635 case XCB_DESTROY_NOTIFY
:
636 show_window( "Event", ev
->d
.event
);
637 show_window( "Window", ev
->d
.window
);
640 case XCB_UNMAP_NOTIFY
:
641 show_window( "Event", ev
->u
.event
);
642 show_window( "Window", ev
->u
.window
);
643 printf( " From configure: %s\n",
644 ev
->u
.from_configure
? "Yes" : "No" );
648 show_window( "Event", ev
->m
.event
);
649 show_window( "Window", ev
->m
.window
);
650 printf( " Override redirect: %s\n",
651 ev
->m
.override_redirect
? "Yes" : "No" );
654 case XCB_MAP_REQUEST
:
655 show_window( "Parent", ev
->mr
.parent
);
656 show_window( "Window", ev
->mr
.window
);
659 case XCB_REPARENT_NOTIFY
:
660 show_window( "Event", ev
->r
.event
);
661 show_window( "Window", ev
->r
.window
);
662 show_window( "Parent", ev
->r
.parent
);
663 printf( " x/y %d, %d\n"
664 " Override redirect %s\n",
666 ev
->r
.override_redirect
? "Yes" : "No" );
669 case XCB_CONFIGURE_NOTIFY
:
670 show_window( "Event", ev
->con
.event
);
671 show_window( "Window", ev
->con
.window
);
672 printf( " x/y %d, %d\n"
673 " width/height %d x %d\n"
674 " Border width %d\n",
675 ev
->con
.x
, ev
->con
.y
,
676 ev
->con
.width
, ev
->con
.height
,
677 ev
->con
.border_width
);
678 show_window( "Above sibling", ev
->con
.above_sibling
);
679 printf( " Override redirect %s\n",
680 ev
->con
.override_redirect
? "Yes" : "No" );
683 case XCB_CONFIGURE_REQUEST
:
684 show_window( "Parent", ev
->cr
.parent
);
685 show_window( "Window", ev
->cr
.window
);
686 printf( " x/y %d, %d\n"
687 " width/height %d x %d\n"
688 " Border width %d\n",
690 ev
->cr
.width
, ev
->cr
.height
,
691 ev
->cr
.border_width
);
692 show_window( "Sibling", ev
->cr
.sibling
);
693 printf( " Stack mode %d\n"
694 " Value mask %04X\n",
695 ev
->cr
.stack_mode
, ev
->cr
.value_mask
);
698 case XCB_GRAVITY_NOTIFY
:
699 show_window( "Event", ev
->gn
.event
);
700 show_window( "Window", ev
->gn
.window
);
701 printf( " x/y %d, %d\n",
702 ev
->gn
.x
, ev
->gn
.y
);
705 case XCB_RESIZE_REQUEST
:
706 show_window( "Window", ev
->rr
.window
);
707 printf( " width/height %d x %d\n",
708 ev
->rr
.width
, ev
->rr
.height
);
711 case XCB_CIRCULATE_NOTIFY
:
712 show_window( "Event", ev
->cir
.event
);
713 show_window( "Window", ev
->cir
.window
);
714 printf( " Place %d\n", ev
->cir
.place
);
717 case XCB_CIRCULATE_REQUEST
:
718 show_window( "Parent", ev
->cirr
.event
);
719 show_window( "Window", ev
->cirr
.window
);
720 printf( " Place %d\n", ev
->cirr
.place
);
723 case XCB_PROPERTY_NOTIFY
:
724 show_window( "Window", ev
->p
.window
);
725 show_atom( "Atom", ev
->p
.atom
);
726 printf( " State %d\n"
728 ev
->p
.state
, ev
->p
.time
);
731 case XCB_SELECTION_CLEAR
:
732 show_window( "Owner", ev
->sc
.owner
);
733 show_atom( "Selection", ev
->sc
.selection
);
734 printf( " Time %d\n", ev
->sc
.time
);
737 case XCB_SELECTION_REQUEST
:
738 show_window( "Owner", ev
->sr
.owner
);
739 show_atom( "Selection", ev
->sr
.selection
);
740 show_atom( "Target", ev
->sr
.target
);
741 show_atom( "Property", ev
->sr
.property
);
742 show_window( "Requestor", ev
->sr
.requestor
);
743 printf( " Time %d\n", ev
->sr
.time
);
746 case XCB_SELECTION_NOTIFY
:
747 show_window( "Requestor", ev
->sn
.requestor
);
748 show_atom( "Selection", ev
->sn
.selection
);
749 show_atom( "Target", ev
->sn
.target
);
750 show_atom( "Property", ev
->sn
.property
);
751 printf( " Time %d\n", ev
->sn
.time
);
754 case XCB_COLORMAP_NOTIFY
:
755 show_window( "Window", ev
->cm
.window
);
756 printf( " Colormap %d\n"
760 ev
->cm
._new
? "Yes" : "No",
764 case XCB_CLIENT_MESSAGE
:
765 show_window( "Window", ev
->cli
.window
);
766 show_atom( "Type", ev
->cli
.type
);
767 printf( " Format %d\n", ev
->cli
.format
);
769 switch( ev
->cli
.format
) {
771 for( i
= 0; i
< 20; i
++ )
772 printf( " %2d %02X\n", i
, ev
->cli
.data
.data8
[ i
] );
776 for( i
= 0; i
< 10; i
++ )
777 printf( " %d %04X\n", i
, ev
->cli
.data
.data16
[ i
] );
781 for( i
= 0; i
< 5; i
++ )
782 printf( " %d %08X\n", i
, ev
->cli
.data
.data32
[ i
] );
788 case XCB_MAPPING_NOTIFY
:
789 printf( " Request %d\n", ev
->map
.request
);
790 if( ev
->map
.request
== XCB_MAPPING_KEYBOARD
)
791 printf( " First keycode %d\n"
793 ev
->map
.first_keycode
, ev
->map
.count
);
799 extern xcb_generic_event_t
*wait_for_event( void ) {
801 xcb_generic_event_t
*ev
;
802 sigset_t sigs
, oldsigs
;
803 int fd
= xcb_get_file_descriptor( c
);
811 if( xcb_connection_has_error( c
) )
814 ev
= xcb_poll_for_event( c
);
817 if( flag_debug
&& ev
)
821 check_async_callbacks(); /* must poll for events first, since
822 polling replies won't dequeue them */
827 if( update_windows
.used
) {
828 struct gwm_window
*update
= update_windows
.values
[ 0 ];
830 update_window( update
);
831 window_update_done( update
);
838 sigemptyset( &sigs
);
839 sigaddset( &sigs
, SIGHUP
);
840 sigaddset( &sigs
, SIGINT
);
841 sigaddset( &sigs
, SIGTERM
);
843 sigprocmask( SIG_BLOCK
, &sigs
, &oldsigs
);
845 if( signal_caught
) {
846 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
855 if( ppoll( &pfd
, 1, NULL
, &oldsigs
) < 0 && errno
!= EINTR
)
861 if( pselect( fd
+ 1, &fds
, NULL
, NULL
, NULL
, &oldsigs
) < 0 &&
866 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
870 extern void handle_async_reply( unsigned int sequence
,
871 void ( *callback
)( unsigned int sequence
,
873 xcb_generic_error_t
*error
,
874 union callback_param p
),
875 union callback_param p
) {
877 struct async_callback
*entry
= xmalloc( sizeof *entry
);
879 entry
->sequence
= sequence
;
880 entry
->callback
= callback
;
885 queue_tail
= queue_tail
->next
= entry
;
887 queue_head
= queue_tail
= entry
;
890 static void error_callback( unsigned int sequence
, void *reply
,
891 xcb_generic_error_t
*error
,
892 union callback_param p
) {
894 unsigned ignore
= p
.l
;
900 return; /* no error */
902 if( !( ignore
& ( 1 << ( error
->error_code
- 1 ) ) ) )
903 show_error( error
); /* an error we didn't expect */
908 extern void handle_error_reply( xcb_void_cookie_t cookie
, unsigned ignore
) {
910 union callback_param p
;
914 handle_async_reply( cookie
.sequence
, error_callback
, p
);
917 static void install_colormap( int screen
, xcb_colormap_t cmap
,
918 xcb_timestamp_t t
) {
920 if( (int) t
- (int) gwm_screens
[ screen
].cmap_time
> 0 ) {
921 gwm_screens
[ screen
].cmap_time
= t
;
923 if( gwm_screens
[ screen
].cmap
!= cmap
)
924 xcb_install_colormap( c
, gwm_screens
[ screen
].cmap
= cmap
);
928 struct cmap_callback
{
933 static void handle_get_window_attributes( unsigned int sequence
, void *reply
,
934 xcb_generic_error_t
*error
,
935 union callback_param cp
) {
937 struct cmap_callback
*cc
= cp
.p
;
940 /* Ignore Window errors, since we were given the ID by a client
941 and we don't trust them to get it right. */
942 if( error
->error_code
!= XCB_WINDOW
)
949 xcb_get_window_attributes_reply_t
*r
= reply
;
951 install_colormap( cc
->screen
, r
->colormap
? r
->colormap
:
952 screens
[ cc
->screen
]->default_colormap
, cc
->t
);
960 /* Install a window's colormap on a screen, or the default colormap if
962 extern void install_window_colormap( int screen
, struct gwm_window
*window
,
963 xcb_timestamp_t t
) {
965 assert( !window
|| window
->type
== WINDOW_MANAGED
);
967 if( window
&& window
->u
.managed
.cmap_window
) {
968 union callback_param cp
;
969 struct cmap_callback
*cc
= xmalloc( sizeof *cc
);
974 handle_async_reply( xcb_get_window_attributes(
975 c
, window
->u
.managed
.cmap_window
).sequence
,
976 handle_get_window_attributes
, cp
);
978 install_colormap( screen
, window
&& window
->u
.managed
.cmap
?
979 window
->u
.managed
.cmap
:
980 screens
[ screen
]->default_colormap
, t
);
983 /* Return TRUE iff an event is the only button pressed. */
984 extern CONST
int initial_press( xcb_button_press_event_t
*ev
) {
986 return !( ev
->state
& 0x1F00 );
989 /* Return TRUE iff an event is the release of the last button (and so would
990 terminate a passive grab). */
991 extern CONST
int final_release( xcb_button_release_event_t
*ev
) {
993 return ( ev
->state
& 0x1F00 ) == ( 0x80 << ev
->detail
);
996 static void stop_listening( struct gwm_window
*window
) {
1000 /* Ignore Window errors on the child, because it might be destroyed
1001 before we are notified. */
1004 handle_error_reply( xcb_change_window_attributes_checked(
1005 c
, window
->w
, XCB_CW_EVENT_MASK
, &value
),
1009 if( have_extension
[ EXT_SHAPE
] )
1010 handle_error_reply( xcb_shape_select_input_checked( c
, window
->w
,
1016 static void place_window( struct gwm_window
*window
, int *x
, int *y
,
1017 int width
, int height
) {
1019 int sx
= screens
[ window
->screen
]->width_in_pixels
- width
;
1020 int sy
= screens
[ window
->screen
]->height_in_pixels
- height
;
1021 #if defined( UINT64_MAX ) || defined( uint64_t )
1030 hash
= window
->w
* 0x9B4A36D1;
1031 #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL
1040 hash
= window
->w
* 0xA6E34925;
1041 #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL
1048 static void start_managing_window( struct gwm_window
*window
,
1049 xcb_get_window_attributes_reply_t
*attr
,
1050 xcb_get_geometry_reply_t
*geom
,
1052 xcb_shape_query_extents_reply_t
*shape
,
1054 xcb_get_property_reply_t
*props
[],
1058 uint32_t values
[ 4 ];
1059 struct gwm_window
*frame
, *button
;
1060 xcb_window_t w
= window
->w
;
1062 frame
= add_window( xcb_generate_id( c
) );
1063 button
= add_window( xcb_generate_id( c
) );
1064 window
->screen
= lookup_window( geom
->root
)->screen
;
1065 window
->type
= WINDOW_MANAGED
;
1066 window
->u
.managed
.frame
= frame
;
1067 window
->u
.managed
.border_width
= geom
->border_width
;
1068 window
->u
.managed
.cmap
= attr
->colormap
;
1069 window
->u
.managed
.hints
= 0;
1071 window
->u
.managed
.full_icons
= window
->u
.managed
.menu_icons
= NULL
;
1072 window
->u
.managed
.net_wm_icon
= FALSE
;
1074 window
->u
.managed
.name
= NULL
;
1075 window
->u
.managed
.net_wm_name
= FALSE
;
1076 window
->u
.managed
.state
= STATE_WITHDRAWN
;
1078 window
->u
.managed
.shaped
= have_extension
[ EXT_SHAPE
] && shape
&&
1079 shape
->bounding_shaped
;
1081 frame
->screen
= window
->screen
;
1082 frame
->type
= WINDOW_FRAME
;
1083 frame
->u
.frame
.child
= window
;
1084 frame
->u
.frame
.button
= button
;
1085 frame
->u
.frame
.decoration
= DEC_DEFAULT
;
1086 for( i
= 0; i
< NUM_BORDER_REGIONS
; i
++ )
1087 frame
->u
.frame
.border_regions
[ i
] = xcb_generate_id( c
);
1088 button
->screen
= window
->screen
;
1089 button
->type
= WINDOW_BUTTON
;
1090 button
->u
.button
.frame
= frame
;
1092 for( i
= 0; i
< NUM_PROPS
; i
++ )
1093 managed_property_change( window
, i
, props
[ i
] );
1095 translate_child_to_frame( frame
, &frame
->u
.frame
.x
, &frame
->u
.frame
.y
,
1096 &frame
->u
.frame
.width
, &frame
->u
.frame
.height
,
1097 geom
->x
, geom
->y
, geom
->width
, geom
->height
,
1099 window
->u
.managed
.win_gravity
);
1101 if( map_request
&& !( window
->u
.managed
.hints
& HINT_POSITION
) ) {
1104 place_window( window
, &x
, &y
,
1105 frame
->u
.frame
.width
, frame
->u
.frame
.height
);
1107 frame
->u
.frame
.x
= x
;
1108 frame
->u
.frame
.y
= y
;
1111 /* Don't create frames entirely off-screen; ensure that at least 8 pixels
1112 are within the root. */
1113 if( frame
->u
.frame
.x
> screens
[ frame
->screen
]->width_in_pixels
- 8 &&
1114 frame
->u
.frame
.x
+ frame
->u
.frame
.width
>
1115 screens
[ frame
->screen
]->width_in_pixels
)
1116 frame
->u
.frame
.x
= screens
[ frame
->screen
]->width_in_pixels
- 8;
1117 else if( frame
->u
.frame
.x
< 0 &&
1118 frame
->u
.frame
.x
+ frame
->u
.frame
.width
< 8 )
1119 frame
->u
.frame
.x
= 8 - frame
->u
.frame
.width
;
1121 if( frame
->u
.frame
.y
> screens
[ frame
->screen
]->height_in_pixels
- 8 &&
1122 frame
->u
.frame
.y
+ frame
->u
.frame
.height
>
1123 screens
[ frame
->screen
]->height_in_pixels
)
1124 frame
->u
.frame
.y
= screens
[ frame
->screen
]->height_in_pixels
- 8;
1125 else if( frame
->u
.frame
.y
< 0 &&
1126 frame
->u
.frame
.y
+ frame
->u
.frame
.height
< 8 )
1127 frame
->u
.frame
.y
= 8 - frame
->u
.frame
.height
;
1129 values
[ 0 ] = gwm_screens
[ frame
->screen
].pixels
[
1130 COL_FRAME_INACTIVE
]; /* background pixel */
1131 values
[ 1 ] = gwm_screens
[ frame
->screen
].pixels
[
1132 COL_BORDER
]; /* border pixel */
1133 values
[ 2 ] = XCB_GRAVITY_NORTH_WEST
; /* bit gravity */
1134 values
[ 3 ] = TRUE
; /* override redirect */
1135 values
[ 4 ] = XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
|
1136 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_POINTER_MOTION_HINT
|
1137 XCB_EVENT_MASK_BUTTON_MOTION
| XCB_EVENT_MASK_EXPOSURE
|
1138 XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
1139 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
|
1140 XCB_EVENT_MASK_OWNER_GRAB_BUTTON
;
1142 xcb_create_window( c
, XCB_COPY_FROM_PARENT
, frame
->w
, geom
->root
,
1143 frame
->u
.frame
.x
, frame
->u
.frame
.y
,
1144 frame
->u
.frame
.width
, frame
->u
.frame
.height
,
1145 frame_xb( frame
), XCB_WINDOW_CLASS_INPUT_OUTPUT
,
1146 XCB_COPY_FROM_PARENT
,
1147 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
|
1148 XCB_CW_BIT_GRAVITY
| XCB_CW_OVERRIDE_REDIRECT
|
1149 XCB_CW_EVENT_MASK
, values
);
1151 values
[ 0 ] = gwm_screens
[ frame
->screen
].pixels
[
1152 COL_BUTTON_INACTIVE
]; /* background pixel */
1153 values
[ 1 ] = gwm_screens
[ frame
->screen
].pixels
[
1154 COL_BORDER
]; /* border pixel */
1155 values
[ 2 ] = TRUE
; /* override redirect */
1156 values
[ 3 ] = XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
|
1157 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
|
1158 XCB_EVENT_MASK_EXPOSURE
| XCB_EVENT_MASK_OWNER_GRAB_BUTTON
;
1159 xcb_create_window( c
, XCB_COPY_FROM_PARENT
, button
->w
, frame
->w
,
1160 2, 1, button_size( button
, FALSE
),
1161 button_size( button
, FALSE
), button_xb( button
),
1162 XCB_WINDOW_CLASS_INPUT_OUTPUT
, XCB_COPY_FROM_PARENT
,
1163 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
|
1164 XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
, values
);
1166 if( frame
->u
.frame
.decoration
& DEC_TITLE
)
1167 xcb_map_window( c
, button
->w
);
1169 for( i
= 0; i
< NUM_BORDER_REGIONS
; i
++ ) {
1170 values
[ 0 ] = cursors
[ i
< BR_R
? i
: i
+ 1 ];
1171 /* Temporary positions: these windows will be moved, sized and
1172 mapped if necessary in the frame ConfigureNotify handler. */
1173 xcb_create_window( c
, 0, frame
->u
.frame
.border_regions
[ i
], frame
->w
,
1174 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY
,
1175 XCB_COPY_FROM_PARENT
, XCB_CW_CURSOR
, values
);
1178 xcb_change_save_set( c
, XCB_SET_MODE_INSERT
, w
);
1180 if( !map_request
) {
1181 /* If the window was mapped already, then reparenting it will
1182 generate an UnmapNotify. To avoid race conditions in discarding
1183 this unwanted event, temporarily reduce our event mask. */
1184 xcb_grab_server( c
);
1186 values
[ 0 ] = XCB_EVENT_MASK_PROPERTY_CHANGE
|
1187 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1188 xcb_change_window_attributes( c
, w
, XCB_CW_EVENT_MASK
, values
);
1191 xcb_reparent_window( c
, w
, frame
->w
, frame_l( frame
, FALSE
),
1192 frame_t( frame
, FALSE
) );
1194 if( !map_request
) {
1195 values
[ 0 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
1196 XCB_EVENT_MASK_PROPERTY_CHANGE
|
1197 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1198 xcb_change_window_attributes( c
, w
, XCB_CW_EVENT_MASK
, values
);
1200 xcb_ungrab_server( c
);
1203 if( geom
->border_width
) {
1205 xcb_configure_window( c
, w
, XCB_CONFIG_WINDOW_BORDER_WIDTH
, values
);
1209 if( window
->u
.managed
.shaped
)
1210 match_managed_shape( window
);
1213 /* Tell the client we've relocated their window with respect to the
1214 root (see ICCCM 2.0, section 4.2.3); we almost certainly have,
1215 between reparenting it and possibly applying a placement policy. */
1216 synthetic_configure_notify( frame
);
1218 set_managed_state( window
, map_request
&&
1219 ( window
->u
.managed
.hints
& HINT_ICONIC
) ?
1220 STATE_ICONIC
: STATE_NORMAL
);
1222 if( window
->u
.managed
.state
== STATE_NORMAL
) {
1224 xcb_map_window( c
, w
);
1226 xcb_map_window( c
, frame
->w
);
1229 /* An ugly hack to get the button cursor properly set now that
1230 the button has been created. */
1231 managed_property_change( window
, PROP_WM_PROTOCOLS
,
1232 props
[ PROP_WM_PROTOCOLS
] );
1236 struct gwm_window
*window
; /* type WINDOW_INCOMPLETE */
1238 xcb_get_geometry_cookie_t geometry_cookie
;
1240 xcb_shape_query_extents_cookie_t shape_cookie
;
1242 xcb_get_property_cookie_t prop_cookies
[ NUM_PROPS
];
1245 static void handle_manage_window( unsigned int sequence
, void *reply
,
1246 xcb_generic_error_t
*error
,
1247 union callback_param cp
) {
1249 struct new_window
*nw
= cp
.p
;
1251 xcb_get_window_attributes_reply_t
*attr
= reply
;
1252 xcb_get_geometry_reply_t
*geom
= xcb_get_geometry_reply(
1253 c
, nw
->geometry_cookie
, NULL
);
1255 xcb_shape_query_extents_reply_t
*shape
= have_extension
[ EXT_SHAPE
] ?
1256 xcb_shape_query_extents_reply( c
, nw
->shape_cookie
, NULL
) : NULL
;
1258 xcb_get_property_reply_t
*props
[ NUM_PROPS
];
1261 for( i
= 0, have_all_props
= TRUE
; i
< NUM_PROPS
; i
++ )
1262 if( !( props
[ i
] = xcb_get_property_reply( c
, nw
->prop_cookies
[ i
],
1264 have_all_props
= FALSE
;
1267 show_error( error
);
1272 if( attr
&& geom
&& have_all_props
&&
1273 attr
->_class
== XCB_WINDOW_CLASS_INPUT_OUTPUT
&&
1274 ( nw
->map_request
|| ( attr
->map_state
== XCB_MAP_STATE_VIEWABLE
&&
1275 !attr
->override_redirect
) ) )
1276 start_managing_window( nw
->window
, attr
, geom
,
1280 props
, nw
->map_request
);
1282 stop_listening( nw
->window
);
1283 forget_window( nw
->window
);
1297 for( i
= 0; i
< NUM_PROPS
; i
++ )
1304 extern void manage_window( xcb_window_t w
, int map_request
) {
1306 struct new_window
*nw
= xmalloc( sizeof *nw
);
1307 uint32_t values
[ 2 ];
1309 union callback_param cp
;
1312 if( !( nw
->window
= add_window( w
) ) ) {
1313 /* The window already exists. This can happen if a client attempts
1314 more than one MapWindow before we've handle the first one, and
1315 so we receive multiple MapRequests. */
1320 /* Our request for notification of events concerning the client
1321 window must be atomic with our query of its state (otherwise
1322 there will exist some interval where we will be unaware of updates,
1323 or receive notifications for updates which were already present in
1324 the originally queried state). Unfortunately this means that we're
1325 forced to receive events before we even know whether we're interested
1326 in managing the window. If we subsequently discover that we
1327 didn't want the events after all, we will call stop_listening()
1328 (below), and any corresponding events for the unknown window
1329 will be silently ignored. */
1330 xcb_grab_server( c
);
1332 nw
->map_request
= map_request
;
1333 nw
->geometry_cookie
= xcb_get_geometry( c
, w
);
1335 if( have_extension
[ EXT_SHAPE
] )
1336 nw
->shape_cookie
= xcb_shape_query_extents( c
, w
);
1338 for( i
= 0; i
< NUM_PROPS
; i
++ )
1339 nw
->prop_cookies
[ i
] = xcb_get_property( c
, FALSE
, w
, prop_atoms
[ i
],
1343 seq
= xcb_get_window_attributes( c
, w
).sequence
;
1345 values
[ 0 ] = XCB_GRAVITY_NORTH_WEST
;
1346 values
[ 1 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
1347 XCB_EVENT_MASK_PROPERTY_CHANGE
|
1348 XCB_EVENT_MASK_COLOR_MAP_CHANGE
;
1349 xcb_change_window_attributes( c
, w
, XCB_CW_WIN_GRAVITY
| XCB_CW_EVENT_MASK
,
1353 if( have_extension
[ EXT_SHAPE
] )
1354 xcb_shape_select_input( c
, w
, TRUE
);
1357 xcb_ungrab_server( c
);
1360 handle_async_reply( seq
, handle_manage_window
, cp
);
1362 nw
->window
->type
= WINDOW_INCOMPLETE
;
1363 nw
->window
->u
.incomplete
.sequence
= seq
;
1366 static void handle_destroy_window( unsigned int sequence
, void *reply
,
1367 xcb_generic_error_t
*error
,
1368 union callback_param cp
) {
1374 show_error( error
);
1379 forget_window( cp
.p
);
1382 extern void unmanage_window( struct gwm_window
*window
) {
1384 xcb_window_t w
= window
->w
;
1385 struct gwm_window
*frame
= window
->u
.managed
.frame
;
1386 uint32_t border_width
= window
->u
.managed
.border_width
;
1388 union callback_param cp
;
1390 if( focus_frame
== frame
)
1393 stop_listening( window
);
1395 /* Ignore Window errors on the child, because it might be destroyed
1396 before we are notified. */
1398 handle_error_reply( xcb_change_save_set_checked(
1399 c
, XCB_SET_MODE_DELETE
, w
), ERR_MASK_WINDOW
);
1401 /* Reparent the window back to the root; delete its WM_STATE
1402 property (see ICCCM 4.1.4); and map it if it was iconic. */
1403 translate_frame_to_child( frame
, &x
, &y
, frame
->u
.frame
.x
, frame
->u
.frame
.y
,
1404 border_width
, window
->u
.managed
.win_gravity
);
1405 handle_error_reply( xcb_reparent_window_checked(
1406 c
, w
, screens
[ window
->screen
]->root
,
1407 x
, y
), ERR_MASK_WINDOW
);
1409 handle_error_reply( xcb_delete_property_checked(
1410 c
, w
, atoms
[ ATOM_WM_STATE
] ), ERR_MASK_WINDOW
);
1412 handle_error_reply( xcb_configure_window_checked(
1413 c
, w
, XCB_CONFIG_WINDOW_BORDER_WIDTH
,
1414 &border_width
), ERR_MASK_WINDOW
);
1416 if( window
->u
.managed
.state
== STATE_ICONIC
)
1417 handle_error_reply( xcb_map_window_checked( c
, w
), ERR_MASK_WINDOW
);
1420 handle_async_reply( xcb_destroy_window_checked( c
, frame
->w
).sequence
,
1421 handle_destroy_window
, cp
);
1423 if( window
->u
.managed
.name
)
1424 free( window
->u
.managed
.name
);
1426 replace_icons( window
, 0, NULL
, NULL
, NULL
, NULL
);
1428 forget_window( window
);
1429 forget_window( frame
->u
.frame
.button
);
1431 /* We can't completely dismiss the frame window yet. Until our
1432 DestroyWindow is handled, other events might still arrive for
1433 it: in particular, we might see a MapRequest indicating a
1434 transition back to the Normal state before the adoption by the
1435 root has occurred. */
1436 frame
->type
= WINDOW_CHILDLESS
;
1439 extern void generic_expose( struct gwm_window
*window
,
1440 xcb_expose_event_t
*ev
) {
1442 queue_window_update( window
, ev
->x
, ev
->y
, ev
->width
, ev
->height
, TRUE
);
1445 xcb_window_t pointer_demux
;
1447 static void handle_fake_change_property( unsigned int sequence
, void *reply
,
1448 xcb_generic_error_t
*error
,
1449 union callback_param cp
) {
1451 xcb_selection_notify_event_t
*p
= cp
.p
;
1458 p
->property
= XCB_NONE
;
1461 handle_error_reply( xcb_send_event_checked( c
, FALSE
, p
->requestor
, 0,
1462 (char *) p
), ERR_MASK_WINDOW
);
1467 static void fake_selection_request( struct gwm_window
*window
,
1468 xcb_selection_request_event_t
*ev
) {
1470 if( ev
->time
!= XCB_CURRENT_TIME
&& ev
->time
< window
->u
.fake
.timestamp
&&
1471 ev
->target
== atoms
[ ATOM_VERSION
] ) {
1472 /* ICCCM version identification: ICCCM 2.0, section 4.3. */
1473 static const uint32_t values
[ 2 ] = { 2, 0 };
1474 struct xcb_selection_notify_event_t
*p
= xmalloc( sizeof *p
);
1475 union callback_param cp
;
1477 p
->requestor
= ev
->requestor
;
1478 p
->selection
= ev
->selection
;
1479 p
->target
= ev
->target
;
1480 p
->property
= ev
->property
;
1483 handle_async_reply( xcb_change_property_checked(
1484 c
, XCB_PROP_MODE_REPLACE
, ev
->requestor
,
1485 atoms
[ ATOM_VERSION
], INTEGER
, 32, 2,
1486 values
).sequence
, handle_fake_change_property
,
1489 struct xcb_selection_notify_event_t
not;
1491 not.requestor
= ev
->requestor
;
1492 not.selection
= ev
->selection
;
1493 not.target
= ev
->target
;
1494 not.property
= XCB_NONE
;
1495 not.time
= ev
->time
;
1497 handle_error_reply( xcb_send_event_checked( c
, FALSE
, not.requestor
,
1503 static const event_handler fake_handlers
[] = {
1506 NULL
, /* KeyPress */
1507 NULL
, /* KeyRelease */
1508 NULL
, /* ButtonPress */
1509 NULL
, /* ButtonRelease */
1510 NULL
, /* MotionNotify */
1511 NULL
, /* EnterNotify */
1512 NULL
, /* LeaveNotify */
1514 NULL
, /* FocusOut */
1515 NULL
, /* KeymapNotify */
1517 NULL
, /* GraphicsExpose */
1518 NULL
, /* NoExposure */
1519 NULL
, /* VisibilityNotify */
1520 NULL
, /* CreateNotify */
1521 NULL
, /* DestroyNotify */
1522 NULL
, /* UnmapNotify */
1523 NULL
, /* MapNotify */
1524 NULL
, /* MapRequest */
1525 NULL
, /* ReparentNotify */
1526 NULL
, /* ConfigureNotify */
1527 NULL
, /* ConfigureRequest */
1528 NULL
, /* GravityNotify */
1529 NULL
, /* ResizeRequest */
1530 NULL
, /* CirculateNotify */
1531 NULL
, /* CirculateRequest */
1532 NULL
, /* PropertyNotify */
1533 NULL
, /* SelectionClear */
1534 (event_handler
) fake_selection_request
, /* SelectionRequest */
1535 NULL
, /* SelectionNotify */
1536 NULL
, /* ColormapNotify */
1537 NULL
, /* ClientMessage */
1538 NULL
, /* MappingNotify */
1539 NULL
, /* (synthetic) */
1540 NULL
/* ShapeNotify */
1541 }, feedback_handlers
[] = {
1544 NULL
, /* KeyPress */
1545 NULL
, /* KeyRelease */
1546 NULL
, /* ButtonPress */
1547 NULL
, /* ButtonRelease */
1548 NULL
, /* MotionNotify */
1549 NULL
, /* EnterNotify */
1550 NULL
, /* LeaveNotify */
1552 NULL
, /* FocusOut */
1553 NULL
, /* KeymapNotify */
1554 (event_handler
) generic_expose
,
1555 NULL
, /* GraphicsExpose */
1556 NULL
, /* NoExposure */
1557 NULL
, /* VisibilityNotify */
1558 NULL
, /* CreateNotify */
1559 NULL
, /* DestroyNotify */
1560 NULL
, /* UnmapNotify */
1561 NULL
, /* MapNotify */
1562 NULL
, /* MapRequest */
1563 NULL
, /* ReparentNotify */
1564 NULL
, /* ConfigureNotify */
1565 NULL
, /* ConfigureRequest */
1566 NULL
, /* GravityNotify */
1567 NULL
, /* ResizeRequest */
1568 NULL
, /* CirculateNotify */
1569 NULL
, /* CirculateRequest */
1570 NULL
, /* PropertyNotify */
1571 NULL
, /* SelectionClear */
1572 NULL
, /* SelectionRequest */
1573 NULL
, /* SelectionNotify */
1574 NULL
, /* ColormapNotify */
1575 NULL
, /* ClientMessage */
1576 NULL
, /* MappingNotify */
1577 NULL
/* (synthetic) */
1580 static const event_handler
*const handlers
[] = {
1581 root_handlers
, managed_handlers
, frame_handlers
, button_handlers
,
1582 menu_handlers
, menuitem_handlers
, fake_handlers
, feedback_handlers
, NULL
,
1586 static void setup_display( void ) {
1589 xcb_intern_atom_cookie_t atom_cookies
[ NUM_ATOMS
];
1590 const xcb_setup_t
*setup
;
1591 xcb_screen_iterator_t iter
;
1592 xcb_intern_atom_cookie_t
*screen_atom_cookies
;
1594 xcb_get_selection_owner_cookie_t
*screen_owner_cookies
;
1595 xcb_window_t
*screen_owners
;
1596 xcb_client_message_event_t msg
;
1597 xcb_void_cookie_t
*cookies
;
1598 xcb_query_tree_cookie_t
*tree_cookies
;
1600 table_init( &windows
);
1601 table_init( &update_windows
);
1603 c
= xcb_connect( NULL
, NULL
);
1605 if( xcb_connection_has_error( c
) )
1606 fatal( "could not connect to server" );
1608 for( i
= 0; i
< NUM_EXTENSIONS
; i
++ )
1609 xcb_prefetch_extension_data( c
, (xcb_extension_t
*) extensions
[ i
] );
1611 for( i
= 0; i
< NUM_ATOMS
; i
++ )
1612 atom_cookies
[ i
] = xcb_intern_atom( c
, 0, strlen( atom_names
[ i
] ),
1615 setup
= xcb_get_setup( c
);
1617 iter
= xcb_setup_roots_iterator( setup
);
1618 num_screens
= iter
.rem
;
1619 screens
= xmalloc( num_screens
* sizeof *screens
);
1620 gwm_screens
= xmalloc( num_screens
* sizeof *gwm_screens
);
1621 screen_atom_cookies
= alloca( num_screens
* sizeof *screen_atom_cookies
);
1622 screen_owners
= alloca( num_screens
* sizeof *screen_owners
);
1623 screen_owner_cookies
= alloca( num_screens
* sizeof *screen_owner_cookies
);
1624 cookies
= alloca( num_screens
* sizeof *cookies
);
1625 tree_cookies
= alloca( num_screens
* sizeof *tree_cookies
);
1627 for( i
= 0; iter
.rem
; i
++, xcb_screen_next( &iter
) ) {
1628 char wm_atom_str
[ 16 ];
1629 xcb_depth_iterator_t depth_iter
;
1631 screens
[ i
] = iter
.data
;
1632 screen_atom_cookies
[ i
] =
1633 xcb_intern_atom( c
, 0, sprintf( wm_atom_str
, "WM_S%d", i
),
1636 for( depth_iter
= xcb_screen_allowed_depths_iterator( screens
[ i
] );
1637 depth_iter
.rem
; xcb_depth_next( &depth_iter
) ) {
1638 xcb_visualtype_iterator_t visual_iter
;
1640 for( visual_iter
= xcb_depth_visuals_iterator( depth_iter
.data
);
1641 visual_iter
.rem
; xcb_visualtype_next( &visual_iter
) ) {
1642 xcb_visualtype_t
*visual
= visual_iter
.data
;
1644 if( screens
[ i
]->root_visual
== visual
->visual_id
) {
1645 gwm_screens
[ i
].root_visual
= visual
;
1650 fatal( "Root visual for screen %d (0x%X) not found", i
,
1651 screens
[ i
]->root_visual
);
1656 fake_window
= add_window( xcb_generate_id( c
) );
1657 fake_window
->screen
= 0;
1658 fake_window
->type
= WINDOW_FAKE
;
1660 n
= XCB_EVENT_MASK_PROPERTY_CHANGE
;
1661 xcb_create_window( c
, 0, fake_window
->w
, screens
[ 0 ]->root
, 0, 0, 1, 1, 0,
1662 XCB_WINDOW_CLASS_INPUT_ONLY
, screens
[ 0 ]->root_visual
,
1663 XCB_CW_EVENT_MASK
, &n
);
1665 /* Obtain a timestamp we can use for our selection acquiry time
1666 (ICCCM 2.0, section 2.1). */
1667 xcb_change_property( c
, XCB_PROP_MODE_APPEND
, fake_window
->w
, WM_HINTS
,
1668 WM_HINTS
, 32, 0, NULL
);
1670 xcb_flush( c
); /* BLOCK */
1672 for( i
= 0; i
< NUM_EXTENSIONS
; i
++ ) {
1673 const xcb_query_extension_reply_t
*r
;
1675 if( ( have_extension
[ i
] =
1676 ( r
= xcb_get_extension_data(
1677 c
, (xcb_extension_t
*) extensions
[ i
] ) ) && r
->present
) ) {
1678 extension_event
[ i
] = r
->first_event
;
1679 extension_error
[ i
] = r
->first_error
;
1683 for( i
= 0; i
< NUM_ATOMS
; i
++ ) {
1684 xcb_intern_atom_reply_t
*r
=
1685 xcb_intern_atom_reply( c
, atom_cookies
[ i
], NULL
);
1687 atoms
[ i
] = r
->atom
;
1691 /* FIXME Also set _NET_FRAME_EXTENTS. */
1692 prop_atoms
[ PROP__MOTIF_WM_HINTS
] = atoms
[ ATOM__MOTIF_WM_HINTS
];
1693 prop_atoms
[ PROP__NET_WM_ICON
] = atoms
[ ATOM__NET_WM_ICON
];
1694 prop_atoms
[ PROP__NET_WM_NAME
] = atoms
[ ATOM__NET_WM_NAME
];
1695 prop_atoms
[ PROP_WM_COLORMAP_WINDOWS
] = atoms
[ ATOM_WM_COLORMAP_WINDOWS
];
1696 prop_atoms
[ PROP_WM_HINTS
] = WM_HINTS
;
1697 prop_atoms
[ PROP_WM_NAME
] = WM_NAME
;
1698 prop_atoms
[ PROP_WM_NORMAL_HINTS
] = WM_NORMAL_HINTS
;
1699 prop_atoms
[ PROP_WM_PROTOCOLS
] = atoms
[ ATOM_WM_PROTOCOLS
];
1701 prop_types
[ PROP__MOTIF_WM_HINTS
] = atoms
[ ATOM__MOTIF_WM_HINTS
];
1702 prop_types
[ PROP__NET_WM_ICON
] = CARDINAL
;
1703 prop_types
[ PROP__NET_WM_NAME
] = atoms
[ ATOM_UTF8_STRING
];
1704 prop_types
[ PROP_WM_COLORMAP_WINDOWS
] = WINDOW
;
1705 prop_types
[ PROP_WM_HINTS
] = WM_HINTS
;
1706 prop_types
[ PROP_WM_NAME
] = XCB_GET_PROPERTY_TYPE_ANY
;
1707 prop_types
[ PROP_WM_NORMAL_HINTS
] = WM_SIZE_HINTS
;
1708 prop_types
[ PROP_WM_PROTOCOLS
] = ATOM
;
1710 for( i
= 0; i
< num_screens
; i
++ ) {
1711 xcb_intern_atom_reply_t
*r
=
1712 xcb_intern_atom_reply( c
, screen_atom_cookies
[ i
], NULL
);
1714 gwm_screens
[ i
].wm_atom
= r
->atom
;
1719 if( have_extension
[ EXT_RENDER
] &&
1720 ( have_extension
[ EXT_RENDER
] == !decorate_render_init() ) ) {
1721 update_window
= render_update_window
;
1722 window_size
= render_window_size
;
1723 replace_icons
= render_replace_icons
;
1727 decorate_core_init();
1728 update_window
= core_update_window
;
1729 window_size
= core_window_size
;
1730 replace_icons
= core_replace_icons
;
1733 /* Listen for a selection timestamp. */
1736 xcb_generic_event_t
*ev
= wait_for_event();
1739 fatal( "did not receive property notification" );
1741 if( ev
->response_type
& SEND_EVENT_MASK
) {
1744 continue; /* ignore synthetic events */
1745 } else if( ev
->response_type
== XCB_PROPERTY_NOTIFY
) {
1746 xcb_property_notify_event_t
*not =
1747 (xcb_property_notify_event_t
*) ev
;
1749 if( not->window
!= fake_window
->w
||
1750 not->atom
!= WM_HINTS
||
1751 not->state
!= XCB_PROPERTY_NEW_VALUE
) {
1752 warning( "unexpected property notification" );
1757 fake_window
->u
.fake
.timestamp
= not->time
;
1762 } else if( ev
->response_type
!= XCB_MAPPING_NOTIFY
) {
1763 /* Ignore MappingNotify -- we'll query the mappings later. */
1764 warning( "unexpected event while waiting for property "
1776 xcb_change_window_attributes( c
, fake_window
->w
, XCB_CW_EVENT_MASK
, &n
);
1779 int existing_manager
;
1780 int managers_changed
;
1781 sigset_t sigs
, oldsigs
;
1783 for( i
= 0; i
< num_screens
; i
++ )
1784 screen_owner_cookies
[ i
] =
1785 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1787 xcb_flush( c
); /* BLOCK */
1789 existing_manager
= 0;
1790 for( i
= 0; i
< num_screens
; i
++ ) {
1791 xcb_get_selection_owner_reply_t
*r
=
1792 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1795 if( ( screen_owners
[ i
] = r
->owner
) != XCB_NONE
) {
1797 printf( "Screen %d is already managed.\n", i
);
1805 if( existing_manager
&& !flag_replace
) {
1806 printf( "Use \"%s --replace\" to replace any existing "
1807 "window manager(s).\n", argv0
);
1812 if( existing_manager
) {
1815 /* Wait for existing manager(s) to terminate (ICCCM 2.0,
1817 n
= XCB_EVENT_MASK_STRUCTURE_NOTIFY
;
1818 for( i
= 0; i
< num_screens
; i
++ )
1819 if( screen_owners
[ i
] != XCB_NONE
)
1820 xcb_change_window_attributes( c
, screen_owners
[ i
],
1821 XCB_CW_EVENT_MASK
, &n
);
1823 for( i
= 0; i
< num_screens
; i
++ )
1824 screen_owner_cookies
[ i
] =
1825 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1827 xcb_flush( c
); /* BLOCK */
1829 managers_changed
= FALSE
;
1830 for( i
= 0; i
< num_screens
; i
++ ) {
1831 xcb_get_selection_owner_reply_t
*r
=
1832 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1835 if( r
->owner
!= screen_owners
[ i
] )
1836 managers_changed
= TRUE
;
1841 if( managers_changed
) {
1842 /* Argh. A window manager changed during all our bookkeeping.
1843 Undo our notifications, and start again. */
1845 for( i
= 0; i
< num_screens
; i
++ )
1846 if( screen_owners
[ i
] != XCB_NONE
)
1847 xcb_change_window_attributes( c
, screen_owners
[ i
],
1848 XCB_CW_EVENT_MASK
, &n
);
1854 /* Acquire ownership of WM_Sn selections (ICCCM 2.0, section 4.3). */
1855 for( i
= 0; i
< num_screens
; i
++ ) {
1856 xcb_set_selection_owner( c
, fake_window
->w
,
1857 gwm_screens
[ i
].wm_atom
,
1858 fake_window
->u
.fake
.timestamp
);
1859 screen_owner_cookies
[ i
] =
1860 xcb_get_selection_owner( c
, gwm_screens
[ i
].wm_atom
);
1863 xcb_flush( c
); /* BLOCK */
1864 sync_with_callback( screen_owner_cookies
[ num_screens
- 1 ].sequence
);
1866 for( i
= 0; i
< num_screens
; i
++ ) {
1867 xcb_get_selection_owner_reply_t
*r
=
1868 xcb_get_selection_owner_reply( c
, screen_owner_cookies
[ i
],
1871 if( r
->owner
!= fake_window
->w
)
1872 fatal( "did not acquire window manager selection" );
1879 /* Listen for DestroyNotify on any selection holders. */
1880 while( existing_manager
) {
1881 static int killed_existing_managers
;
1883 xcb_generic_event_t
*ev
= wait_for_event(); /* BLOCK */
1886 if( flag_force
&& signal_caught
== SIGALRM
&&
1887 !killed_existing_managers
) {
1890 for( i
= 0; i
< num_screens
; i
++ )
1891 if( screen_owners
[ i
] )
1892 xcb_kill_client( c
, screen_owners
[ i
] );
1894 killed_existing_managers
= TRUE
;
1901 fatal( "did not receive destroy notification" );
1904 if( ev
->response_type
& SEND_EVENT_MASK
) {
1907 continue; /* ignore synthetic events */
1908 } else if( ev
->response_type
== XCB_DESTROY_NOTIFY
) {
1909 xcb_destroy_notify_event_t
*not =
1910 (xcb_destroy_notify_event_t
*) ev
;
1912 for( i
= 0; i
< num_screens
; i
++ )
1913 if( not->window
== screen_owners
[ i
] ) {
1914 screen_owners
[ i
] = XCB_NONE
;
1919 } else if( ev
->response_type
== XCB_SELECTION_CLEAR
)
1920 /* We lost a window manager selection before we even
1921 started. Just shut down quietly. */
1923 else if( ev
->response_type
== XCB_MAPPING_NOTIFY
)
1924 /* Ignore MappingNotify. */
1926 else if( !ev
->response_type
) {
1928 xcb_generic_error_t
*error
= (xcb_generic_error_t
*) ev
;
1930 /* Expect Window and Value errors, because old window managers
1931 might die before we sent a corresponding request. */
1932 if( error
->error_code
!= XCB_WINDOW
&&
1933 error
->error_code
!= XCB_VALUE
)
1934 show_error( error
);
1938 warning( "unexpected event while waiting for destroy "
1949 sigfillset( &sigs
);
1950 sigprocmask( SIG_BLOCK
, &sigs
, &oldsigs
);
1953 if( signal_caught
== SIGALRM
)
1956 sigprocmask( SIG_SETMASK
, &oldsigs
, NULL
);
1959 msg
.response_type
= XCB_CLIENT_MESSAGE
;
1962 msg
.type
= atoms
[ ATOM_MANAGER
];
1963 msg
.data
.data32
[ 0 ] = fake_window
->u
.fake
.timestamp
;
1964 /* Data32[ 1 ] varies; will be completed later. */
1965 msg
.data
.data32
[ 2 ] = fake_window
->w
;
1966 msg
.data
.data32
[ 3 ] = 0;
1967 msg
.data
.data32
[ 4 ] = 0;
1969 n
= XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
|
1970 XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
|
1971 XCB_EVENT_MASK_OWNER_GRAB_BUTTON
;
1973 for( i
= 0; i
< num_screens
; i
++ ) {
1974 /* Send StructureNotify ClientMessage (ICCCM 2.0, section 2.8). */
1975 msg
.window
= screens
[ i
]->root
;
1976 msg
.data
.data32
[ 1 ] = gwm_screens
[ i
].wm_atom
;
1977 xcb_send_event( c
, FALSE
, screens
[ i
]->root
,
1978 XCB_EVENT_MASK_STRUCTURE_NOTIFY
, (char *) &msg
);
1981 xcb_change_window_attributes_checked( c
, screens
[ i
]->root
,
1982 XCB_CW_EVENT_MASK
, &n
);
1984 tree_cookies
[ i
] = xcb_query_tree( c
, screens
[ i
]->root
);
1987 xcb_set_input_focus( c
, XCB_INPUT_FOCUS_NONE
, XCB_INPUT_FOCUS_POINTER_ROOT
,
1988 fake_window
->u
.fake
.timestamp
);
1990 get_keyboard_mapping( setup
->min_keycode
,
1991 setup
->max_keycode
- setup
->min_keycode
+ 1 );
1992 get_modifier_mapping();
1994 xcb_flush( c
); /* BLOCK */
1995 sync_with_callback( cookies
[ num_screens
- 1 ].sequence
);
1997 for( i
= 0; i
< num_screens
; i
++ ) {
1998 xcb_generic_error_t
*error
= xcb_request_check( c
, cookies
[ i
] );
1999 xcb_query_tree_reply_t
*r
;
2000 struct gwm_window
*window
;
2001 xcb_window_t
*children
;
2005 fatal( "could not acquire SubstructureRedirect" );
2007 window
= add_window( screens
[ i
]->root
);
2009 window
->type
= WINDOW_ROOT
;
2011 gwm_screens
[ i
].cmap
= XCB_NONE
;
2012 gwm_screens
[ i
].cmap_time
= fake_window
->u
.fake
.timestamp
;
2014 r
= xcb_query_tree_reply( c
, tree_cookies
[ i
], NULL
);
2015 children
= xcb_query_tree_children( r
);
2016 for( j
= 0; j
< r
->children_len
; j
++ )
2017 manage_window( children
[ j
], FALSE
);
2023 static void handle_mapping_notify( xcb_mapping_notify_event_t
*ev
) {
2025 switch( ev
->request
) {
2026 case XCB_MAPPING_MODIFIER
:
2027 get_modifier_mapping();
2030 case XCB_MAPPING_KEYBOARD
:
2031 get_keyboard_mapping( ev
->first_keycode
, ev
->count
);
2034 case XCB_MAPPING_POINTER
:
2039 static void handle_client_message( xcb_client_message_event_t
*ev
) {
2041 if( ev
->type
== atoms
[ ATOM_WM_CHANGE_STATE
] ) {
2042 struct gwm_window
*window
;
2044 if( ( window
= lookup_window( ev
->window
) ) &&
2045 window
->type
== WINDOW_MANAGED
&&
2046 window
->u
.managed
.state
== STATE_NORMAL
&&
2047 ev
->data
.data32
[ 0 ] == STATE_ICONIC
)
2048 normal_to_iconic( window
);
2049 } else if( ev
->type
== atoms
[ ATOM_WM_COLORMAP_NOTIFY
] ) {
2050 struct gwm_window
*window
;
2052 if( ( window
= lookup_window( ev
->window
) ) &&
2053 window
->type
== WINDOW_ROOT
&&
2054 !ev
->data
.data32
[ 1 ] )
2055 /* Ignore the case where the client is starting installation:
2056 if it is well-behaved, it will do so only when the pointer
2057 is grabbed (in which case we won't do anything to conflict
2058 with it); if it is not well-behaved, it's not worth trying
2059 to accomodate it. */
2060 install_window_colormap( window
->screen
, focus_frame
?
2061 focus_frame
->u
.frame
.child
: NULL
,
2062 ev
->data
.data32
[ 0 ] );
2065 /* FIXME and all the EWMH client messages:
2067 _NET_MOVE_RESIZE_WINDOW
2070 _NET_REQUEST_FRAME_EXTENTS */
2073 static void handle_events( void ) {
2075 xcb_generic_event_t
*ev
;
2077 /* FIXME Should read as many events as possible (loop on
2078 xcb_poll_for_event), and queue them... might be able to
2079 elide some event processing that way if later events
2080 supercede earlier ones. */
2081 while( ( ev
= wait_for_event() ) ) {
2082 int event_type
= ev
->response_type
;
2083 int event_base_type
= event_type
& 0x7F;
2086 /* Determine event window and time. */
2087 switch( event_base_type
) {
2090 case XCB_KEYMAP_NOTIFY
:
2091 case XCB_CLIENT_MESSAGE
:
2092 case XCB_MAPPING_NOTIFY
:
2097 w
= ( (xcb_key_press_event_t
*) ev
)->event
;
2098 latest_timestamp
= ( (xcb_key_press_event_t
*) ev
)->time
;
2101 case XCB_KEY_RELEASE
:
2102 w
= ( (xcb_key_release_event_t
*) ev
)->event
;
2103 latest_timestamp
= ( (xcb_key_release_event_t
*) ev
)->time
;
2106 case XCB_BUTTON_PRESS
:
2108 pointer_demux
: ( (xcb_button_press_event_t
*) ev
)->event
;
2109 latest_timestamp
= ( (xcb_button_press_event_t
*) ev
)->time
;
2112 case XCB_BUTTON_RELEASE
:
2114 pointer_demux
: ( (xcb_button_release_event_t
*) ev
)->event
;
2115 latest_timestamp
= ( (xcb_button_release_event_t
*) ev
)->time
;
2118 case XCB_MOTION_NOTIFY
:
2120 pointer_demux
: ( (xcb_motion_notify_event_t
*) ev
)->event
;
2121 latest_timestamp
= ( (xcb_motion_notify_event_t
*) ev
)->time
;
2124 case XCB_ENTER_NOTIFY
:
2125 case XCB_LEAVE_NOTIFY
:
2127 pointer_demux
: ( (xcb_enter_notify_event_t
*) ev
)->event
;
2128 latest_timestamp
= ( (xcb_enter_notify_event_t
*) ev
)->time
;
2133 w
= ( (xcb_focus_in_event_t
*) ev
)->event
;
2137 w
= ( (xcb_expose_event_t
*) ev
)->window
;
2140 case XCB_GRAPHICS_EXPOSURE
:
2141 w
= ( (xcb_graphics_exposure_event_t
*) ev
)->drawable
;
2144 case XCB_NO_EXPOSURE
:
2145 w
= ( (xcb_no_exposure_event_t
*) ev
)->drawable
;
2148 case XCB_VISIBILITY_NOTIFY
:
2149 w
= ( (xcb_visibility_notify_event_t
*) ev
)->window
;
2152 case XCB_CREATE_NOTIFY
:
2153 w
= ( (xcb_create_notify_event_t
*) ev
)->parent
;
2156 case XCB_DESTROY_NOTIFY
:
2157 w
= ( (xcb_destroy_notify_event_t
*) ev
)->event
;
2160 case XCB_UNMAP_NOTIFY
:
2161 w
= ( (xcb_unmap_notify_event_t
*) ev
)->event
;
2164 case XCB_MAP_NOTIFY
:
2165 w
= ( (xcb_map_notify_event_t
*) ev
)->event
;
2168 case XCB_MAP_REQUEST
:
2169 w
= ( (xcb_map_request_event_t
*) ev
)->parent
;
2172 case XCB_REPARENT_NOTIFY
:
2173 w
= ( (xcb_reparent_notify_event_t
*) ev
)->event
;
2176 case XCB_CONFIGURE_NOTIFY
:
2177 w
= ( (xcb_configure_notify_event_t
*) ev
)->event
;
2180 case XCB_CONFIGURE_REQUEST
:
2181 w
= ( (xcb_configure_request_event_t
*) ev
)->parent
;
2184 case XCB_GRAVITY_NOTIFY
:
2185 w
= ( (xcb_gravity_notify_event_t
*) ev
)->event
;
2188 case XCB_RESIZE_REQUEST
:
2189 w
= ( (xcb_resize_request_event_t
*) ev
)->window
;
2192 case XCB_CIRCULATE_NOTIFY
:
2193 case XCB_CIRCULATE_REQUEST
:
2194 w
= ( (xcb_circulate_notify_event_t
*) ev
)->event
;
2197 case XCB_PROPERTY_NOTIFY
:
2198 w
= ( (xcb_property_notify_event_t
*) ev
)->window
;
2199 latest_timestamp
= ( (xcb_property_notify_event_t
*) ev
)->time
;
2202 case XCB_SELECTION_CLEAR
:
2203 w
= ( (xcb_selection_clear_event_t
*) ev
)->owner
;
2204 latest_timestamp
= ( (xcb_selection_clear_event_t
*) ev
)->time
;
2207 case XCB_SELECTION_REQUEST
:
2208 w
= ( (xcb_selection_request_event_t
*) ev
)->owner
;
2209 if( ( (xcb_selection_request_event_t
*) ev
)->time
)
2211 ( (xcb_selection_request_event_t
*) ev
)->time
;
2214 case XCB_SELECTION_NOTIFY
:
2215 w
= ( (xcb_selection_notify_event_t
*) ev
)->requestor
;
2216 if( ( (xcb_selection_notify_event_t
*) ev
)->time
)
2218 ( (xcb_selection_notify_event_t
*) ev
)->time
;
2221 case XCB_COLORMAP_NOTIFY
:
2222 w
= ( (xcb_colormap_notify_event_t
*) ev
)->window
;
2227 if( event_base_type
== extension_event
[ EXT_SHAPE
] ) {
2228 w
= ( (xcb_shape_notify_event_t
*) ev
)->affected_window
;
2229 event_type
= SHAPE_NOTIFY
;
2233 warning( "Unknown event" );
2237 if( ev
->response_type
& 0x80 )
2238 event_type
= SYNTHETIC_EVENT
;
2240 /* Global handling for events (before specific window handler). */
2241 switch( event_type
) {
2243 show_error( (xcb_generic_error_t
*) ev
);
2247 warning( "handle_events: received reply message" );
2250 case XCB_KEYMAP_NOTIFY
:
2253 case XCB_SELECTION_CLEAR
:
2254 /* We've lost the manager selection (see ICCCM 2.0, section
2255 2.8); close down gracefully. */
2259 case XCB_MAPPING_NOTIFY
:
2260 handle_mapping_notify( (xcb_mapping_notify_event_t
*) ev
);
2263 case SYNTHETIC_EVENT
:
2264 if( ev
->response_type
== ( XCB_CLIENT_MESSAGE
| SEND_EVENT_MASK
) )
2265 handle_client_message( (xcb_client_message_event_t
*) ev
);
2269 if( w
!= XCB_NONE
) {
2270 struct gwm_window
*window
;
2271 void ( *handler
)( struct gwm_window
*, xcb_generic_event_t
* );
2273 if( ( window
= lookup_window( w
) ) &&
2274 handlers
[ window
->type
] &&
2275 ( handler
= handlers
[ window
->type
][ event_type
] ) )
2276 handler( window
, ev
);
2279 /* FIXME other features:
2280 - make override-redirect windows translucent
2281 - when in the window menu, turn all windows translucent
2282 except for the selected item (temporarily raise that one?)
2283 - root background/cursor? (no; xsetroot for non composite case)
2284 - standard colourmaps? (no; xstdcmap does that)
2286 - add debugging structure to detect memory leaks, and maybe even
2287 correlating X protocol errors to request call sites
2288 - other extensions... XINERAMA? RANDR? RandR should send
2289 ConfigureNotify to the root when resolution changes, so don't
2290 need the extension... test it. */
2296 static void handle_intern_atom( unsigned int sequence
, void *reply
,
2297 xcb_generic_error_t
*error
,
2298 union callback_param p
) {
2307 static void shutdown_display( void ) {
2311 xcb_intern_atom_cookie_t cookie
;
2312 union callback_param cp
;
2313 xcb_query_tree_cookie_t
*tree_cookies
=
2314 alloca( num_screens
* sizeof *tree_cookies
);
2316 /* Query the window list from the server, so that we can reparent
2317 the windows bottom-to-top and therefore avoid disturbing the
2318 stacking order. Of course, if we kept stacking order information
2319 ourselves, this query would not be necessary. */
2320 for( i
= 0; i
< num_screens
; i
++ )
2321 tree_cookies
[ i
] = xcb_query_tree( c
, screens
[ i
]->root
);
2323 xcb_flush( c
); /* BLOCK */
2325 for( i
= 0; i
< num_screens
; i
++ ) {
2326 xcb_query_tree_reply_t
*r
;
2327 xcb_window_t
*children
;
2328 struct gwm_window
*window
;
2331 r
= xcb_query_tree_reply( c
, tree_cookies
[ i
], NULL
);
2332 children
= xcb_query_tree_children( r
);
2333 for( j
= 0; j
< r
->children_len
; j
++ )
2334 if( ( window
= lookup_window( children
[ j
] ) ) &&
2335 window
->type
== WINDOW_FRAME
)
2336 unmanage_window( window
->u
.frame
.child
);
2341 /* The ICCCM says that using CurrentTime for SetInputFocus is naughty
2342 (section 4.2.7). But we don't have much alternative in this case:
2343 if we're shutting down in response to a signal, for instance. */
2344 xcb_set_input_focus( c
, XCB_INPUT_FOCUS_NONE
, XCB_INPUT_FOCUS_POINTER_ROOT
,
2348 for( i
= 0; i
< num_screens
; i
++ )
2349 xcb_change_window_attributes( c
, screens
[ i
]->root
,
2350 XCB_CW_EVENT_MASK
, &n
);
2353 if( have_extension
[ EXT_RENDER
] )
2354 decorate_render_done();
2357 decorate_core_done();
2359 /* A pointless request to generate a reply, so we'll know when we've
2360 processed everything the server will send us. */
2361 cookie
= xcb_intern_atom( c
, TRUE
, 4, "ATOM" );
2363 handle_async_reply( cookie
.sequence
, handle_intern_atom
, cp
);
2367 sync_with_callback( cookie
.sequence
);
2369 xcb_disconnect( c
);
2371 /* Be pedantic about deallocating memory, to assist any tools that are
2372 looking for leaks. */
2373 forget_window( fake_window
);
2376 for( i
= 0; i
< windows
.used
; i
++ )
2377 if( windows
.values
[ i
]->type
== WINDOW_ROOT
) {
2378 forget_window( windows
.values
[ i
] );
2383 /* All windows have now been deallocated -- release the tables, too. */
2384 table_destroy( &windows
);
2385 table_destroy( &update_windows
);
2390 free( gwm_screens
);
2400 static void unknown( char *opt
) {
2402 printf( "%s: unknown option %s\n"
2403 "Usage: %s [OPTION]...\n"
2404 "Try \"%s --help\" for more information.\n",
2405 argv0
, opt
, argv0
, argv0
);
2408 extern int main( int argc
, char *argv
[] ) {
2411 struct sigaction sa
;
2413 static int flag_help
, flag_version
;
2414 static const struct option
{
2419 { "debug", &flag_debug
},
2421 { "force", &flag_force
},
2422 { "help", &flag_help
},
2423 { "replace", &flag_replace
},
2424 { "version", &flag_version
}
2426 #define NUM_OPTIONS ( sizeof options / sizeof *options )
2428 #if DEBUG && HAVE_MTRACE
2434 for( i
= 1; i
< argc
; i
++ )
2435 if( argv
[ i
][ 0 ] == '-' ) {
2436 if( argv
[ i
][ 1 ] == '-' && argv
[ i
][ 2 ] ) {
2437 for( j
= 0; j
< NUM_OPTIONS
; j
++ )
2438 if( !strncmp( argv
[ i
] + 2, options
[ j
].opt
,
2439 strlen( argv
[ i
] + 2 ) ) ) {
2440 *options
[ j
].flag
= 1;
2444 if( j
== NUM_OPTIONS
) {
2445 unknown( argv
[ i
] );
2449 } else if( argv
[ i
][ 1 ] != '-' ) {
2450 for( j
= 0; j
< NUM_OPTIONS
; j
++ )
2451 if( argv
[ i
][ 1 ] == options
[ j
].opt
[ 0 ] &&
2453 *options
[ j
].flag
= 1;
2457 if( j
== NUM_OPTIONS
) {
2458 unknown( argv
[ i
] );
2464 unknown( argv
[ i
] );
2470 printf( "Usage: %s [OPTION]...\n"
2473 " -d, --debug Show all events received\n"
2475 " -f, --force If replacing another window manager, "
2476 "terminate it if necessary\n"
2477 " -h, --help Display this information and exit\n"
2478 " -r, --replace Take over window management if another "
2480 " -v, --version Display version information and exit\n"
2481 "Please send bug reports to <" PACKAGE_BUGREPORT
">.\n",
2487 if( flag_version
) {
2488 puts( PACKAGE_STRING
"\nPlease send bug reports to <"
2489 PACKAGE_BUGREPORT
">." );
2494 sa
.sa_handler
= catch_signal
;
2495 sigemptyset( &sa
.sa_mask
);
2497 sigaction( SIGHUP
, &sa
, NULL
);
2498 sigaction( SIGINT
, &sa
, NULL
);
2499 sigaction( SIGTERM
, &sa
, NULL
);
2501 sa
.sa_handler
= alarm_signal
;
2502 sigaction( SIGALRM
, &sa
, NULL
);
2504 sa
.sa_handler
= child_signal
;
2505 sa
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
2506 sigaction( SIGCHLD
, &sa
, NULL
);
2511 which_signal
= signal_caught
;
2512 signal_caught
= 0; /* acknowledge receipt, so we get normal event
2513 processing during shutdown */
2515 if( xcb_connection_has_error( c
) )
2516 /* Don't bother attempting orderly shutdown. */
2517 fatal( "server connection error" );
2521 if( which_signal
> 0 ) {
2522 raise( which_signal
);
2530 /* FIXME xv moves when changing files (or pressing "n")... investigate. */