Added basic icon support.
[gwm.git] / gwm.c
blobe43d827aa9daa3e3eaece184e7212f5c65cf3a30
1 /*
2 * gwm.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <limits.h>
29 #if HAVE_MCHECK_H
30 #include <mcheck.h>
31 #endif
32 #if HAVE_POLL_H
33 #include <poll.h>
34 #endif
35 #include <signal.h>
36 #include <stdarg.h>
37 #if HAVE_STDINT_H
38 #include <stdint.h>
39 #endif
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #if HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 #include <sys/wait.h>
47 #if USE_COMPOSITE
48 #include <xcb/composite.h>
49 #endif
50 #if USE_DAMAGE
51 #include <xcb/damage.h>
52 #endif
53 #if USE_RENDER
54 #include <xcb/render.h>
55 #endif
56 #if USE_SHAPE
57 #include <xcb/shape.h>
58 #endif
59 #include <xcb/xcb.h>
60 #include <xcb/xcbext.h>
61 #if USE_XFIXES
62 #include <xcb/xfixes.h>
63 #endif
65 #include "gwm.h"
67 #include "button.h"
68 #include "decorate-core.h"
69 #if USE_RENDER
70 #include "decorate-render.h"
71 #endif
72 #include "frame.h"
73 #include "keyboard.h"
74 #include "managed.h"
75 #include "menu.h"
76 #include "root.h"
77 #include "utf8.h"
78 #include "window-table.h"
80 xcb_connection_t *c;
81 xcb_timestamp_t latest_timestamp;
83 xcb_atom_t atoms[ NUM_ATOMS ];
85 /* FIXME add:
86 _NET_SUPPORTED,
87 _NET_NUMBER_OF_DESKTOPS (1),
88 _NET_DESKTOP_GEOMETRY,
89 _NET_DESKTOP_VIEWPORT (0,0),
90 _NET_CURRENT_DESKTOP (0),
91 _NET_WORKAREA,
92 _NET_SUPPORTING_WM_CHECK properties to root windows. */
93 static const char *const atom_names[ NUM_ATOMS ] = {
94 "COMPOUND_TEXT",
95 "MANAGER",
96 "_MOTIF_WM_HINTS",
97 "_NET_WM_ICON",
98 "_NET_WM_NAME",
99 "UTF8_STRING",
100 "VERSION",
101 "WM_CHANGE_STATE",
102 "WM_COLORMAP_NOTIFY",
103 "WM_COLORMAP_WINDOWS",
104 "WM_DELETE_WINDOW",
105 "WM_PROTOCOLS",
106 "WM_STATE",
107 "WM_TAKE_FOCUS"
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 ] = {
114 #if USE_COMPOSITE
115 &xcb_composite_id,
116 #endif
117 #if USE_DAMAGE
118 &xcb_damage_id,
119 #endif
120 #if USE_RENDER
121 &xcb_render_id,
122 #endif
123 #if USE_SHAPE
124 &xcb_shape_id,
125 #endif
126 #if USE_XFIXES
127 &xcb_xfixes_id
128 #endif
131 int have_extension[ EXTENSIONS_SIZE ];
132 uint8_t extension_event[ EXTENSIONS_SIZE ],
133 extension_error[ EXTENSIONS_SIZE ];
135 int num_screens;
136 xcb_screen_t **screens;
137 struct gwm_screen *gwm_screens;
139 xcb_cursor_t cursors[ NUM_CURSORS ];
141 struct gwm_window *fake_window;
143 const char *argv0;
144 static int flag_replace, flag_force;
145 #if DEBUG
146 static int flag_debug;
147 #endif
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, ... ) {
159 va_list val;
161 va_start( val, format );
163 fprintf( stderr, "%s: warning: ", argv0 );
164 vfprintf( stderr, format, val );
165 putc( '\n', stderr );
167 va_end( val );
170 extern FORMAT( printf, 1, 2 ) NORETURN void fatal( char *format, ... ) {
172 va_list val;
174 va_start( val, format );
176 fprintf( stderr, "%s: ", argv0 );
177 vfprintf( stderr, format, val );
178 putc( '\n', stderr );
180 va_end( val );
182 exit( 1 ); /* don't attempt an orderly shutdown */
185 extern MALLOC void *xmalloc( size_t size ) {
187 void *p;
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 ) ) ) {
196 perror( argv0 );
198 exit( 1 );
201 return p;
204 extern void *xrealloc( void *p, size_t size ) {
206 if( !( p = realloc( p, size ) ) ) {
207 perror( argv0 );
209 exit( 1 );
212 return p;
215 extern MALLOC void *xcalloc( size_t number, size_t size ) {
217 void *p;
219 if( !( p = calloc( number, size ) ) ) {
220 perror( argv0 );
222 exit( 1 );
225 return p;
228 static void catch_signal( int n ) {
230 struct sigaction sa;
232 signal_caught = n;
234 sa.sa_handler = SIG_DFL;
235 sigemptyset( &sa.sa_mask );
236 sa.sa_flags = 0;
237 sigaction( SIGHUP, &sa, NULL );
238 sigaction( SIGINT, &sa, NULL );
239 sigaction( SIGTERM, &sa, NULL );
242 static void alarm_signal( int n ) {
244 signal_caught = 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 ) {
263 void *reply;
264 xcb_generic_error_t *error;
266 while( queue_head && xcb_poll_for_reply( c, queue_head->sequence,
267 &reply, &error ) ) {
268 struct async_callback *entry = queue_head;
270 if( !( queue_head = entry->next ) )
271 queue_tail = NULL;
273 entry->callback( entry->sequence, reply, error, entry->p );
275 free( entry );
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 ) )
287 queue_tail = NULL;
289 entry->callback( entry->sequence, reply, error, entry->p );
291 free( entry );
295 #if DEBUG
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
300 windows. */
301 struct gwm_window *window = table_lookup( &windows, w );
303 printf( " %s %08X (", label, w );
305 if( w == XCB_NONE )
306 fputs( "None", stdout );
307 else if( window ) {
308 struct gwm_window *client;
309 switch( window->type ) {
310 case WINDOW_ROOT:
311 printf( "root, screen %d", window->screen );
312 client = NULL;
313 break;
315 case WINDOW_MANAGED:
316 client = window;
317 break;
319 case WINDOW_FRAME:
320 fputs( "frame, ", stdout );
321 client = window->u.frame.child;
322 break;
324 case WINDOW_BUTTON:
325 fputs( "button, ", stdout );
326 client = window->u.button.frame->u.frame.child;
327 break;
329 case WINDOW_MENU:
330 fputs( "menu", stdout );
331 client = NULL;
332 break;
334 case WINDOW_MENUITEM:
335 fputs( "menu item", stdout );
336 client = NULL;
337 break;
339 case WINDOW_FAKE:
340 fputs( "fake", stdout );
341 client = NULL;
342 break;
344 case WINDOW_FEEDBACK:
345 printf( "feedback, screen %d", window->screen );
346 client = NULL;
347 break;
349 case WINDOW_INCOMPLETE:
350 fputs( "incomplete", stdout );
351 client = NULL;
352 break;
354 case WINDOW_CHILDLESS:
355 fputs( "childless", stdout );
356 client = NULL;
357 break;
360 if( client )
361 fputs( client->u.managed.name ? client->u.managed.name : "unnamed",
362 stdout );
363 } else
364 fputs( "unknown", stdout );
366 putchar( ')' );
367 putchar( '\n' );
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"
389 const char *name;
391 if( atom < 69 )
392 name = predefined_names[ atom ];
393 else {
394 int i;
396 for( i = 0; i < NUM_ATOMS; i++ )
397 if( atoms[ i ] == atom ) {
398 name = atom_names[ i ];
399 break;
402 if( i == NUM_ATOMS )
403 name = "unknown";
406 printf( " %s %d (%s)\n", label, atom, name );
408 #endif
410 extern void show_error( xcb_generic_error_t *error ) {
412 xcb_value_error_t *verror = (xcb_value_error_t *) error;
413 #if DEBUG
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 ];
424 else {
425 sprintf( num, "%d", error->error_code );
426 type = num;
429 switch( error->error_code ) {
430 case XCB_VALUE:
431 case XCB_WINDOW:
432 case XCB_PIXMAP:
433 case XCB_ATOM:
434 case XCB_CURSOR:
435 case XCB_FONT:
436 case XCB_DRAWABLE:
437 case XCB_COLORMAP:
438 case XCB_G_CONTEXT:
439 case XCB_ID_CHOICE:
440 sprintf( bad_num, ", val %X", verror->bad_value );
441 bad_value_str = bad_num;
442 break;
444 default:
445 bad_value_str = "";
446 break;
449 #if USE_RENDER
450 if( have_extension[ EXT_RENDER ] &&
451 error->error_code >= extension_error[ EXT_RENDER ] &&
452 error->error_code <= extension_error[ EXT_RENDER ] +
453 XCB_RENDER_GLYPH ) {
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 ] ];
461 #endif
463 warning( "unexpected error (type %s, sequence %d; opcode %d %d%s)", type,
464 error->sequence, verror->major_opcode, verror->minor_opcode,
465 bad_value_str );
466 #else
467 warning( "unexpected error (type %d, sequence %d; opcode %d %d)",
468 error->error_code, error->sequence,
469 verror->major_opcode, verror->minor_opcode );
470 #endif
473 #if DEBUG
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"
487 union event {
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;
518 int i;
520 /* FIXME handle extensions */
522 if( !( generic->response_type & ~SEND_EVENT_MASK ) ) {
523 show_error( (xcb_generic_error_t *) generic );
525 return;
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 )
533 return;
535 if( type != XCB_KEYMAP_NOTIFY )
536 printf( " Sequence %X\n", generic->full_sequence );
538 switch( type ) {
539 case XCB_KEY_PRESS:
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"
548 " Root x/y %d, %d\n"
549 " Event x/y %d, %d\n"
550 " Detail %d\n"
551 " State %04X\n"
552 " Time %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 );
557 break;
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"
565 " Root x/y %d, %d\n"
566 " Event x/y %d, %d\n"
567 " Mode %d\n"
568 " Detail %d\n"
569 " Focus %d\n"
570 " State %04X\n"
571 " Time %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 );
577 break;
579 case XCB_FOCUS_IN:
580 case XCB_FOCUS_OUT:
581 show_window( "Event", ev->f.event );
582 printf( " Mode %d\n"
583 " Detail %d\n",
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 );
591 break;
593 case XCB_EXPOSE:
594 show_window( "Window", ev->e.window );
595 printf( " x/y %d, %d\n"
596 " width/height %d x %d\n"
597 " Count %d\n",
598 ev->e.x, ev->e.y, ev->e.width, ev->e.height, ev->e.count );
599 break;
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"
605 " Count %d\n"
606 " Opcode %d, %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 );
609 break;
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 );
615 break;
617 case XCB_VISIBILITY_NOTIFY:
618 show_window( "Window", ev->v.window );
619 printf( " State %d\n", ev->v.state );
620 break;
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"
627 " Border width %d\n"
628 " Override redirect %s\n",
629 ev->c.x, ev->c.y,
630 ev->c.width, ev->c.height,
631 ev->c.border_width,
632 ev->c.override_redirect ? "Yes" : "No" );
633 break;
635 case XCB_DESTROY_NOTIFY:
636 show_window( "Event", ev->d.event );
637 show_window( "Window", ev->d.window );
638 break;
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" );
645 break;
647 case XCB_MAP_NOTIFY:
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" );
652 break;
654 case XCB_MAP_REQUEST:
655 show_window( "Parent", ev->mr.parent );
656 show_window( "Window", ev->mr.window );
657 break;
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",
665 ev->r.x, ev->r.y,
666 ev->r.override_redirect ? "Yes" : "No" );
667 break;
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" );
681 break;
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",
689 ev->cr.x, ev->cr.y,
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 );
696 break;
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 );
703 break;
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 );
709 break;
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 );
715 break;
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 );
721 break;
723 case XCB_PROPERTY_NOTIFY:
724 show_window( "Window", ev->p.window );
725 show_atom( "Atom", ev->p.atom );
726 printf( " State %d\n"
727 " Time %d\n",
728 ev->p.state, ev->p.time );
729 break;
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 );
735 break;
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 );
744 break;
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 );
752 break;
754 case XCB_COLORMAP_NOTIFY:
755 show_window( "Window", ev->cm.window );
756 printf( " Colormap %d\n"
757 " New %s\n"
758 " State %d\n",
759 ev->cm.colormap,
760 ev->cm._new ? "Yes" : "No",
761 ev->cm.state );
762 break;
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 ) {
770 case 8:
771 for( i = 0; i < 20; i++ )
772 printf( " %2d %02X\n", i, ev->cli.data.data8[ i ] );
773 break;
775 case 16:
776 for( i = 0; i < 10; i++ )
777 printf( " %d %04X\n", i, ev->cli.data.data16[ i ] );
778 break;
780 case 32:
781 for( i = 0; i < 5; i++ )
782 printf( " %d %08X\n", i, ev->cli.data.data32[ i ] );
783 break;
786 break;
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"
792 " Count %d",
793 ev->map.first_keycode, ev->map.count );
794 break;
797 #endif
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 );
804 #if HAVE_PPOLL
805 struct pollfd pfd;
806 #else
807 fd_set fds;
808 #endif
810 for(;;) {
811 if( xcb_connection_has_error( c ) )
812 return NULL;
814 ev = xcb_poll_for_event( c );
816 #if DEBUG
817 if( flag_debug && ev )
818 show_event( ev );
819 #endif
821 check_async_callbacks(); /* must poll for events first, since
822 polling replies won't dequeue them */
824 if( ev )
825 return ev;
827 if( update_windows.used ) {
828 struct gwm_window *update = update_windows.values[ 0 ];
830 update_window( update );
831 window_update_done( update );
833 continue;
836 xcb_flush( c );
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 );
848 return NULL;
851 #if HAVE_PPOLL
852 pfd.fd = fd;
853 pfd.events = POLLIN;
855 if( ppoll( &pfd, 1, NULL, &oldsigs ) < 0 && errno != EINTR )
856 perror( "ppoll" );
857 #else
858 FD_ZERO( &fds );
859 FD_SET( fd, &fds );
861 if( pselect( fd + 1, &fds, NULL, NULL, NULL, &oldsigs ) < 0 &&
862 errno != EINTR )
863 perror( "pselect" );
864 #endif
866 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
870 extern void handle_async_reply( unsigned int sequence,
871 void ( *callback )( unsigned int sequence,
872 void *reply,
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;
881 entry->p = p;
882 entry->next = NULL;
884 if( queue_tail )
885 queue_tail = queue_tail->next = entry;
886 else
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;
896 if( reply )
897 free( reply );
899 if( !error )
900 return; /* no error */
902 if( !( ignore & ( 1 << ( error->error_code - 1 ) ) ) )
903 show_error( error ); /* an error we didn't expect */
905 free( error );
908 extern void handle_error_reply( xcb_void_cookie_t cookie, unsigned ignore ) {
910 union callback_param p;
912 p.l = ignore;
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 {
929 int screen;
930 xcb_timestamp_t t;
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;
939 if( error ) {
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 )
943 show_error( error );
945 free( error );
948 if( reply ) {
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 );
954 free( reply );
957 free( cc );
960 /* Install a window's colormap on a screen, or the default colormap if
961 unspecified. */
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 );
971 cc->screen = screen;
972 cc->t = t;
973 cp.p = cc;
974 handle_async_reply( xcb_get_window_attributes(
975 c, window->u.managed.cmap_window ).sequence,
976 handle_get_window_attributes, cp );
977 } else
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 ) {
998 uint32_t value;
1000 /* Ignore Window errors on the child, because it might be destroyed
1001 before we are notified. */
1003 value = 0;
1004 handle_error_reply( xcb_change_window_attributes_checked(
1005 c, window->w, XCB_CW_EVENT_MASK, &value ),
1006 ERR_MASK_WINDOW );
1008 #if USE_SHAPE
1009 if( have_extension[ EXT_SHAPE ] )
1010 handle_error_reply( xcb_shape_select_input_checked( c, window->w,
1011 FALSE ),
1012 ERR_MASK_WINDOW );
1013 #endif
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 )
1022 uint64_t hash;
1023 #else
1024 unsigned long hash;
1025 #endif
1027 if( sx <= 0 )
1028 *x = 0;
1029 else {
1030 hash = window->w * 0x9B4A36D1;
1031 #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL
1032 hash ^= hash >> 32;
1033 #endif
1034 *x = hash % sx;
1037 if( sy <= 0 )
1038 *y = 0;
1039 else {
1040 hash = window->w * 0xA6E34925;
1041 #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL
1042 hash ^= hash >> 32;
1043 #endif
1044 *y = hash % sy;
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,
1051 #if USE_SHAPE
1052 xcb_shape_query_extents_reply_t *shape,
1053 #endif
1054 xcb_get_property_reply_t *props[],
1055 int map_request ) {
1057 int i;
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;
1070 #if USE_RENDER
1071 window->u.managed.full_icons = window->u.managed.menu_icons = NULL;
1072 window->u.managed.net_wm_icon = FALSE;
1073 #endif
1074 window->u.managed.name = NULL;
1075 window->u.managed.net_wm_name = FALSE;
1076 window->u.managed.state = STATE_WITHDRAWN;
1077 #if USE_SHAPE
1078 window->u.managed.shaped = have_extension[ EXT_SHAPE ] && shape &&
1079 shape->bounding_shaped;
1080 #endif
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,
1098 geom->border_width,
1099 window->u.managed.win_gravity );
1101 if( map_request && !( window->u.managed.hints & HINT_POSITION ) ) {
1102 int x, y;
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 ) {
1204 values[ 0 ] = 0;
1205 xcb_configure_window( c, w, XCB_CONFIG_WINDOW_BORDER_WIDTH, values );
1208 #if USE_SHAPE
1209 if( window->u.managed.shaped )
1210 match_managed_shape( window );
1211 #endif
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 ) {
1223 if( map_request )
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 ] );
1235 struct new_window {
1236 struct gwm_window *window; /* type WINDOW_INCOMPLETE */
1237 int map_request;
1238 xcb_get_geometry_cookie_t geometry_cookie;
1239 #if USE_SHAPE
1240 xcb_shape_query_extents_cookie_t shape_cookie;
1241 #endif
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;
1250 int i;
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 );
1254 #if USE_SHAPE
1255 xcb_shape_query_extents_reply_t *shape = have_extension[ EXT_SHAPE ] ?
1256 xcb_shape_query_extents_reply( c, nw->shape_cookie, NULL ) : NULL;
1257 #endif
1258 xcb_get_property_reply_t *props[ NUM_PROPS ];
1259 int have_all_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 ],
1263 NULL ) ) )
1264 have_all_props = FALSE;
1266 if( error ) {
1267 show_error( error );
1269 free( 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,
1277 #if USE_SHAPE
1278 shape,
1279 #endif
1280 props, nw->map_request );
1281 else {
1282 stop_listening( nw->window );
1283 forget_window( nw->window );
1286 if( attr )
1287 free( attr );
1289 if( geom )
1290 free( geom );
1292 #if USE_SHAPE
1293 if( shape )
1294 free( shape );
1295 #endif
1297 for( i = 0; i < NUM_PROPS; i++ )
1298 if( props[ i ] )
1299 free( props[ i ] );
1301 free( nw );
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 ];
1308 int i;
1309 union callback_param cp;
1310 unsigned int seq;
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. */
1316 free( nw );
1317 return;
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 );
1334 #if USE_SHAPE
1335 if( have_extension[ EXT_SHAPE ] )
1336 nw->shape_cookie = xcb_shape_query_extents( c, w );
1337 #endif
1338 for( i = 0; i < NUM_PROPS; i++ )
1339 nw->prop_cookies[ i ] = xcb_get_property( c, FALSE, w, prop_atoms[ i ],
1340 prop_types[ i ], 0,
1341 PROP_SIZE );
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,
1350 values );
1352 #if USE_SHAPE
1353 if( have_extension[ EXT_SHAPE ] )
1354 xcb_shape_select_input( c, w, TRUE );
1355 #endif
1357 xcb_ungrab_server( c );
1359 cp.p = nw;
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 ) {
1370 if( reply )
1371 free( reply );
1373 if( error ) {
1374 show_error( error );
1376 free( 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;
1387 int x, y;
1388 union callback_param cp;
1390 if( focus_frame == frame )
1391 focus_frame = NULL;
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 );
1419 cp.p = frame;
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;
1453 assert( !reply );
1455 if( error ) {
1456 free( error );
1458 p->property = XCB_NONE;
1461 handle_error_reply( xcb_send_event_checked( c, FALSE, p->requestor, 0,
1462 (char *) p ), ERR_MASK_WINDOW );
1464 free( p );
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;
1481 p->time = ev->time;
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,
1487 cp );
1488 } else {
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,
1498 0, (char *) &not ),
1499 ERR_MASK_WINDOW );
1503 static const event_handler fake_handlers[] = {
1504 NULL, /* Error */
1505 NULL, /* Reply */
1506 NULL, /* KeyPress */
1507 NULL, /* KeyRelease */
1508 NULL, /* ButtonPress */
1509 NULL, /* ButtonRelease */
1510 NULL, /* MotionNotify */
1511 NULL, /* EnterNotify */
1512 NULL, /* LeaveNotify */
1513 NULL, /* FocusIn */
1514 NULL, /* FocusOut */
1515 NULL, /* KeymapNotify */
1516 NULL, /* Expose */
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[] = {
1542 NULL, /* Error */
1543 NULL, /* Reply */
1544 NULL, /* KeyPress */
1545 NULL, /* KeyRelease */
1546 NULL, /* ButtonPress */
1547 NULL, /* ButtonRelease */
1548 NULL, /* MotionNotify */
1549 NULL, /* EnterNotify */
1550 NULL, /* LeaveNotify */
1551 NULL, /* FocusIn */
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,
1583 childless_handlers
1586 static void setup_display( void ) {
1588 int i;
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;
1593 uint32_t n;
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 ] ),
1613 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 ),
1634 wm_atom_str );
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;
1646 goto visual_found;
1650 fatal( "Root visual for screen %d (0x%X) not found", i,
1651 screens[ i ]->root_visual );
1652 visual_found:
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;
1688 free( r );
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;
1715 free( r );
1718 #if USE_RENDER
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;
1724 } else
1725 #endif
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. */
1734 for(;;) {
1735 /* BLOCK */
1736 xcb_generic_event_t *ev = wait_for_event();
1738 if( !ev )
1739 fatal( "did not receive property notification" );
1741 if( ev->response_type & SEND_EVENT_MASK ) {
1742 free( ev );
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" );
1754 continue;
1757 fake_window->u.fake.timestamp = not->time;
1759 free( ev );
1761 break;
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 "
1765 "notification" );
1767 #if DEBUG
1768 show_event( ev );
1769 #endif
1771 free( ev );
1775 n = 0;
1776 xcb_change_window_attributes( c, fake_window->w, XCB_CW_EVENT_MASK, &n );
1778 do {
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 ],
1793 NULL );
1795 if( ( screen_owners[ i ] = r->owner ) != XCB_NONE ) {
1796 if( !flag_replace )
1797 printf( "Screen %d is already managed.\n", i );
1799 existing_manager++;
1802 free( r );
1805 if( existing_manager && !flag_replace ) {
1806 printf( "Use \"%s --replace\" to replace any existing "
1807 "window manager(s).\n", argv0 );
1809 exit( 1 );
1812 if( existing_manager ) {
1813 uint32_t n;
1815 /* Wait for existing manager(s) to terminate (ICCCM 2.0,
1816 section 2.8). */
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 ],
1833 NULL );
1835 if( r->owner != screen_owners[ i ] )
1836 managers_changed = TRUE;
1838 free( r );
1841 if( managers_changed ) {
1842 /* Argh. A window manager changed during all our bookkeeping.
1843 Undo our notifications, and start again. */
1844 n = 0;
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 );
1850 continue;
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 ],
1869 NULL );
1871 if( r->owner != fake_window->w )
1872 fatal( "did not acquire window manager selection" );
1874 free( r );
1877 alarm( 3 );
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 */
1885 if( !ev ) {
1886 if( flag_force && signal_caught == SIGALRM &&
1887 !killed_existing_managers ) {
1888 signal_caught = 0;
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;
1896 alarm( 3 );
1898 continue;
1901 fatal( "did not receive destroy notification" );
1904 if( ev->response_type & SEND_EVENT_MASK ) {
1905 free( ev );
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;
1915 existing_manager--;
1918 free( ev );
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. */
1922 exit( 0 );
1923 else if( ev->response_type == XCB_MAPPING_NOTIFY )
1924 /* Ignore MappingNotify. */
1925 free( ev );
1926 else if( !ev->response_type ) {
1927 /* Error. */
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 );
1936 free( ev );
1937 } else {
1938 warning( "unexpected event while waiting for destroy "
1939 "notification" );
1941 #if DEBUG
1942 show_event( ev );
1943 #endif
1945 free( ev );
1949 sigfillset( &sigs );
1950 sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
1952 alarm( 0 );
1953 if( signal_caught == SIGALRM )
1954 signal_caught = 0;
1956 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
1957 } while( 0 );
1959 msg.response_type = XCB_CLIENT_MESSAGE;
1960 msg.format = 32;
1961 msg.sequence = 0;
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 );
1980 cookies[ i ] =
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;
2002 int j;
2004 if( error )
2005 fatal( "could not acquire SubstructureRedirect" );
2007 window = add_window( screens[ i ]->root );
2008 window->screen = i;
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 );
2019 free( r );
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();
2028 break;
2030 case XCB_MAPPING_KEYBOARD:
2031 get_keyboard_mapping( ev->first_keycode, ev->count );
2032 break;
2034 case XCB_MAPPING_POINTER:
2035 break;
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:
2066 _NET_CLOSE_WINDOW
2067 _NET_MOVE_RESIZE_WINDOW
2068 _NET_WM_MOVERESIZE
2069 _NET_RESTACK_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;
2084 xcb_window_t w;
2086 /* Determine event window and time. */
2087 switch( event_base_type ) {
2088 case 0: /* Error */
2089 case 1: /* Reply */
2090 case XCB_KEYMAP_NOTIFY:
2091 case XCB_CLIENT_MESSAGE:
2092 case XCB_MAPPING_NOTIFY:
2093 w = XCB_NONE;
2094 break;
2096 case XCB_KEY_PRESS:
2097 w = ( (xcb_key_press_event_t *) ev )->event;
2098 latest_timestamp = ( (xcb_key_press_event_t *) ev )->time;
2099 break;
2101 case XCB_KEY_RELEASE:
2102 w = ( (xcb_key_release_event_t *) ev )->event;
2103 latest_timestamp = ( (xcb_key_release_event_t *) ev )->time;
2104 break;
2106 case XCB_BUTTON_PRESS:
2107 w = pointer_demux ?
2108 pointer_demux : ( (xcb_button_press_event_t *) ev )->event;
2109 latest_timestamp = ( (xcb_button_press_event_t *) ev )->time;
2110 break;
2112 case XCB_BUTTON_RELEASE:
2113 w = pointer_demux ?
2114 pointer_demux : ( (xcb_button_release_event_t *) ev )->event;
2115 latest_timestamp = ( (xcb_button_release_event_t *) ev )->time;
2116 break;
2118 case XCB_MOTION_NOTIFY:
2119 w = pointer_demux ?
2120 pointer_demux : ( (xcb_motion_notify_event_t *) ev )->event;
2121 latest_timestamp = ( (xcb_motion_notify_event_t *) ev )->time;
2122 break;
2124 case XCB_ENTER_NOTIFY:
2125 case XCB_LEAVE_NOTIFY:
2126 w = pointer_demux ?
2127 pointer_demux : ( (xcb_enter_notify_event_t *) ev )->event;
2128 latest_timestamp = ( (xcb_enter_notify_event_t *) ev )->time;
2129 break;
2131 case XCB_FOCUS_IN:
2132 case XCB_FOCUS_OUT:
2133 w = ( (xcb_focus_in_event_t *) ev )->event;
2134 break;
2136 case XCB_EXPOSE:
2137 w = ( (xcb_expose_event_t *) ev )->window;
2138 break;
2140 case XCB_GRAPHICS_EXPOSURE:
2141 w = ( (xcb_graphics_exposure_event_t *) ev )->drawable;
2142 break;
2144 case XCB_NO_EXPOSURE:
2145 w = ( (xcb_no_exposure_event_t *) ev )->drawable;
2146 break;
2148 case XCB_VISIBILITY_NOTIFY:
2149 w = ( (xcb_visibility_notify_event_t *) ev )->window;
2150 break;
2152 case XCB_CREATE_NOTIFY:
2153 w = ( (xcb_create_notify_event_t *) ev )->parent;
2154 break;
2156 case XCB_DESTROY_NOTIFY:
2157 w = ( (xcb_destroy_notify_event_t *) ev )->event;
2158 break;
2160 case XCB_UNMAP_NOTIFY:
2161 w = ( (xcb_unmap_notify_event_t *) ev )->event;
2162 break;
2164 case XCB_MAP_NOTIFY:
2165 w = ( (xcb_map_notify_event_t *) ev )->event;
2166 break;
2168 case XCB_MAP_REQUEST:
2169 w = ( (xcb_map_request_event_t *) ev )->parent;
2170 break;
2172 case XCB_REPARENT_NOTIFY:
2173 w = ( (xcb_reparent_notify_event_t *) ev )->event;
2174 break;
2176 case XCB_CONFIGURE_NOTIFY:
2177 w = ( (xcb_configure_notify_event_t *) ev )->event;
2178 break;
2180 case XCB_CONFIGURE_REQUEST:
2181 w = ( (xcb_configure_request_event_t *) ev )->parent;
2182 break;
2184 case XCB_GRAVITY_NOTIFY:
2185 w = ( (xcb_gravity_notify_event_t *) ev )->event;
2186 break;
2188 case XCB_RESIZE_REQUEST:
2189 w = ( (xcb_resize_request_event_t *) ev )->window;
2190 break;
2192 case XCB_CIRCULATE_NOTIFY:
2193 case XCB_CIRCULATE_REQUEST:
2194 w = ( (xcb_circulate_notify_event_t *) ev )->event;
2195 break;
2197 case XCB_PROPERTY_NOTIFY:
2198 w = ( (xcb_property_notify_event_t *) ev )->window;
2199 latest_timestamp = ( (xcb_property_notify_event_t *) ev )->time;
2200 break;
2202 case XCB_SELECTION_CLEAR:
2203 w = ( (xcb_selection_clear_event_t *) ev )->owner;
2204 latest_timestamp = ( (xcb_selection_clear_event_t *) ev )->time;
2205 break;
2207 case XCB_SELECTION_REQUEST:
2208 w = ( (xcb_selection_request_event_t *) ev )->owner;
2209 if( ( (xcb_selection_request_event_t *) ev )->time )
2210 latest_timestamp =
2211 ( (xcb_selection_request_event_t *) ev )->time;
2212 break;
2214 case XCB_SELECTION_NOTIFY:
2215 w = ( (xcb_selection_notify_event_t *) ev )->requestor;
2216 if( ( (xcb_selection_notify_event_t *) ev )->time )
2217 latest_timestamp =
2218 ( (xcb_selection_notify_event_t *) ev )->time;
2219 break;
2221 case XCB_COLORMAP_NOTIFY:
2222 w = ( (xcb_colormap_notify_event_t *) ev )->window;
2223 break;
2225 default:
2226 #if USE_SHAPE
2227 if( event_base_type == extension_event[ EXT_SHAPE ] ) {
2228 w = ( (xcb_shape_notify_event_t *) ev )->affected_window;
2229 event_type = SHAPE_NOTIFY;
2230 break;
2232 #endif
2233 warning( "Unknown event" );
2234 break;
2237 if( ev->response_type & 0x80 )
2238 event_type = SYNTHETIC_EVENT;
2240 /* Global handling for events (before specific window handler). */
2241 switch( event_type ) {
2242 case 0: /* Error */
2243 show_error( (xcb_generic_error_t *) ev );
2244 break;
2246 case 1: /* Reply */
2247 warning( "handle_events: received reply message" );
2248 break;
2250 case XCB_KEYMAP_NOTIFY:
2251 break;
2253 case XCB_SELECTION_CLEAR:
2254 /* We've lost the manager selection (see ICCCM 2.0, section
2255 2.8); close down gracefully. */
2256 free( ev );
2257 return;
2259 case XCB_MAPPING_NOTIFY:
2260 handle_mapping_notify( (xcb_mapping_notify_event_t *) ev );
2261 break;
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 );
2266 break;
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)
2285 - DPMS
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. */
2292 free( ev );
2296 static void handle_intern_atom( unsigned int sequence, void *reply,
2297 xcb_generic_error_t *error,
2298 union callback_param p ) {
2300 if( error )
2301 free( error );
2303 if( reply )
2304 free( reply );
2307 static void shutdown_display( void ) {
2309 int i;
2310 uint32_t n;
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;
2329 int j;
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 );
2338 free( r );
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,
2345 XCB_CURRENT_TIME );
2347 n = 0;
2348 for( i = 0; i < num_screens; i++ )
2349 xcb_change_window_attributes( c, screens[ i ]->root,
2350 XCB_CW_EVENT_MASK, &n );
2352 #if USE_RENDER
2353 if( have_extension[ EXT_RENDER ] )
2354 decorate_render_done();
2355 else
2356 #endif
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" );
2362 cp.l = 0;
2363 handle_async_reply( cookie.sequence, handle_intern_atom, cp );
2365 xcb_flush( c );
2367 sync_with_callback( cookie.sequence );
2369 xcb_disconnect( c );
2370 #if DEBUG
2371 /* Be pedantic about deallocating memory, to assist any tools that are
2372 looking for leaks. */
2373 forget_window( fake_window );
2375 retry_root:
2376 for( i = 0; i < windows.used; i++ )
2377 if( windows.values[ i ]->type == WINDOW_ROOT ) {
2378 forget_window( windows.values[ i ] );
2380 goto retry_root;
2383 /* All windows have now been deallocated -- release the tables, too. */
2384 table_destroy( &windows );
2385 table_destroy( &update_windows );
2387 cleanup_keyboard();
2389 free( screens );
2390 free( gwm_screens );
2392 cleanup_utf8();
2394 #if HAVE_MTRACE
2395 muntrace();
2396 #endif
2397 #endif
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[] ) {
2410 int i, j;
2411 struct sigaction sa;
2412 int which_signal;
2413 static int flag_help, flag_version;
2414 static const struct option {
2415 const char *opt;
2416 int *flag;
2417 } options[] = {
2418 #if DEBUG
2419 { "debug", &flag_debug },
2420 #endif
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
2429 mtrace();
2430 #endif
2432 argv0 = argv[ 0 ];
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;
2441 break;
2444 if( j == NUM_OPTIONS ) {
2445 unknown( argv[ i ] );
2447 return 1;
2449 } else if( argv[ i ][ 1 ] != '-' ) {
2450 for( j = 0; j < NUM_OPTIONS; j++ )
2451 if( argv[ i ][ 1 ] == options[ j ].opt[ 0 ] &&
2452 !argv[ i ][ 2 ] ) {
2453 *options[ j ].flag = 1;
2454 break;
2457 if( j == NUM_OPTIONS ) {
2458 unknown( argv[ i ] );
2460 return 1;
2463 } else {
2464 unknown( argv[ i ] );
2466 return 1;
2469 if( flag_help ) {
2470 printf( "Usage: %s [OPTION]...\n"
2471 "Options:\n"
2472 #if DEBUG
2473 " -d, --debug Show all events received\n"
2474 #endif
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 "
2479 "manager exists\n"
2480 " -v, --version Display version information and exit\n"
2481 "Please send bug reports to <" PACKAGE_BUGREPORT ">.\n",
2482 argv0 );
2484 return 0;
2487 if( flag_version ) {
2488 puts( PACKAGE_STRING "\nPlease send bug reports to <"
2489 PACKAGE_BUGREPORT ">." );
2491 return 0;
2494 sa.sa_handler = catch_signal;
2495 sigemptyset( &sa.sa_mask );
2496 sa.sa_flags = 0;
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 );
2508 setup_display();
2510 handle_events();
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" );
2519 shutdown_display();
2521 if( which_signal > 0 ) {
2522 raise( which_signal );
2524 return 1;
2527 return 0;
2530 /* FIXME xv moves when changing files (or pressing "n")... investigate. */