Use _NET_WM_NAME in preference to WM_NAME.
[gwm.git] / gwm.c
blobef8761be7036694570888588e1ef5632f70dd599
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 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #if HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <sys/wait.h>
44 #if USE_COMPOSITE
45 #include <xcb/composite.h>
46 #endif
47 #if USE_DAMAGE
48 #include <xcb/damage.h>
49 #endif
50 #if USE_RENDER
51 #include <xcb/render.h>
52 #endif
53 #if USE_SHAPE
54 #include <xcb/shape.h>
55 #endif
56 #include <xcb/xcb.h>
57 #include <xcb/xcbext.h>
58 #if USE_XFIXES
59 #include <xcb/xfixes.h>
60 #endif
62 #include "gwm.h"
64 #include "button.h"
65 #include "decorate-core.h"
66 #if USE_RENDER
67 #include "decorate-render.h"
68 #endif
69 #include "frame.h"
70 #include "keyboard.h"
71 #include "managed.h"
72 #include "root.h"
73 #include "utf8.h"
74 #include "window-table.h"
76 xcb_connection_t *c;
77 xcb_timestamp_t latest_timestamp;
79 xcb_atom_t atoms[ NUM_ATOMS ];
81 /* FIXME add:
82 _NET_SUPPORTED,
83 _NET_NUMBER_OF_DESKTOPS (1),
84 _NET_DESKTOP_GEOMETRY,
85 _NET_DESKTOP_VIEWPORT (0,0),
86 _NET_CURRENT_DESKTOP (0),
87 _NET_WORKAREA,
88 _NET_SUPPORTING_WM_CHECK properties to root windows. */
89 static const char *atom_names[ NUM_ATOMS ] = {
90 "COMPOUND_TEXT",
91 "MANAGER",
92 "_NET_WM_NAME",
93 "UTF8_STRING",
94 "VERSION",
95 "WM_CHANGE_STATE",
96 "WM_COLORMAP_NOTIFY",
97 "WM_COLORMAP_WINDOWS",
98 "WM_DELETE_WINDOW",
99 "WM_PROTOCOLS",
100 "WM_STATE",
101 "WM_TAKE_FOCUS"
104 xcb_atom_t prop_atoms[ NUM_PROPS ];
105 xcb_atom_t prop_types[ NUM_PROPS ];
107 static const xcb_extension_t *extensions[ EXTENSIONS_SIZE ] = {
108 #if USE_COMPOSITE
109 &xcb_composite_id,
110 #endif
111 #if USE_DAMAGE
112 &xcb_damage_id,
113 #endif
114 #if USE_RENDER
115 &xcb_render_id,
116 #endif
117 #if USE_SHAPE
118 &xcb_shape_id,
119 #endif
120 #if USE_XFIXES
121 &xcb_xfixes_id
122 #endif
125 int have_extension[ EXTENSIONS_SIZE ];
126 uint8_t extension_event[ EXTENSIONS_SIZE ],
127 extension_error[ EXTENSIONS_SIZE ];
129 int num_screens;
130 xcb_screen_t **screens;
131 struct gwm_screen *gwm_screens;
133 #if USE_RENDER
134 xcb_render_pictformat_t fmt_rgba8, fmt_a8;
135 xcb_render_directformat_t dfmt_rgba8, dfmt_a8;
136 #endif
138 xcb_cursor_t cursors[ NUM_CURSORS ];
140 struct gwm_window *fake_window, *focus_frame;
142 const char *argv0;
143 static int flag_replace, flag_force;
144 #if DEBUG
145 static int flag_debug;
146 #endif
148 static volatile int signal_caught;
150 static void ( *update_window )( struct gwm_window *window );
152 extern FORMAT( printf, 1, 2 ) void warning( char *format, ... ) {
154 va_list val;
156 va_start( val, format );
158 fprintf( stderr, "%s: warning: ", argv0 );
159 vfprintf( stderr, format, val );
160 putc( '\n', stderr );
162 va_end( val );
165 extern FORMAT( printf, 1, 2 ) NORETURN void fatal( char *format, ... ) {
167 va_list val;
169 va_start( val, format );
171 fprintf( stderr, "%s: ", argv0 );
172 vfprintf( stderr, format, val );
173 putc( '\n', stderr );
175 va_end( val );
177 exit( 1 ); /* don't attempt an orderly shutdown */
180 extern MALLOC void *xmalloc( size_t size ) {
182 void *p;
184 /* It would be nice to clean up gracefully on allocation errors.
185 Unfortunately, doing so would require transmitting many requests
186 to the X server, which would require further allocations which
187 are likely to fail. Simply aborting with an error message is
188 therefore probably about as effective, and much simpler. */
190 if( !( p = malloc( size ) ) ) {
191 perror( argv0 );
193 exit( 1 );
196 return p;
199 extern void *xrealloc( void *p, size_t size ) {
201 if( !( p = realloc( p, size ) ) ) {
202 perror( argv0 );
204 exit( 1 );
207 return p;
210 extern MALLOC void *xcalloc( size_t number, size_t size ) {
212 void *p;
214 if( !( p = calloc( number, size ) ) ) {
215 perror( argv0 );
217 exit( 1 );
220 return p;
223 static void catch_signal( int n ) {
225 struct sigaction sa;
227 signal_caught = n;
229 sa.sa_handler = SIG_DFL;
230 sigemptyset( &sa.sa_mask );
231 sa.sa_flags = 0;
232 sigaction( SIGHUP, &sa, NULL );
233 sigaction( SIGINT, &sa, NULL );
234 sigaction( SIGTERM, &sa, NULL );
237 static void alarm_signal( int n ) {
239 signal_caught = n;
242 static void child_signal( int n ) {
244 while( waitpid( -1, 0, WNOHANG ) > 0 )
248 static struct async_callback {
249 unsigned int sequence;
250 void ( *callback )( unsigned int sequence, void *reply,
251 xcb_generic_error_t *error, union callback_param p );
252 union callback_param p;
253 struct async_callback *next;
254 } *queue_head, *queue_tail;
256 static void check_async_callbacks( void ) {
258 void *reply;
259 xcb_generic_error_t *error;
261 while( queue_head && xcb_poll_for_reply( c, queue_head->sequence,
262 &reply, &error ) ) {
263 struct async_callback *entry = queue_head;
265 if( !( queue_head = entry->next ) )
266 queue_tail = NULL;
268 entry->callback( entry->sequence, reply, error, entry->p );
270 free( entry );
274 extern void sync_with_callback( unsigned int sequence ) {
276 while( queue_head && sequence - queue_head->sequence < UINT_MAX >> 1 ) {
277 struct async_callback *entry = queue_head;
278 xcb_generic_error_t *error;
279 void *reply = xcb_wait_for_reply( c, entry->sequence, &error );
281 if( !( queue_head = entry->next ) )
282 queue_tail = NULL;
284 entry->callback( entry->sequence, reply, error, entry->p );
286 free( entry );
290 #if DEBUG
291 static void show_window( char *label, xcb_window_t w ) {
293 /* We don't want to use lookup_window here, since mere debugging
294 output shouldn't trigger the side effect of finishing incomplete
295 windows. */
296 struct gwm_window *window = table_lookup( &windows, w );
298 printf( " %s %08X (", label, w );
300 if( w == XCB_NONE )
301 fputs( "None", stdout );
302 else if( window ) {
303 struct gwm_window *client;
304 switch( window->type ) {
305 case WINDOW_ROOT:
306 printf( "root, screen %d", window->screen );
307 client = NULL;
308 break;
310 case WINDOW_MANAGED:
311 client = window;
312 break;
314 case WINDOW_FRAME:
315 fputs( "frame, ", stdout );
316 client = window->u.frame.child;
317 break;
319 case WINDOW_BUTTON:
320 fputs( "button, ", stdout );
321 client = window->u.button.frame->u.frame.child;
322 break;
324 case WINDOW_FAKE:
325 fputs( "fake", stdout );
326 client = NULL;
327 break;
329 case WINDOW_FEEDBACK:
330 printf( "feedback, screen %d", window->screen );
331 client = NULL;
332 break;
334 case WINDOW_INCOMPLETE:
335 fputs( "incomplete", stdout );
336 client = NULL;
337 break;
339 case WINDOW_CHILDLESS:
340 fputs( "childless", stdout );
341 client = NULL;
342 break;
345 if( client )
346 fputs( client->u.managed.name ? client->u.managed.name : "unnamed",
347 stdout );
348 } else
349 fputs( "unknown", stdout );
351 putchar( ')' );
352 putchar( '\n' );
355 static void show_atom( char *label, xcb_atom_t atom ) {
357 static const char *predefined_names[ 69 ] = {
358 "None", "PRIMARY", "SECONDARY", "ARC", "ATOM", "BITMAP", "CARDINAL",
359 "COLORMAP", "CURSOR", "CUT_BUFFER0", "CUT_BUFFER1", "CUT_BUFFER2",
360 "CUT_BUFFER3", "CUT_BUFFER4", "CUT_BUFFER5", "CUT_BUFFER6",
361 "CUT_BUFFER7", "DRAWABLE", "FONT", "INTEGER", "PIXMAP", "POINT",
362 "RECTANGLE", "RESOURCE_MANAGER", "RGB_COLOR_MAP", "RGB_BEST_MAP",
363 "RGB_BLUE_MAP", "RGB_DEFAULT_MAP", "RGB_GRAY_MAP", "RGB_GREEN_MAP",
364 "RGB_RED_MAP", "STRING", "VISUALID", "WINDOW", "WM_COMMAND",
365 "WM_HINTS", "WM_CLIENT_MACHINE", "WM_ICON_NAME", "WM_ICON_SIZE",
366 "WM_NAME", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_ZOOM_HINTS",
367 "MIN_SPACE", "NORM_SPACE", "MAX_SPACE", "END_SPACE", "SUPERSCRIPT_X",
368 "SUPERSCRIPT_Y", "SUBSCRIPT_X", "SUBSCRIPT_Y", "UNDERLINE_POSITION",
369 "UNDERLINE_THICKNESS", "STRIKEOUT_ASCENT", "STRIKEOUT_DESCENT",
370 "ITALIC_ANGLE", "X_HEIGHT", "QUAD_WIDTH", "WEIGHT", "POINT_SIZE",
371 "RESOLUTION", "COPYRIGHT", "NOTICE", "FONT_NAME", "FAMILY_NAME",
372 "FULL_NAME", "CAP_HEIGHT", "WM_CLASS", "WM_TRANSIENT_FOR"
374 const char *name;
376 if( atom < 69 )
377 name = predefined_names[ atom ];
378 else {
379 int i;
381 for( i = 0; i < NUM_ATOMS; i++ )
382 if( atoms[ i ] == atom ) {
383 name = atom_names[ i ];
384 break;
387 if( i == NUM_ATOMS )
388 name = "unknown";
391 printf( " %s %d (%s)\n", label, atom, name );
393 #endif
395 extern void show_error( xcb_generic_error_t *error ) {
397 xcb_value_error_t *verror = (xcb_value_error_t *) error;
398 #if DEBUG
399 static const char *type_strings[ XCB_IMPLEMENTATION + 1 ] = {
400 "Unknown", "Request", "Value", "Window", "Pixmap", "Atom", "Cursor",
401 "Font", "Match", "Drawable", "Access", "Alloc", "Colormap", "GContext",
402 "IDChoice", "Name", "Length", "Implementation"
404 const char *type, *bad_value_str;
405 char num[ 32 ], bad_num[ 32 ];
407 if( error->error_code <= XCB_IMPLEMENTATION )
408 type = type_strings[ error->error_code ];
409 else {
410 sprintf( num, "%d", error->error_code );
411 type = num;
414 switch( error->error_code ) {
415 case XCB_VALUE:
416 case XCB_WINDOW:
417 case XCB_PIXMAP:
418 case XCB_ATOM:
419 case XCB_CURSOR:
420 case XCB_FONT:
421 case XCB_DRAWABLE:
422 case XCB_COLORMAP:
423 case XCB_G_CONTEXT:
424 case XCB_ID_CHOICE:
425 sprintf( bad_num, ", val %X", verror->bad_value );
426 bad_value_str = bad_num;
427 break;
429 default:
430 bad_value_str = "";
431 break;
434 #if USE_RENDER
435 if( have_extension[ EXT_RENDER ] &&
436 error->error_code >= extension_error[ EXT_RENDER ] &&
437 error->error_code <= extension_error[ EXT_RENDER ] +
438 XCB_RENDER_GLYPH ) {
439 static const char *render_strings[ XCB_RENDER_GLYPH + 1 ] = {
440 "PictFormat", "Picture", "PictOp", "GlyphSet", "Glyph"
443 type = render_strings[ error->error_code -
444 extension_error[ EXT_RENDER ] ];
446 #endif
448 warning( "unexpected error (type %s, sequence %d; opcode %d %d%s)", type,
449 error->sequence, verror->major_opcode, verror->minor_opcode,
450 bad_value_str );
451 #else
452 warning( "unexpected error (type %d, sequence %d; opcode %d %d)",
453 error->error_code, error->sequence,
454 verror->major_opcode, verror->minor_opcode );
455 #endif
458 #if DEBUG
459 static void show_event( xcb_generic_event_t *generic ) {
461 static const char *event_names[ XCB_MAPPING_NOTIFY + 1 ] = {
462 "Error", "Reply", "KeyPress", "KeyRelease", "ButtonPress",
463 "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify",
464 "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExposure",
465 "NoExposure", "VisibilityNotify", "CreateNotify", "DestroyNotify",
466 "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify",
467 "ConfigureNotify", "ConfigureRequest", "GravityNotify",
468 "ResizeRequest", "CirculateNotify", "CirculateRequest",
469 "PropertyNotify", "SelectionClear", "SelectionRequest",
470 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify"
472 union event {
473 xcb_key_press_event_t kbm; /* KeyPress, KeyRelease, ButtonPress,
474 ButtonRelease, MotionNotify */
475 xcb_enter_notify_event_t el; /* EnterNotify, LeaveNotify */
476 xcb_focus_in_event_t f; /* FocusIn, FocusOut */
477 xcb_keymap_notify_event_t km; /* KeymapNotify */
478 xcb_expose_event_t e; /* Expose */
479 xcb_graphics_exposure_event_t g; /* GraphicsExposure */
480 xcb_no_exposure_event_t n; /* NoExposure */
481 xcb_visibility_notify_event_t v; /* VisibilityNotify */
482 xcb_create_notify_event_t c; /* CreateNotify */
483 xcb_destroy_notify_event_t d; /* DestroyNotify */
484 xcb_unmap_notify_event_t u; /* UnmapNotify */
485 xcb_map_notify_event_t m; /* MapNotify */
486 xcb_map_request_event_t mr; /* MapRequest */
487 xcb_reparent_notify_event_t r; /* ReparentNotify */
488 xcb_configure_notify_event_t con; /* ConfigureNotify */
489 xcb_configure_request_event_t cr; /* ConfigureRequest */
490 xcb_gravity_notify_event_t gn; /* GravityNotify */
491 xcb_resize_request_event_t rr; /* ResizeRequest */
492 xcb_circulate_notify_event_t cir; /* CirculateNotify */
493 xcb_circulate_request_event_t cirr; /* CirculateRequest */
494 xcb_property_notify_event_t p; /* PropertyNotify */
495 xcb_selection_clear_event_t sc; /* SelectionClear */
496 xcb_selection_request_event_t sr; /* SelectionRequest */
497 xcb_selection_notify_event_t sn; /* SelectionNotify */
498 xcb_colormap_notify_event_t cm; /* ColormapNotify */
499 xcb_client_message_event_t cli; /* ClientMessage */
500 xcb_mapping_notify_event_t map; /* MappingNotify */
501 } *ev = (union event *) generic;
502 int type = generic->response_type & 0x7F;
503 int i;
505 /* FIXME handle extensions */
507 if( !( generic->response_type & ~SEND_EVENT_MASK ) ) {
508 show_error( (xcb_generic_error_t *) generic );
510 return;
513 printf( "Event %d (%s%s)\n", generic->response_type,
514 type <= XCB_MAPPING_NOTIFY ? event_names[ type ] : "Unknown",
515 generic->response_type & SEND_EVENT_MASK ? ", synthetic" : "" );
517 if( type < XCB_KEY_PRESS || type > XCB_MAPPING_NOTIFY )
518 return;
520 if( type != XCB_KEYMAP_NOTIFY )
521 printf( " Sequence %X\n", generic->full_sequence );
523 switch( type ) {
524 case XCB_KEY_PRESS:
525 case XCB_KEY_RELEASE:
526 case XCB_BUTTON_PRESS:
527 case XCB_BUTTON_RELEASE:
528 case XCB_MOTION_NOTIFY:
529 show_window( "Root", ev->kbm.root );
530 show_window( "Event", ev->kbm.event );
531 show_window( "Child", ev->kbm.child );
532 printf( " Same-screen %s\n"
533 " Root x/y %d, %d\n"
534 " Event x/y %d, %d\n"
535 " Detail %d\n"
536 " State %04X\n"
537 " Time %d\n",
538 ev->kbm.same_screen ? "Yes" : "No",
539 ev->kbm.root_x, ev->kbm.root_y,
540 ev->kbm.event_x, ev->kbm.event_y,
541 ev->kbm.detail, ev->kbm.state, ev->kbm.time );
542 break;
544 case XCB_ENTER_NOTIFY:
545 case XCB_LEAVE_NOTIFY:
546 show_window( "Root", ev->el.root );
547 show_window( "Event", ev->el.event );
548 show_window( "Child", ev->el.child );
549 printf( " Same-screen %s\n"
550 " Root x/y %d, %d\n"
551 " Event x/y %d, %d\n"
552 " Mode %d\n"
553 " Detail %d\n"
554 " Focus %d\n"
555 " State %04X\n"
556 " Time %d\n",
557 ev->el.same_screen_focus & 2 ? "Yes" : "No",
558 ev->el.root_x, ev->el.root_y,
559 ev->el.event_x, ev->el.event_y,
560 ev->el.mode, ev->el.detail, ev->el.same_screen_focus & 1,
561 ev->el.state, ev->el.time );
562 break;
564 case XCB_FOCUS_IN:
565 case XCB_FOCUS_OUT:
566 show_window( "Event", ev->f.event );
567 printf( " Mode %d\n"
568 " Detail %d\n",
569 ev->f.mode, ev->f.detail );
571 case XCB_KEYMAP_NOTIFY:
572 for( i = 0; i < 0xF8; i++ )
573 if( ev->km.keys[ i >> 3 ] & ( 1 << ( i & 7 ) ) )
574 printf( " key %d down\n", i );
576 break;
578 case XCB_EXPOSE:
579 show_window( "Window", ev->e.window );
580 printf( " x/y %d, %d\n"
581 " width/height %d x %d\n"
582 " Count %d\n",
583 ev->e.x, ev->e.y, ev->e.width, ev->e.height, ev->e.count );
584 break;
586 case XCB_GRAPHICS_EXPOSURE:
587 show_window( "Drawable", ev->g.drawable );
588 printf( " x/y %d, %d\n"
589 " width/height %d x %d\n"
590 " Count %d\n"
591 " Opcode %d, %d\n",
592 ev->g.x, ev->g.y, ev->g.width, ev->g.height, ev->g.count,
593 ev->g.major_opcode, ev->g.minor_opcode );
594 break;
596 case XCB_NO_EXPOSURE:
597 show_window( "Drawable", ev->n.drawable );
598 printf( " Opcode %d, %d\n",
599 ev->n.major_opcode, ev->n.minor_opcode );
600 break;
602 case XCB_VISIBILITY_NOTIFY:
603 show_window( "Window", ev->v.window );
604 printf( " State %d\n", ev->v.state );
605 break;
607 case XCB_CREATE_NOTIFY:
608 show_window( "Parent", ev->c.parent );
609 show_window( "Window", ev->c.window );
610 printf( " x/y %d, %d\n"
611 " width/height %d x %d\n"
612 " Border width %d\n"
613 " Override redirect %s\n",
614 ev->c.x, ev->c.y,
615 ev->c.width, ev->c.height,
616 ev->c.border_width,
617 ev->c.override_redirect ? "Yes" : "No" );
618 break;
620 case XCB_DESTROY_NOTIFY:
621 show_window( "Event", ev->d.event );
622 show_window( "Window", ev->d.window );
623 break;
625 case XCB_UNMAP_NOTIFY:
626 show_window( "Event", ev->u.event );
627 show_window( "Window", ev->u.window );
628 printf( " From configure: %s\n",
629 ev->u.from_configure ? "Yes" : "No" );
630 break;
632 case XCB_MAP_NOTIFY:
633 show_window( "Event", ev->m.event );
634 show_window( "Window", ev->m.window );
635 printf( " Override redirect: %s\n",
636 ev->m.override_redirect ? "Yes" : "No" );
637 break;
639 case XCB_MAP_REQUEST:
640 show_window( "Parent", ev->mr.parent );
641 show_window( "Window", ev->mr.window );
642 break;
644 case XCB_REPARENT_NOTIFY:
645 show_window( "Event", ev->r.event );
646 show_window( "Window", ev->r.window );
647 show_window( "Parent", ev->r.parent );
648 printf( " x/y %d, %d\n"
649 " Override redirect %s\n",
650 ev->r.x, ev->r.y,
651 ev->r.override_redirect ? "Yes" : "No" );
652 break;
654 case XCB_CONFIGURE_NOTIFY:
655 show_window( "Event", ev->con.event );
656 show_window( "Window", ev->con.window );
657 printf( " x/y %d, %d\n"
658 " width/height %d x %d\n"
659 " Border width %d\n",
660 ev->con.x, ev->con.y,
661 ev->con.width, ev->con.height,
662 ev->con.border_width );
663 show_window( "Above sibling", ev->con.above_sibling );
664 printf( " Override redirect %s\n",
665 ev->con.override_redirect ? "Yes" : "No" );
666 break;
668 case XCB_CONFIGURE_REQUEST:
669 show_window( "Parent", ev->cr.parent );
670 show_window( "Window", ev->cr.window );
671 printf( " x/y %d, %d\n"
672 " width/height %d x %d\n"
673 " Border width %d\n",
674 ev->cr.x, ev->cr.y,
675 ev->cr.width, ev->cr.height,
676 ev->cr.border_width );
677 show_window( "Sibling", ev->cr.sibling );
678 printf( " Stack mode %d\n"
679 " Value mask %04X\n",
680 ev->cr.stack_mode, ev->cr.value_mask );
681 break;
683 case XCB_GRAVITY_NOTIFY:
684 show_window( "Event", ev->gn.event );
685 show_window( "Window", ev->gn.window );
686 printf( " x/y %d, %d\n",
687 ev->gn.x, ev->gn.y );
688 break;
690 case XCB_RESIZE_REQUEST:
691 show_window( "Window", ev->rr.window );
692 printf( " width/height %d x %d\n",
693 ev->rr.width, ev->rr.height );
694 break;
696 case XCB_CIRCULATE_NOTIFY:
697 show_window( "Event", ev->cir.event );
698 show_window( "Window", ev->cir.window );
699 printf( " Place %d\n", ev->cir.place );
700 break;
702 case XCB_CIRCULATE_REQUEST:
703 show_window( "Parent", ev->cirr.event );
704 show_window( "Window", ev->cirr.window );
705 printf( " Place %d\n", ev->cirr.place );
706 break;
708 case XCB_PROPERTY_NOTIFY:
709 show_window( "Window", ev->p.window );
710 show_atom( "Atom", ev->p.atom );
711 printf( " State %d\n"
712 " Time %d\n",
713 ev->p.state, ev->p.time );
714 break;
716 case XCB_SELECTION_CLEAR:
717 show_window( "Owner", ev->sc.owner );
718 show_atom( "Selection", ev->sc.selection );
719 printf( " Time %d\n", ev->sc.time );
720 break;
722 case XCB_SELECTION_REQUEST:
723 show_window( "Owner", ev->sr.owner );
724 show_atom( "Selection", ev->sr.selection );
725 show_atom( "Target", ev->sr.target );
726 show_atom( "Property", ev->sr.property );
727 show_window( "Requestor", ev->sr.requestor );
728 printf( " Time %d\n", ev->sr.time );
729 break;
731 case XCB_SELECTION_NOTIFY:
732 show_window( "Requestor", ev->sn.requestor );
733 show_atom( "Selection", ev->sn.selection );
734 show_atom( "Target", ev->sn.target );
735 show_atom( "Property", ev->sn.property );
736 printf( " Time %d\n", ev->sn.time );
737 break;
739 case XCB_COLORMAP_NOTIFY:
740 show_window( "Window", ev->cm.window );
741 printf( " Colormap %d\n"
742 " New %s\n"
743 " State %d\n",
744 ev->cm.colormap,
745 ev->cm._new ? "Yes" : "No",
746 ev->cm.state );
747 break;
749 case XCB_CLIENT_MESSAGE:
750 show_window( "Window", ev->cli.window );
751 show_atom( "Type", ev->cli.type );
752 printf( " Format %d\n", ev->cli.format );
754 switch( ev->cli.format ) {
755 case 8:
756 for( i = 0; i < 20; i++ )
757 printf( " %2d %02X\n", i, ev->cli.data.data8[ i ] );
758 break;
760 case 16:
761 for( i = 0; i < 10; i++ )
762 printf( " %d %04X\n", i, ev->cli.data.data16[ i ] );
763 break;
765 case 32:
766 for( i = 0; i < 5; i++ )
767 printf( " %d %08X\n", i, ev->cli.data.data32[ i ] );
768 break;
771 break;
773 case XCB_MAPPING_NOTIFY:
774 printf( " Request %d\n", ev->map.request );
775 if( ev->map.request == XCB_MAPPING_KEYBOARD )
776 printf( " First keycode %d\n"
777 " Count %d",
778 ev->map.first_keycode, ev->map.count );
779 break;
782 #endif
784 extern xcb_generic_event_t *wait_for_event( void ) {
786 xcb_generic_event_t *ev;
787 sigset_t sigs, oldsigs;
788 int fd = xcb_get_file_descriptor( c );
789 #if HAVE_PPOLL
790 struct pollfd pfd;
791 #else
792 fd_set fds;
793 #endif
795 for(;;) {
796 if( xcb_connection_has_error( c ) )
797 return NULL;
799 ev = xcb_poll_for_event( c );
801 #if DEBUG
802 if( flag_debug && ev )
803 show_event( ev );
804 #endif
806 check_async_callbacks(); /* must poll for events first, since
807 polling replies won't dequeue them */
809 if( ev )
810 return ev;
812 if( update_windows.used ) {
813 struct gwm_window *update = update_windows.values[ 0 ];
815 update_window( update );
816 window_update_done( update );
818 continue;
821 xcb_flush( c );
823 sigemptyset( &sigs );
824 sigaddset( &sigs, SIGHUP );
825 sigaddset( &sigs, SIGINT );
826 sigaddset( &sigs, SIGTERM );
828 sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
830 if( signal_caught ) {
831 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
833 return NULL;
836 #if HAVE_PPOLL
837 pfd.fd = fd;
838 pfd.events = POLLIN;
840 if( ppoll( &pfd, 1, NULL, &oldsigs ) < 0 && errno != EINTR )
841 perror( "ppoll" );
842 #else
843 FD_ZERO( &fds );
844 FD_SET( fd, &fds );
846 if( pselect( fd + 1, &fds, NULL, NULL, NULL, &oldsigs ) < 0 &&
847 errno != EINTR )
848 perror( "pselect" );
849 #endif
851 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
855 extern void handle_async_reply( unsigned int sequence,
856 void ( *callback )( unsigned int sequence,
857 void *reply,
858 xcb_generic_error_t *error,
859 union callback_param p ),
860 union callback_param p ) {
862 struct async_callback *entry = xmalloc( sizeof *entry );
864 entry->sequence = sequence;
865 entry->callback = callback;
866 entry->p = p;
867 entry->next = NULL;
869 if( queue_tail )
870 queue_tail = queue_tail->next = entry;
871 else
872 queue_head = queue_tail = entry;
875 static void error_callback( unsigned int sequence, void *reply,
876 xcb_generic_error_t *error,
877 union callback_param p ) {
879 unsigned ignore = p.l;
881 if( reply )
882 free( reply );
884 if( !error )
885 return; /* no error */
887 if( !( ignore & ( 1 << ( error->error_code - 1 ) ) ) )
888 show_error( error ); /* an error we didn't expect */
890 free( error );
893 extern void handle_error_reply( xcb_void_cookie_t cookie, unsigned ignore ) {
895 union callback_param p;
897 p.l = ignore;
899 handle_async_reply( cookie.sequence, error_callback, p );
902 static void install_colormap( int screen, xcb_colormap_t cmap,
903 xcb_timestamp_t t ) {
905 if( (int) t - (int) gwm_screens[ screen ].cmap_time > 0 ) {
906 gwm_screens[ screen ].cmap_time = t;
908 if( gwm_screens[ screen ].cmap != cmap )
909 xcb_install_colormap( c, gwm_screens[ screen ].cmap = cmap );
913 struct cmap_callback {
914 int screen;
915 xcb_timestamp_t t;
918 static void handle_get_window_attributes( unsigned int sequence, void *reply,
919 xcb_generic_error_t *error,
920 union callback_param cp ) {
922 struct cmap_callback *cc = cp.p;
924 if( error ) {
925 /* Ignore Window errors, since we were given the ID by a client
926 and we don't trust them to get it right. */
927 if( error->error_code != XCB_WINDOW )
928 show_error( error );
930 free( error );
933 if( reply ) {
934 xcb_get_window_attributes_reply_t *r = reply;
936 install_colormap( cc->screen, r->colormap ? r->colormap :
937 screens[ cc->screen ]->default_colormap, cc->t );
939 free( reply );
942 free( cc );
945 /* Install a window's colormap on a screen, or the default colormap if
946 unspecified. */
947 extern void install_window_colormap( int screen, struct gwm_window *window,
948 xcb_timestamp_t t ) {
950 assert( !window || window->type == WINDOW_MANAGED );
952 if( window && window->u.managed.cmap_window ) {
953 union callback_param cp;
954 struct cmap_callback *cc = xmalloc( sizeof *cc );
956 cc->screen = screen;
957 cc->t = t;
958 cp.p = cc;
959 handle_async_reply( xcb_get_window_attributes(
960 c, window->u.managed.cmap_window ).sequence,
961 handle_get_window_attributes, cp );
962 } else
963 install_colormap( screen, window && window->u.managed.cmap ?
964 window->u.managed.cmap :
965 screens[ screen ]->default_colormap, t );
968 /* Return TRUE iff an event is the only button pressed (and so would
969 initiate a passive grab). */
970 extern CONST int initial_press( xcb_button_press_event_t *ev ) {
972 return !( ev->state & 0x1F00 );
975 /* Return TRUE iff an event is the release of the last button (and so would
976 terminate a passive grab). */
977 extern CONST int final_release( xcb_button_release_event_t *ev ) {
979 return ( ev->state & 0x1F00 ) == ( 0x80 << ev->detail );
982 extern void translate_child_to_frame( int *fx, int *fy, int *fwidth,
983 int *fheight, int cx, int cy,
984 int cwidth, int cheight,
985 int cborder, int win_gravity ) {
987 *fwidth = cwidth + FRAME_BORDER_WIDTH * 2;
988 *fheight = cheight + FRAME_BORDER_WIDTH + FRAME_TITLE_HEIGHT;
990 switch( win_gravity ) {
991 case XCB_GRAVITY_NORTH_WEST:
992 case XCB_GRAVITY_WEST:
993 case XCB_GRAVITY_SOUTH_WEST:
994 default:
995 *fx = cx;
996 break;
998 case XCB_GRAVITY_NORTH:
999 case XCB_GRAVITY_CENTER:
1000 case XCB_GRAVITY_SOUTH:
1001 case XCB_GRAVITY_STATIC:
1002 *fx = cx + cborder - FRAME_BORDER_WIDTH - FRAME_X_BORDER;
1003 break;
1005 case XCB_GRAVITY_NORTH_EAST:
1006 case XCB_GRAVITY_EAST:
1007 case XCB_GRAVITY_SOUTH_EAST:
1008 *fx = cx + ( ( cborder - FRAME_BORDER_WIDTH - FRAME_X_BORDER ) << 1 );
1009 break;
1012 switch( win_gravity ) {
1013 case XCB_GRAVITY_NORTH_WEST:
1014 case XCB_GRAVITY_NORTH:
1015 case XCB_GRAVITY_NORTH_EAST:
1016 default:
1017 *fy = cy;
1018 break;
1020 case XCB_GRAVITY_WEST:
1021 case XCB_GRAVITY_CENTER:
1022 case XCB_GRAVITY_EAST:
1023 *fy = cy + cborder - ( ( FRAME_BORDER_WIDTH +
1024 FRAME_TITLE_HEIGHT ) >> 1 ) - FRAME_X_BORDER;
1025 break;
1027 case XCB_GRAVITY_SOUTH_WEST:
1028 case XCB_GRAVITY_SOUTH:
1029 case XCB_GRAVITY_SOUTH_EAST:
1030 *fy = cy + ( cborder << 1 ) - FRAME_TITLE_HEIGHT - FRAME_BORDER_WIDTH -
1031 ( FRAME_X_BORDER << 1 );
1032 break;
1034 case XCB_GRAVITY_STATIC:
1035 *fy = cy + cborder - FRAME_TITLE_HEIGHT - FRAME_X_BORDER;
1036 break;
1040 extern void translate_frame_to_child( int *cx, int *cy, int fx, int fy,
1041 int cborder, int win_gravity ) {
1043 switch( win_gravity ) {
1044 case XCB_GRAVITY_NORTH_WEST:
1045 case XCB_GRAVITY_WEST:
1046 case XCB_GRAVITY_SOUTH_WEST:
1047 default:
1048 *cx = fx;
1049 break;
1051 case XCB_GRAVITY_NORTH:
1052 case XCB_GRAVITY_CENTER:
1053 case XCB_GRAVITY_SOUTH:
1054 case XCB_GRAVITY_STATIC:
1055 *cx = fx - cborder + FRAME_BORDER_WIDTH + FRAME_X_BORDER;
1056 break;
1058 case XCB_GRAVITY_NORTH_EAST:
1059 case XCB_GRAVITY_EAST:
1060 case XCB_GRAVITY_SOUTH_EAST:
1061 *cx = fx + ( ( FRAME_BORDER_WIDTH + FRAME_X_BORDER - cborder ) << 1 );
1062 break;
1065 switch( win_gravity ) {
1066 case XCB_GRAVITY_NORTH_WEST:
1067 case XCB_GRAVITY_NORTH:
1068 case XCB_GRAVITY_NORTH_EAST:
1069 default:
1070 *cy = fy;
1071 break;
1073 case XCB_GRAVITY_WEST:
1074 case XCB_GRAVITY_CENTER:
1075 case XCB_GRAVITY_EAST:
1076 *cy = fy - cborder + ( ( FRAME_BORDER_WIDTH +
1077 FRAME_TITLE_HEIGHT ) >> 1 ) + FRAME_X_BORDER;
1078 break;
1080 case XCB_GRAVITY_SOUTH_WEST:
1081 case XCB_GRAVITY_SOUTH:
1082 case XCB_GRAVITY_SOUTH_EAST:
1083 *cy = fy - ( cborder << 1 ) + FRAME_TITLE_HEIGHT + FRAME_BORDER_WIDTH +
1084 ( FRAME_X_BORDER << 1 );
1085 break;
1087 case XCB_GRAVITY_STATIC:
1088 *cy = fy - cborder + FRAME_TITLE_HEIGHT + FRAME_X_BORDER;
1089 break;
1093 extern void apply_size_constraints( struct gwm_window *window, int *width,
1094 int *height ) {
1096 int eff_base_width = window->u.managed.base_width ?
1097 window->u.managed.base_width : window->u.managed.min_width,
1098 eff_base_height = window->u.managed.base_height ?
1099 window->u.managed.base_height : window->u.managed.min_height;
1101 /* Apply the minimum and maximum constraints. These are already known
1102 to be compatible. */
1103 if( *width < window->u.managed.min_width )
1104 *width = window->u.managed.min_width;
1106 if( *height < window->u.managed.min_height )
1107 *height = window->u.managed.min_height;
1109 if( *width > window->u.managed.max_width )
1110 *width = window->u.managed.max_width;
1112 if( *height > window->u.managed.max_height )
1113 *height = window->u.managed.max_height;
1115 /* Now round down each dimension to an integer multiple of increments.
1116 Rounding down cannot violate the maximum constraint, and since
1117 eff_base_* >= min_*, it will not reduce below the minimum constraint. */
1118 *width -= ( *width - eff_base_width ) % window->u.managed.width_inc;
1119 *height -= ( *height - eff_base_height ) % window->u.managed.height_inc;
1121 if( window->u.managed.min_aspect_x * *height >
1122 window->u.managed.min_aspect_y * *width ) {
1123 /* Minimum aspect ratio violated. Attempt to either increase the
1124 width or decrease the height (whichever is a smaller change), but
1125 don't do either if it would go outside the min/max bounds.
1126 Both division operations are safe (min_aspect_y is always
1127 positive, and min_aspect_x must be positive if there is a
1128 violation). Note that an exact solution might not be possible
1129 (e.g. certain cases where the aspect ratio and increments are
1130 coprime). */
1131 int min_x, max_y;
1133 min_x = ( window->u.managed.min_aspect_x * *height +
1134 ( window->u.managed.min_aspect_y - 1 ) ) /
1135 window->u.managed.min_aspect_y + window->u.managed.width_inc - 1;
1136 min_x -= ( min_x - eff_base_width ) % window->u.managed.width_inc;
1138 max_y = window->u.managed.min_aspect_y * *width /
1139 window->u.managed.min_aspect_x;
1140 max_y -= ( max_y - eff_base_height ) % window->u.managed.height_inc;
1142 if( min_x - *width < *height - max_y ) {
1143 /* The width change is smaller: prefer it if possible. */
1144 if( min_x >= window->u.managed.min_width )
1145 *width = min_x;
1146 else if( max_y < window->u.managed.max_height )
1147 *height = max_y;
1148 } else {
1149 /* The height change is smaller: prefer it if possible. */
1150 if( max_y < window->u.managed.max_height )
1151 *height = max_y;
1152 else if( min_x >= window->u.managed.min_width )
1153 *width = min_x;
1157 if( window->u.managed.max_aspect_x * *height <
1158 window->u.managed.max_aspect_y * *width ) {
1159 /* Maximum aspect ratio violated. Much like the case above... */
1160 int min_y, max_x;
1162 min_y = ( window->u.managed.max_aspect_y * *width +
1163 ( window->u.managed.max_aspect_x - 1 ) ) /
1164 window->u.managed.max_aspect_x + window->u.managed.height_inc - 1;
1165 min_y -= ( min_y - eff_base_height ) % window->u.managed.height_inc;
1167 max_x = window->u.managed.max_aspect_x * *height /
1168 window->u.managed.max_aspect_y;
1169 max_x -= ( max_x - eff_base_width ) % window->u.managed.width_inc;
1171 if( min_y - *height < *width - max_x ) {
1172 /* The height change is smaller: prefer it if possible. */
1173 if( min_y >= window->u.managed.min_height )
1174 *height = min_y;
1175 else if( max_x < window->u.managed.max_width )
1176 *width = max_x;
1177 } else {
1178 /* The width change is smaller: prefer it if possible. */
1179 if( max_x < window->u.managed.max_width )
1180 *width = max_x;
1181 else if( min_y >= window->u.managed.min_height )
1182 *height = min_y;
1187 static void stop_listening( struct gwm_window *window ) {
1189 uint32_t value;
1191 /* Ignore Window errors on the child, because it might be destroyed
1192 before we are notified. */
1194 value = 0;
1195 handle_error_reply( xcb_change_window_attributes_checked(
1196 c, window->w, XCB_CW_EVENT_MASK, &value ),
1197 ERR_MASK_WINDOW );
1199 #if USE_SHAPE
1200 if( have_extension[ EXT_SHAPE ] )
1201 handle_error_reply( xcb_shape_select_input_checked( c, window->w,
1202 FALSE ),
1203 ERR_MASK_WINDOW );
1204 #endif
1207 static void set_managed_state( struct gwm_window *window, int state ) {
1209 uint32_t values[ 2 ];
1211 assert( window->type == WINDOW_MANAGED );
1213 /* Place a WM_STATE property on the client window (ICCCM 2.0, section
1214 4.1.3.1). */
1215 values[ 0 ] = window->u.managed.state = state;
1216 values[ 1 ] = XCB_NONE;
1217 xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->w,
1218 atoms[ ATOM_WM_STATE ], atoms[ ATOM_WM_STATE ],
1219 32, 2, values );
1222 static void place_window( struct gwm_window *window, int *x, int *y,
1223 int width, int height ) {
1225 int sx = screens[ window->screen ]->width_in_pixels - width;
1226 int sy = screens[ window->screen ]->height_in_pixels - height;
1227 unsigned long long hash;
1229 if( sx <= 0 )
1230 *x = 0;
1231 else {
1232 hash = window->w * 0x9B4A36D1;
1233 hash ^= hash >> 32;
1234 *x = hash % sx;
1237 if( sy <= 0 )
1238 *y = 0;
1239 else {
1240 hash = window->w * 0x9B4A36D1;
1241 hash ^= hash >> 32;
1242 *y = hash % sy;
1246 static void start_managing_window( struct gwm_window *window,
1247 xcb_get_window_attributes_reply_t *attr,
1248 xcb_get_geometry_reply_t *geom,
1249 #if USE_SHAPE
1250 xcb_shape_query_extents_reply_t *shape,
1251 #endif
1252 xcb_get_property_reply_t *props[],
1253 int map_request ) {
1255 int i;
1256 uint32_t values[ 4 ];
1257 struct gwm_window *frame, *button;
1258 xcb_window_t w = window->w;
1260 frame = add_window( xcb_generate_id( c ) );
1261 button = add_window( xcb_generate_id( c ) );
1262 window->screen = lookup_window( geom->root )->screen;
1263 window->type = WINDOW_MANAGED;
1264 window->u.managed.frame = frame;
1265 window->u.managed.border_width = geom->border_width;
1266 window->u.managed.cmap = attr->colormap;
1267 window->u.managed.hints = 0;
1268 window->u.managed.name = NULL;
1269 window->u.managed.net_wm_name = 0;
1270 window->u.managed.state = STATE_WITHDRAWN;
1271 frame->screen = window->screen;
1272 frame->type = WINDOW_FRAME;
1273 frame->u.frame.child = window;
1274 frame->u.frame.button = button;
1275 button->screen = window->screen;
1276 button->type = WINDOW_BUTTON;
1277 button->u.button.frame = frame;
1279 for( i = 0; i < NUM_PROPS; i++ )
1280 managed_property_change( window, i, props[ i ] );
1282 translate_child_to_frame( &frame->u.frame.x, &frame->u.frame.y,
1283 &frame->u.frame.width, &frame->u.frame.height,
1284 geom->x, geom->y, geom->width, geom->height,
1285 geom->border_width,
1286 window->u.managed.win_gravity );
1288 if( map_request && !( window->u.managed.hints & HINT_POSITION ) ) {
1289 int x, y;
1291 place_window( window, &x, &y,
1292 frame->u.frame.width, frame->u.frame.height );
1294 frame->u.frame.x = x;
1295 frame->u.frame.y = y;
1298 /* Don't create frames entirely off-screen; ensure that at least 8 pixels
1299 are within the root. */
1300 if( frame->u.frame.x > screens[ frame->screen ]->width_in_pixels - 8 &&
1301 frame->u.frame.x + frame->u.frame.width >
1302 screens[ frame->screen ]->width_in_pixels )
1303 frame->u.frame.x = screens[ frame->screen ]->width_in_pixels - 8;
1304 else if( frame->u.frame.x < 0 &&
1305 frame->u.frame.x + frame->u.frame.width < 8 )
1306 frame->u.frame.x = 8 - frame->u.frame.width;
1308 if( frame->u.frame.y > screens[ frame->screen ]->height_in_pixels - 8 &&
1309 frame->u.frame.y + frame->u.frame.height >
1310 screens[ frame->screen ]->height_in_pixels )
1311 frame->u.frame.y = screens[ frame->screen ]->height_in_pixels - 8;
1312 else if( frame->u.frame.y < 0 &&
1313 frame->u.frame.y + frame->u.frame.height < 8 )
1314 frame->u.frame.y = 8 - frame->u.frame.height;
1316 values[ 0 ] = gwm_screens[ frame->screen ].pixels[
1317 COL_FRAME_INACTIVE ]; /* background pixel */
1318 values[ 1 ] = gwm_screens[ frame->screen ].pixels[
1319 COL_BORDER ]; /* border pixel */
1320 values[ 2 ] = XCB_GRAVITY_NORTH_WEST; /* bit gravity */
1321 values[ 3 ] = TRUE; /* override redirect */
1322 values[ 4 ] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
1323 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_POINTER_MOTION_HINT |
1324 XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_EXPOSURE |
1325 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
1327 xcb_create_window( c, XCB_COPY_FROM_PARENT, frame->w, geom->root,
1328 frame->u.frame.x, frame->u.frame.y,
1329 frame->u.frame.width, frame->u.frame.height, 1,
1330 XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
1331 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
1332 XCB_CW_BIT_GRAVITY | XCB_CW_OVERRIDE_REDIRECT |
1333 XCB_CW_EVENT_MASK, values );
1335 values[ 0 ] = gwm_screens[ frame->screen ].pixels[
1336 COL_BUTTON_INACTIVE ]; /* background pixel */
1337 values[ 1 ] = gwm_screens[ frame->screen ].pixels[
1338 COL_BORDER ]; /* border pixel */
1339 values[ 2 ] = TRUE; /* override redirect */
1340 values[ 3 ] = XCB_EVENT_MASK_EXPOSURE;
1341 xcb_create_window( c, XCB_COPY_FROM_PARENT, button->w, frame->w,
1342 2, 1, FRAME_BUTTON_SIZE, FRAME_BUTTON_SIZE, 1,
1343 XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
1344 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
1345 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, values );
1347 xcb_grab_button( c, FALSE, button->w, XCB_EVENT_MASK_BUTTON_PRESS |
1348 XCB_EVENT_MASK_BUTTON_RELEASE |
1349 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW,
1350 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE,
1351 XCB_NONE, XCB_BUTTON_INDEX_ANY, 0x8000 );
1353 xcb_map_window( c, button->w );
1355 xcb_change_save_set( c, XCB_SET_MODE_INSERT, w );
1357 if( !map_request ) {
1358 /* If the window was mapped already, then reparenting it will
1359 generate an UnmapNotify. To avoid race conditions in discarding
1360 this unwanted event, temporarily reduce our event mask. */
1361 xcb_grab_server( c );
1363 values[ 0 ] = XCB_EVENT_MASK_PROPERTY_CHANGE |
1364 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
1365 xcb_change_window_attributes( c, w, XCB_CW_EVENT_MASK, values );
1368 xcb_reparent_window( c, w, frame->w, FRAME_BORDER_WIDTH,
1369 FRAME_TITLE_HEIGHT );
1370 if( !map_request ) {
1371 values[ 0 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1372 XCB_EVENT_MASK_PROPERTY_CHANGE |
1373 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
1374 xcb_change_window_attributes( c, w, XCB_CW_EVENT_MASK, values );
1376 xcb_ungrab_server( c );
1379 if( geom->border_width ) {
1380 values[ 0 ] = 0;
1381 xcb_configure_window( c, w, XCB_CONFIG_WINDOW_BORDER_WIDTH, values );
1384 #if USE_SHAPE
1385 if( have_extension[ EXT_SHAPE ] && shape && shape->bounding_shaped )
1386 match_managed_shape( window, TRUE );
1387 #endif
1389 /* Tell the client we've relocated their window with respect to the
1390 root (see ICCCM 2.0, section 4.2.3); we almost certainly have,
1391 between reparenting it and possibly applying a placement policy. */
1392 synthetic_configure_notify( frame );
1394 set_managed_state( window, map_request &&
1395 ( window->u.managed.hints & HINT_ICONIC ) ?
1396 STATE_ICONIC : STATE_NORMAL );
1398 if( window->u.managed.state == STATE_NORMAL ) {
1399 if( map_request )
1400 xcb_map_window( c, w );
1402 xcb_map_window( c, frame->w );
1405 /* An ugly hack to get the button cursor properly set now that
1406 the button has been created. */
1407 managed_property_change( window, PROP_WM_PROTOCOLS,
1408 props[ PROP_WM_PROTOCOLS ] );
1411 struct new_window {
1412 struct gwm_window *window; /* type WINDOW_INCOMPLETE */
1413 int map_request;
1414 xcb_get_geometry_cookie_t geometry_cookie;
1415 #if USE_SHAPE
1416 xcb_shape_query_extents_cookie_t shape_cookie;
1417 #endif
1418 xcb_get_property_cookie_t prop_cookies[ NUM_PROPS ];
1421 static void handle_manage_window( unsigned int sequence, void *reply,
1422 xcb_generic_error_t *error,
1423 union callback_param cp ) {
1425 struct new_window *nw = cp.p;
1426 int i;
1427 xcb_get_window_attributes_reply_t *attr = reply;
1428 xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(
1429 c, nw->geometry_cookie, NULL );
1430 #if USE_SHAPE
1431 xcb_shape_query_extents_reply_t *shape = have_extension[ EXT_SHAPE ] ?
1432 xcb_shape_query_extents_reply( c, nw->shape_cookie, NULL ) : NULL;
1433 #endif
1434 xcb_get_property_reply_t *props[ NUM_PROPS ];
1435 int have_all_props;
1437 for( i = 0, have_all_props = TRUE; i < NUM_PROPS; i++ )
1438 if( !( props[ i ] = xcb_get_property_reply( c, nw->prop_cookies[ i ],
1439 NULL ) ) )
1440 have_all_props = FALSE;
1442 if( error ) {
1443 show_error( error );
1445 free( error );
1448 if( attr && geom && have_all_props &&
1449 attr->_class == XCB_WINDOW_CLASS_INPUT_OUTPUT &&
1450 ( nw->map_request || ( attr->map_state == XCB_MAP_STATE_VIEWABLE &&
1451 !attr->override_redirect ) ) )
1452 start_managing_window( nw->window, attr, geom,
1453 #if USE_SHAPE
1454 shape,
1455 #endif
1456 props, nw->map_request );
1457 else {
1458 stop_listening( nw->window );
1459 forget_window( nw->window );
1462 if( attr )
1463 free( attr );
1465 if( geom )
1466 free( geom );
1468 #if USE_SHAPE
1469 if( shape )
1470 free( shape );
1471 #endif
1473 for( i = 0; i < NUM_PROPS; i++ )
1474 if( props[ i ] )
1475 free( props[ i ] );
1477 free( nw );
1480 extern void manage_window( xcb_window_t w, int map_request ) {
1482 struct new_window *nw = xmalloc( sizeof *nw );
1483 uint32_t values[ 2 ];
1484 int i;
1485 union callback_param cp;
1486 unsigned int seq;
1488 if( !( nw->window = add_window( w ) ) ) {
1489 /* The window already exists. This can happen if a client attempts
1490 more than one MapWindow before we've handle the first one, and
1491 so we receive multiple MapRequests. */
1492 free( nw );
1493 return;
1496 /* Our request for notification of events concerning the client
1497 window must be atomic with our query of its state (otherwise
1498 there will exist some interval where we will be unaware of updates,
1499 or receive notifications for updates which were already present in
1500 the originally queried state). Unfortunately this means that we're
1501 forced to receive events before we even know whether we're interested
1502 in managing the window. If we subsequently discover that we
1503 didn't want the events after all, we will call stop_listening()
1504 (below), and any corresponding events for the unknown window
1505 will be silently ignored. */
1506 xcb_grab_server( c );
1508 nw->map_request = map_request;
1509 nw->geometry_cookie = xcb_get_geometry( c, w );
1510 #if USE_SHAPE
1511 if( have_extension[ EXT_SHAPE ] )
1512 nw->shape_cookie = xcb_shape_query_extents( c, w );
1513 #endif
1514 for( i = 0; i < NUM_PROPS; i++ )
1515 nw->prop_cookies[ i ] = xcb_get_property( c, FALSE, w, prop_atoms[ i ],
1516 prop_types[ i ], 0,
1517 PROP_SIZE );
1519 seq = xcb_get_window_attributes( c, w ).sequence;
1521 values[ 0 ] = XCB_GRAVITY_NORTH_WEST;
1522 values[ 1 ] = XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1523 XCB_EVENT_MASK_PROPERTY_CHANGE |
1524 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
1525 xcb_change_window_attributes( c, w, XCB_CW_WIN_GRAVITY | XCB_CW_EVENT_MASK,
1526 values );
1528 #if USE_SHAPE
1529 if( have_extension[ EXT_SHAPE ] )
1530 xcb_shape_select_input( c, w, TRUE );
1531 #endif
1533 xcb_ungrab_server( c );
1535 cp.p = nw;
1536 handle_async_reply( seq, handle_manage_window, cp );
1538 nw->window->type = WINDOW_INCOMPLETE;
1539 nw->window->u.incomplete.sequence = seq;
1542 static void handle_destroy_window( unsigned int sequence, void *reply,
1543 xcb_generic_error_t *error,
1544 union callback_param cp ) {
1546 if( reply )
1547 free( reply );
1549 if( error ) {
1550 show_error( error );
1552 free( error );
1555 forget_window( cp.p );
1558 extern void unmanage_window( struct gwm_window *window ) {
1560 xcb_window_t w = window->w;
1561 struct gwm_window *frame = window->u.managed.frame;
1562 uint32_t border_width = window->u.managed.border_width;
1563 int x, y;
1564 union callback_param cp;
1566 if( focus_frame == frame )
1567 focus_frame = NULL;
1569 stop_listening( window );
1571 /* Ignore Window errors on the child, because it might be destroyed
1572 before we are notified. */
1574 handle_error_reply( xcb_change_save_set_checked(
1575 c, XCB_SET_MODE_DELETE, w ), ERR_MASK_WINDOW );
1577 /* Reparent the window back to the root; delete its WM_STATE
1578 property (see ICCCM 4.1.4); and map it if it was iconic. */
1579 translate_frame_to_child( &x, &y, frame->u.frame.x, frame->u.frame.y,
1580 border_width, window->u.managed.win_gravity );
1581 handle_error_reply( xcb_reparent_window_checked(
1582 c, w, screens[ window->screen ]->root,
1583 x, y ), ERR_MASK_WINDOW );
1585 handle_error_reply( xcb_delete_property_checked(
1586 c, w, atoms[ ATOM_WM_STATE ] ), ERR_MASK_WINDOW );
1588 handle_error_reply( xcb_configure_window_checked(
1589 c, w, XCB_CONFIG_WINDOW_BORDER_WIDTH,
1590 &border_width ), ERR_MASK_WINDOW );
1592 if( window->u.managed.state == STATE_ICONIC )
1593 handle_error_reply( xcb_map_window_checked( c, w ), ERR_MASK_WINDOW );
1595 cp.p = frame;
1596 handle_async_reply( xcb_destroy_window_checked( c, frame->w ).sequence,
1597 handle_destroy_window, cp );
1599 if( window->u.managed.name )
1600 free( window->u.managed.name );
1602 forget_window( window );
1603 forget_window( frame->u.frame.button );
1605 /* We can't completely dismiss the frame window yet. Until our
1606 DestroyWindow is handled, other events might still arrive for
1607 it: in particular, we might see a MapRequest indicating a
1608 transition back to the Normal state before the adoption by the
1609 root has occurred. */
1610 frame->type = WINDOW_CHILDLESS;
1613 extern void iconic_to_normal( struct gwm_window *window ) {
1615 assert( window->type == WINDOW_MANAGED );
1617 set_managed_state( window, STATE_NORMAL );
1618 xcb_map_window( c, window->w );
1619 xcb_map_window( c, window->u.managed.frame->w );
1622 extern void deactivate_focus_frame( void ) {
1624 uint32_t n;
1626 if( !focus_frame )
1627 return;
1629 n = gwm_screens[ focus_frame->screen ].pixels[ COL_FRAME_INACTIVE ];
1630 xcb_change_window_attributes( c, focus_frame->w, XCB_CW_BACK_PIXEL, &n );
1632 queue_window_update( focus_frame, 0, 0, focus_frame->u.frame.width,
1633 focus_frame->u.frame.height, FALSE );
1636 extern void generic_expose( struct gwm_window *window,
1637 xcb_expose_event_t *ev ) {
1639 queue_window_update( window, ev->x, ev->y, ev->width, ev->height, TRUE );
1642 xcb_window_t passive_grab; /* the window which has the pointer automatically
1643 grabbed via a button press, or XCB_NONE */
1645 static void handle_fake_change_property( unsigned int sequence, void *reply,
1646 xcb_generic_error_t *error,
1647 union callback_param cp ) {
1649 xcb_selection_notify_event_t *p = cp.p;
1651 assert( !reply );
1653 if( error ) {
1654 free( error );
1656 p->property = XCB_NONE;
1659 handle_error_reply( xcb_send_event_checked( c, FALSE, p->requestor, 0,
1660 (char *) p ), ERR_MASK_WINDOW );
1662 free( p );
1665 static void fake_selection_request( struct gwm_window *window,
1666 xcb_selection_request_event_t *ev ) {
1668 if( ev->time != XCB_CURRENT_TIME && ev->time < window->u.fake.timestamp &&
1669 ev->target == atoms[ ATOM_VERSION ] ) {
1670 /* ICCCM version identification: ICCCM 2.0, section 4.3. */
1671 static const uint32_t values[ 2 ] = { 2, 0 };
1672 struct xcb_selection_notify_event_t *p = xmalloc( sizeof *p );
1673 union callback_param cp;
1675 p->requestor = ev->requestor;
1676 p->selection = ev->selection;
1677 p->target = ev->target;
1678 p->property = ev->property;
1679 p->time = ev->time;
1681 handle_async_reply( xcb_change_property_checked(
1682 c, XCB_PROP_MODE_REPLACE, ev->requestor,
1683 atoms[ ATOM_VERSION ], INTEGER, 32, 2,
1684 values ).sequence, handle_fake_change_property,
1685 cp );
1686 } else {
1687 struct xcb_selection_notify_event_t not;
1689 not.requestor = ev->requestor;
1690 not.selection = ev->selection;
1691 not.target = ev->target;
1692 not.property = XCB_NONE;
1693 not.time = ev->time;
1695 handle_error_reply( xcb_send_event_checked( c, FALSE, not.requestor,
1696 0, (char *) &not ),
1697 ERR_MASK_WINDOW );
1701 static event_handler fake_handlers[] = {
1702 NULL, /* Error */
1703 NULL, /* Reply */
1704 NULL, /* KeyPress */
1705 NULL, /* KeyRelease */
1706 NULL, /* ButtonPress */
1707 NULL, /* ButtonRelease */
1708 NULL, /* MotionNotify */
1709 NULL, /* EnterNotify */
1710 NULL, /* LeaveNotify */
1711 NULL, /* FocusIn */
1712 NULL, /* FocusOut */
1713 NULL, /* KeymapNotify */
1714 NULL, /* Expose */
1715 NULL, /* GraphicsExpose */
1716 NULL, /* NoExposure */
1717 NULL, /* VisibilityNotify */
1718 NULL, /* CreateNotify */
1719 NULL, /* DestroyNotify */
1720 NULL, /* UnmapNotify */
1721 NULL, /* MapNotify */
1722 NULL, /* MapRequest */
1723 NULL, /* ReparentNotify */
1724 NULL, /* ConfigureNotify */
1725 NULL, /* ConfigureRequest */
1726 NULL, /* GravityNotify */
1727 NULL, /* ResizeRequest */
1728 NULL, /* CirculateNotify */
1729 NULL, /* CirculateRequest */
1730 NULL, /* PropertyNotify */
1731 NULL, /* SelectionClear */
1732 (event_handler) fake_selection_request, /* SelectionRequest */
1733 NULL, /* SelectionNotify */
1734 NULL, /* ColormapNotify */
1735 NULL, /* ClientMessage */
1736 NULL, /* MappingNotify */
1737 NULL, /* (synthetic) */
1738 NULL /* ShapeNotify */
1741 static event_handler feedback_handlers[] = {
1742 NULL, /* Error */
1743 NULL, /* Reply */
1744 NULL, /* KeyPress */
1745 NULL, /* KeyRelease */
1746 NULL, /* ButtonPress */
1747 NULL, /* ButtonRelease */
1748 NULL, /* MotionNotify */
1749 NULL, /* EnterNotify */
1750 NULL, /* LeaveNotify */
1751 NULL, /* FocusIn */
1752 NULL, /* FocusOut */
1753 NULL, /* KeymapNotify */
1754 (event_handler) generic_expose,
1755 NULL, /* GraphicsExpose */
1756 NULL, /* NoExposure */
1757 NULL, /* VisibilityNotify */
1758 NULL, /* CreateNotify */
1759 NULL, /* DestroyNotify */
1760 NULL, /* UnmapNotify */
1761 NULL, /* MapNotify */
1762 NULL, /* MapRequest */
1763 NULL, /* ReparentNotify */
1764 NULL, /* ConfigureNotify */
1765 NULL, /* ConfigureRequest */
1766 NULL, /* GravityNotify */
1767 NULL, /* ResizeRequest */
1768 NULL, /* CirculateNotify */
1769 NULL, /* CirculateRequest */
1770 NULL, /* PropertyNotify */
1771 NULL, /* SelectionClear */
1772 NULL, /* SelectionRequest */
1773 NULL, /* SelectionNotify */
1774 NULL, /* ColormapNotify */
1775 NULL, /* ClientMessage */
1776 NULL, /* MappingNotify */
1777 NULL /* (synthetic) */
1780 static const event_handler *handlers[] = {
1781 root_handlers, managed_handlers, frame_handlers, button_handlers,
1782 fake_handlers, feedback_handlers, NULL, childless_handlers
1785 static void setup_display( void ) {
1787 int i;
1788 xcb_intern_atom_cookie_t atom_cookies[ NUM_ATOMS ];
1789 const xcb_setup_t *setup;
1790 xcb_screen_iterator_t iter;
1791 xcb_intern_atom_cookie_t *screen_atom_cookies;
1792 uint32_t n;
1793 xcb_get_selection_owner_cookie_t *screen_owner_cookies;
1794 xcb_window_t *screen_owners;
1795 xcb_client_message_event_t msg;
1796 xcb_void_cookie_t *cookies;
1797 xcb_query_tree_cookie_t *tree_cookies;
1799 table_init( &windows );
1800 table_init( &update_windows );
1802 c = xcb_connect( NULL, NULL );
1804 if( xcb_connection_has_error( c ) )
1805 fatal( "could not connect to server" );
1807 for( i = 0; i < NUM_EXTENSIONS; i++ )
1808 xcb_prefetch_extension_data( c, (xcb_extension_t *) extensions[ i ] );
1810 for( i = 0; i < NUM_ATOMS; i++ )
1811 atom_cookies[ i ] = xcb_intern_atom( c, 0, strlen( atom_names[ i ] ),
1812 atom_names[ i ] );
1814 setup = xcb_get_setup( c );
1816 iter = xcb_setup_roots_iterator( setup );
1817 num_screens = iter.rem;
1818 screens = xmalloc( num_screens * sizeof *screens );
1819 gwm_screens = xmalloc( num_screens * sizeof *gwm_screens );
1820 screen_atom_cookies = alloca( num_screens * sizeof *screen_atom_cookies );
1821 screen_owners = alloca( num_screens * sizeof *screen_owners );
1822 screen_owner_cookies = alloca( num_screens * sizeof *screen_owner_cookies );
1823 cookies = alloca( num_screens * sizeof *cookies );
1824 tree_cookies = alloca( num_screens * sizeof *tree_cookies );
1826 for( i = 0; iter.rem; i++, xcb_screen_next( &iter ) ) {
1827 static char wm_atom_str[ 16 ] = "WM_S";
1828 xcb_depth_iterator_t depth_iter;
1830 screens[ i ] = iter.data;
1831 screen_atom_cookies[ i ] =
1832 xcb_intern_atom( c, 0, 4 + sprintf( wm_atom_str + 4, "%d", i ),
1833 wm_atom_str );
1835 for( depth_iter = xcb_screen_allowed_depths_iterator( screens[ i ] );
1836 depth_iter.rem; xcb_depth_next( &depth_iter ) ) {
1837 xcb_visualtype_iterator_t visual_iter;
1839 for( visual_iter = xcb_depth_visuals_iterator( depth_iter.data );
1840 visual_iter.rem; xcb_visualtype_next( &visual_iter ) ) {
1841 xcb_visualtype_t *visual = visual_iter.data;
1843 if( screens[ i ]->root_visual == visual->visual_id ) {
1844 gwm_screens[ i ].root_visual = visual;
1845 goto visual_found;
1849 fatal( "Root visual for screen %d (0x%X) not found", i,
1850 screens[ i ]->root_visual );
1851 visual_found:
1855 fake_window = add_window( xcb_generate_id( c ) );
1856 fake_window->screen = 0;
1857 fake_window->type = WINDOW_FAKE;
1859 n = XCB_EVENT_MASK_PROPERTY_CHANGE;
1860 xcb_create_window( c, 0, fake_window->w, screens[ 0 ]->root, 0, 0, 1, 1, 0,
1861 XCB_WINDOW_CLASS_INPUT_ONLY, screens[ 0 ]->root_visual,
1862 XCB_CW_EVENT_MASK, &n );
1864 /* Obtain a timestamp we can use for our selection acquiry time
1865 (ICCCM 2.0, section 2.1). */
1866 xcb_change_property( c, XCB_PROP_MODE_APPEND, fake_window->w, WM_HINTS,
1867 WM_HINTS, 32, 0, NULL );
1869 xcb_flush( c ); /* BLOCK */
1871 for( i = 0; i < NUM_EXTENSIONS; i++ ) {
1872 const xcb_query_extension_reply_t *r;
1874 if( ( have_extension[ i ] =
1875 ( r = xcb_get_extension_data(
1876 c, (xcb_extension_t *) extensions[ i ] ) ) && r->present ) ) {
1877 extension_event[ i ] = r->first_event;
1878 extension_error[ i ] = r->first_error;
1882 for( i = 0; i < NUM_ATOMS; i++ ) {
1883 xcb_intern_atom_reply_t *r =
1884 xcb_intern_atom_reply( c, atom_cookies[ i ], NULL );
1886 atoms[ i ] = r->atom;
1887 free( r );
1890 /* FIXME Also monitor EWMH client window properties:
1891 _MOTIF_WM_HINTS, to see what decoration clients want
1892 _NET_WM_NAME, UTF-8 in preference to WM_NAME
1893 _NET_WM_ICON, ARGB icon(s)
1894 and set _NET_FRAME_EXTENTS. */
1895 prop_atoms[ PROP__NET_WM_NAME ] = atoms[ ATOM__NET_WM_NAME ];
1896 prop_atoms[ PROP_WM_COLORMAP_WINDOWS ] = atoms[ ATOM_WM_COLORMAP_WINDOWS ];
1897 prop_atoms[ PROP_WM_HINTS ] = WM_HINTS;
1898 prop_atoms[ PROP_WM_NAME ] = WM_NAME;
1899 prop_atoms[ PROP_WM_NORMAL_HINTS ] = WM_NORMAL_HINTS;
1900 prop_atoms[ PROP_WM_PROTOCOLS ] = atoms[ ATOM_WM_PROTOCOLS ];
1902 prop_types[ PROP__NET_WM_NAME ] = atoms[ ATOM_UTF8_STRING ];
1903 prop_types[ PROP_WM_COLORMAP_WINDOWS ] = WINDOW;
1904 prop_types[ PROP_WM_HINTS ] = WM_HINTS;
1905 prop_types[ PROP_WM_NAME ] = XCB_GET_PROPERTY_TYPE_ANY;
1906 prop_types[ PROP_WM_NORMAL_HINTS ] = WM_SIZE_HINTS;
1907 prop_types[ PROP_WM_PROTOCOLS ] = ATOM;
1909 for( i = 0; i < num_screens; i++ ) {
1910 xcb_intern_atom_reply_t *r =
1911 xcb_intern_atom_reply( c, screen_atom_cookies[ i ], NULL );
1913 gwm_screens[ i ].wm_atom = r->atom;
1914 free( r );
1917 #if USE_RENDER
1918 if( have_extension[ EXT_RENDER ] ) {
1919 xcb_render_query_version_cookie_t render_cookie;
1920 xcb_render_query_pict_formats_cookie_t formats_cookie;
1921 xcb_render_query_version_reply_t *render_reply;
1922 xcb_render_query_pict_formats_reply_t *formats_reply;
1923 xcb_render_pictforminfo_t *formats;
1924 int i;
1925 xcb_render_pictscreen_iterator_t siter;
1927 render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION,
1928 XCB_RENDER_MINOR_VERSION );
1930 formats_cookie = xcb_render_query_pict_formats( c );
1932 xcb_flush( c ); /* BLOCK */
1934 render_reply = xcb_render_query_version_reply( c, render_cookie, NULL );
1936 if( !render_reply ) {
1937 have_extension[ EXT_RENDER ] = FALSE;
1938 goto no_render;
1941 if( !render_reply->major_version && render_reply->minor_version < 1 ) {
1942 /* We need at least version 0.1, for FillRectangles support. It's
1943 too inconvenient to initialise pictures to a constant colour
1944 without it (which seems to have been an oversight in the
1945 original specification of the protocol). */
1946 have_extension[ EXT_RENDER ] = FALSE;
1948 free( render_reply );
1950 goto no_render;
1953 free( render_reply );
1955 formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie,
1956 NULL );
1958 formats = xcb_render_query_pict_formats_formats( formats_reply );
1960 for( i = 0; i < formats_reply->num_formats; i++ )
1961 if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1962 formats[ i ].direct.alpha_mask == 0xFF ) {
1963 if( !formats[ i ].direct.red_mask &&
1964 !formats[ i ].direct.green_mask &&
1965 !formats[ i ].direct.blue_mask ) {
1966 fmt_a8 = formats[ i ].id;
1967 memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 );
1968 } else if( formats[ i ].direct.red_mask == 0xFF &&
1969 formats[ i ].direct.green_mask == 0xFF &&
1970 formats[ i ].direct.blue_mask == 0xFF ) {
1971 fmt_rgba8 = formats[ i ].id;
1972 memcpy( &dfmt_rgba8, &formats[ i ].direct,
1973 sizeof dfmt_rgba8 );
1977 if( !fmt_a8 || !fmt_rgba8 )
1978 /* These two pictformats are guaranteed by the RENDER protocol
1979 (section 7). We can't cope without them. */
1980 have_extension[ EXT_RENDER ] = FALSE;
1982 for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
1983 formats_reply ); siter.rem;
1984 i++, xcb_render_pictscreen_next( &siter ) ) {
1985 xcb_render_pictdepth_iterator_t diter;
1987 for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
1988 diter.rem; xcb_render_pictdepth_next( &diter ) ) {
1989 xcb_render_pictvisual_iterator_t viter;
1991 for( viter = xcb_render_pictdepth_visuals_iterator(
1992 diter.data ); viter.rem;
1993 xcb_render_pictvisual_next( &viter ) )
1994 if( viter.data->visual ==
1995 gwm_screens[ i ].root_visual->visual_id ) {
1996 gwm_screens[ i ].root_pictformat = viter.data->format;
1997 goto next_screen;
2000 next_screen:
2004 free( formats_reply );
2006 no_render:
2009 #endif
2011 #if USE_RENDER
2012 if( have_extension[ EXT_RENDER ] ) {
2013 decorate_render_init();
2014 update_window = render_update_window;
2015 } else
2016 #endif
2018 decorate_core_init();
2019 update_window = core_update_window;
2022 /* Listen for a selection timestamp. */
2023 for(;;) {
2024 /* BLOCK */
2025 xcb_generic_event_t *ev = wait_for_event();
2027 if( !ev )
2028 fatal( "did not receive property notification" );
2030 if( ev->response_type & SEND_EVENT_MASK ) {
2031 free( ev );
2033 continue; /* ignore synthetic events */
2034 } else if( ev->response_type == XCB_PROPERTY_NOTIFY ) {
2035 xcb_property_notify_event_t *not =
2036 (xcb_property_notify_event_t *) ev;
2038 if( not->window != fake_window->w ||
2039 not->atom != WM_HINTS ||
2040 not->state != XCB_PROPERTY_NEW_VALUE ) {
2041 warning( "unexpected property notification" );
2043 continue;
2046 fake_window->u.fake.timestamp = not->time;
2048 free( ev );
2050 break;
2051 } else if( ev->response_type != XCB_MAPPING_NOTIFY ) {
2052 /* Ignore MappingNotify -- we'll query the mappings later. */
2053 warning( "unexpected event while waiting for property "
2054 "notification" );
2056 #if DEBUG
2057 show_event( ev );
2058 #endif
2060 free( ev );
2064 n = 0;
2065 xcb_change_window_attributes( c, fake_window->w, XCB_CW_EVENT_MASK, &n );
2067 do {
2068 int existing_manager;
2069 int managers_changed;
2070 sigset_t sigs, oldsigs;
2072 for( i = 0; i < num_screens; i++ )
2073 screen_owner_cookies[ i ] =
2074 xcb_get_selection_owner( c, gwm_screens[ i ].wm_atom );
2076 xcb_flush( c ); /* BLOCK */
2078 existing_manager = 0;
2079 for( i = 0; i < num_screens; i++ ) {
2080 xcb_get_selection_owner_reply_t *r =
2081 xcb_get_selection_owner_reply( c, screen_owner_cookies[ i ],
2082 NULL );
2084 if( ( screen_owners[ i ] = r->owner ) != XCB_NONE ) {
2085 if( !flag_replace )
2086 printf( "Screen %d is already managed.\n", i );
2088 existing_manager++;
2091 free( r );
2094 if( existing_manager && !flag_replace ) {
2095 printf( "Use \"%s --replace\" to replace any existing "
2096 "window manager(s).\n", argv0 );
2098 exit( 1 );
2101 if( existing_manager ) {
2102 uint32_t n;
2104 /* Wait for existing manager(s) to terminate (ICCCM 2.0,
2105 section 2.8). */
2106 n = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
2107 for( i = 0; i < num_screens; i++ )
2108 if( screen_owners[ i ] != XCB_NONE )
2109 xcb_change_window_attributes( c, screen_owners[ i ],
2110 XCB_CW_EVENT_MASK, &n );
2112 for( i = 0; i < num_screens; i++ )
2113 screen_owner_cookies[ i ] =
2114 xcb_get_selection_owner( c, gwm_screens[ i ].wm_atom );
2116 xcb_flush( c ); /* BLOCK */
2118 managers_changed = FALSE;
2119 for( i = 0; i < num_screens; i++ ) {
2120 xcb_get_selection_owner_reply_t *r =
2121 xcb_get_selection_owner_reply( c, screen_owner_cookies[ i ],
2122 NULL );
2124 if( r->owner != screen_owners[ i ] )
2125 managers_changed = TRUE;
2127 free( r );
2130 if( managers_changed ) {
2131 /* Argh. A window manager changed during all our bookkeeping.
2132 Undo our notifications, and start again. */
2133 n = 0;
2134 for( i = 0; i < num_screens; i++ )
2135 if( screen_owners[ i ] != XCB_NONE )
2136 xcb_change_window_attributes( c, screen_owners[ i ],
2137 XCB_CW_EVENT_MASK, &n );
2139 continue;
2143 /* Acquire ownership of WM_Sn selections (ICCCM 2.0, section 4.3). */
2144 for( i = 0; i < num_screens; i++ ) {
2145 xcb_set_selection_owner( c, fake_window->w,
2146 gwm_screens[ i ].wm_atom,
2147 fake_window->u.fake.timestamp );
2148 screen_owner_cookies[ i ] =
2149 xcb_get_selection_owner( c, gwm_screens[ i ].wm_atom );
2152 xcb_flush( c ); /* BLOCK */
2153 sync_with_callback( screen_owner_cookies[ num_screens - 1 ].sequence );
2155 for( i = 0; i < num_screens; i++ ) {
2156 xcb_get_selection_owner_reply_t *r =
2157 xcb_get_selection_owner_reply( c, screen_owner_cookies[ i ],
2158 NULL );
2160 if( r->owner != fake_window->w )
2161 fatal( "did not acquire window manager selection" );
2163 free( r );
2166 alarm( 3 );
2168 /* Listen for DestroyNotify on any selection holders. */
2169 while( existing_manager ) {
2170 static int killed_existing_managers;
2172 xcb_generic_event_t *ev = wait_for_event(); /* BLOCK */
2174 if( !ev ) {
2175 if( flag_force && signal_caught == SIGALRM &&
2176 !killed_existing_managers ) {
2177 signal_caught = 0;
2179 for( i = 0; i < num_screens; i++ )
2180 if( screen_owners[ i ] )
2181 xcb_kill_client( c, screen_owners[ i ] );
2183 killed_existing_managers = TRUE;
2185 alarm( 3 );
2187 continue;
2190 fatal( "did not receive destroy notification" );
2193 if( ev->response_type & SEND_EVENT_MASK ) {
2194 free( ev );
2196 continue; /* ignore synthetic events */
2197 } else if( ev->response_type == XCB_DESTROY_NOTIFY ) {
2198 xcb_destroy_notify_event_t *not =
2199 (xcb_destroy_notify_event_t *) ev;
2201 for( i = 0; i < num_screens; i++ )
2202 if( not->window == screen_owners[ i ] ) {
2203 screen_owners[ i ] = XCB_NONE;
2204 existing_manager--;
2207 free( ev );
2208 } else if( ev->response_type == XCB_SELECTION_CLEAR )
2209 /* We lost a window manager selection before we even
2210 started. Just shut down quietly. */
2211 exit( 0 );
2212 else if( ev->response_type == XCB_MAPPING_NOTIFY )
2213 /* Ignore MappingNotify. */
2214 free( ev );
2215 else if( !ev->response_type ) {
2216 /* Error. */
2217 xcb_generic_error_t *error = (xcb_generic_error_t *) ev;
2219 /* Expect Window and Value errors, because old window managers
2220 might die before we sent a corresponding request. */
2221 if( error->error_code != XCB_WINDOW &&
2222 error->error_code != XCB_VALUE )
2223 show_error( error );
2225 free( ev );
2226 } else {
2227 warning( "unexpected event while waiting for destroy "
2228 "notification" );
2230 #if DEBUG
2231 show_event( ev );
2232 #endif
2234 free( ev );
2238 sigfillset( &sigs );
2239 sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
2241 alarm( 0 );
2242 if( signal_caught == SIGALRM )
2243 signal_caught = 0;
2245 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
2246 } while( 0 );
2248 msg.response_type = XCB_CLIENT_MESSAGE;
2249 msg.format = 32;
2250 msg.sequence = 0;
2251 msg.type = atoms[ ATOM_MANAGER ];
2252 msg.data.data32[ 0 ] = fake_window->u.fake.timestamp;
2253 /* Data32[ 1 ] varies; will be completed later. */
2254 msg.data.data32[ 2 ] = fake_window->w;
2255 msg.data.data32[ 3 ] = 0;
2256 msg.data.data32[ 4 ] = 0;
2258 n = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
2259 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
2261 for( i = 0; i < num_screens; i++ ) {
2262 /* Send StructureNotify ClientMessage (ICCCM 2.0, section 2.8). */
2263 msg.window = screens[ i ]->root;
2264 msg.data.data32[ 1 ] = gwm_screens[ i ].wm_atom;
2265 xcb_send_event( c, FALSE, screens[ i ]->root,
2266 XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &msg );
2268 cookies[ i ] =
2269 xcb_change_window_attributes_checked( c, screens[ i ]->root,
2270 XCB_CW_EVENT_MASK, &n );
2272 tree_cookies[ i ] = xcb_query_tree( c, screens[ i ]->root );
2275 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE, XCB_INPUT_FOCUS_POINTER_ROOT,
2276 fake_window->u.fake.timestamp );
2278 get_keyboard_mapping( setup->min_keycode,
2279 setup->max_keycode - setup->min_keycode + 1 );
2280 get_modifier_mapping();
2282 xcb_flush( c ); /* BLOCK */
2283 sync_with_callback( cookies[ num_screens - 1 ].sequence );
2285 for( i = 0; i < num_screens; i++ ) {
2286 xcb_generic_error_t *error = xcb_request_check( c, cookies[ i ] );
2287 xcb_query_tree_reply_t *r;
2288 struct gwm_window *window;
2289 xcb_window_t *children;
2290 int j;
2292 if( error )
2293 fatal( "could not acquire SubstructureRedirect" );
2295 window = add_window( screens[ i ]->root );
2296 window->screen = i;
2297 window->type = WINDOW_ROOT;
2299 gwm_screens[ i ].cmap = XCB_NONE;
2300 gwm_screens[ i ].cmap_time = fake_window->u.fake.timestamp;
2302 r = xcb_query_tree_reply( c, tree_cookies[ i ], NULL );
2303 children = xcb_query_tree_children( r );
2304 for( j = 0; j < r->children_len; j++ )
2305 manage_window( children[ j ], FALSE );
2307 free( r );
2311 static void handle_mapping_notify( xcb_mapping_notify_event_t *ev ) {
2313 switch( ev->request ) {
2314 case XCB_MAPPING_MODIFIER:
2315 get_modifier_mapping();
2316 break;
2318 case XCB_MAPPING_KEYBOARD:
2319 get_keyboard_mapping( ev->first_keycode, ev->count );
2320 break;
2322 case XCB_MAPPING_POINTER:
2323 break;
2327 static void handle_client_message( xcb_client_message_event_t *ev ) {
2329 if( ev->type == atoms[ ATOM_WM_CHANGE_STATE ] ) {
2330 struct gwm_window *window;
2332 if( ( window = lookup_window( ev->window ) ) &&
2333 window->type == WINDOW_MANAGED &&
2334 window->u.managed.state == STATE_NORMAL &&
2335 ev->data.data32[ 0 ] == STATE_ICONIC ) {
2336 uint32_t n;
2338 set_managed_state( window, STATE_ICONIC );
2339 /* We need to unmap our child without invoking the normal
2340 UnmapNotify handler response. Unfortunately it's very
2341 difficult to communicate to the handler the distinction
2342 between the resultant UnmapNotify from an unmap here
2343 and an unrelated event. Checking the request sequence
2344 is not robust, because asynchronous events might not be
2345 assigned unique sequence numbers. Instead, we grab
2346 the server and temporarily change our event mask, which
2347 seems a heavyweight approach but does guarantee that
2348 only this event will be ignored. */
2349 xcb_grab_server( c );
2351 n = XCB_EVENT_MASK_PROPERTY_CHANGE |
2352 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
2353 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
2355 xcb_unmap_window( c, window->w );
2357 n = XCB_EVENT_MASK_STRUCTURE_NOTIFY |
2358 XCB_EVENT_MASK_PROPERTY_CHANGE |
2359 XCB_EVENT_MASK_COLOR_MAP_CHANGE;
2360 xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n );
2362 xcb_ungrab_server( c );
2364 xcb_unmap_window( c, window->u.managed.frame->w );
2366 } else if( ev->type == atoms[ ATOM_WM_COLORMAP_NOTIFY ] ) {
2367 struct gwm_window *window;
2369 if( ( window = lookup_window( ev->window ) ) &&
2370 window->type == WINDOW_ROOT &&
2371 !ev->data.data32[ 1 ] )
2372 /* Ignore the case where the client is starting installation:
2373 if it is well-behaved, it will do so only when the pointer
2374 is grabbed (in which case we won't do anything to conflict
2375 with it); if it is not well-behaved, it's not worth trying
2376 to accomodate it. */
2377 install_window_colormap( window->screen, focus_frame ?
2378 focus_frame->u.frame.child : NULL,
2379 ev->data.data32[ 0 ] );
2382 /* FIXME and all the EWMH client messages:
2383 _NET_CLOSE_WINDOW
2384 _NET_MOVE_RESIZE_WINDOW
2385 _NET_WM_MOVERESIZE
2386 _NET_RESTACK_WINDOW
2387 _NET_REQUEST_FRAME_EXTENTS */
2390 static void handle_events( void ) {
2392 xcb_generic_event_t *ev;
2394 /* FIXME Should read as many events as possible (loop on
2395 xcb_poll_for_event), and queue them... might be able to
2396 elide some event processing that way if later events
2397 supercede earlier ones. */
2398 while( ( ev = wait_for_event() ) ) {
2399 int event_type = ev->response_type;
2400 int event_base_type = event_type & 0x7F;
2401 xcb_window_t w;
2403 /* Determine event window and time. */
2404 switch( event_base_type ) {
2405 case 0: /* Error */
2406 case 1: /* Reply */
2407 case XCB_KEYMAP_NOTIFY:
2408 case XCB_CLIENT_MESSAGE:
2409 case XCB_MAPPING_NOTIFY:
2410 w = XCB_NONE;
2411 break;
2413 case XCB_KEY_PRESS:
2414 w = ( (xcb_key_press_event_t *) ev )->event;
2415 latest_timestamp = ( (xcb_key_press_event_t *) ev )->time;
2416 break;
2418 case XCB_KEY_RELEASE:
2419 w = ( (xcb_key_release_event_t *) ev )->event;
2420 latest_timestamp = ( (xcb_key_release_event_t *) ev )->time;
2421 break;
2423 case XCB_BUTTON_PRESS:
2424 w = ( (xcb_button_press_event_t *) ev )->event;
2425 latest_timestamp = ( (xcb_button_press_event_t *) ev )->time;
2426 break;
2428 case XCB_BUTTON_RELEASE:
2429 w = ( (xcb_button_release_event_t *) ev )->event;
2430 latest_timestamp = ( (xcb_button_release_event_t *) ev )->time;
2431 break;
2433 case XCB_MOTION_NOTIFY:
2434 w = ( (xcb_motion_notify_event_t *) ev )->event;
2435 latest_timestamp = ( (xcb_motion_notify_event_t *) ev )->time;
2436 break;
2438 case XCB_ENTER_NOTIFY:
2439 case XCB_LEAVE_NOTIFY:
2440 w = ( (xcb_enter_notify_event_t *) ev )->event;
2441 latest_timestamp = ( (xcb_enter_notify_event_t *) ev )->time;
2442 break;
2444 case XCB_FOCUS_IN:
2445 case XCB_FOCUS_OUT:
2446 w = ( (xcb_focus_in_event_t *) ev )->event;
2447 break;
2449 case XCB_EXPOSE:
2450 w = ( (xcb_expose_event_t *) ev )->window;
2451 break;
2453 case XCB_GRAPHICS_EXPOSURE:
2454 w = ( (xcb_graphics_exposure_event_t *) ev )->drawable;
2455 break;
2457 case XCB_NO_EXPOSURE:
2458 w = ( (xcb_no_exposure_event_t *) ev )->drawable;
2459 break;
2461 case XCB_VISIBILITY_NOTIFY:
2462 w = ( (xcb_visibility_notify_event_t *) ev )->window;
2463 break;
2465 case XCB_CREATE_NOTIFY:
2466 w = ( (xcb_create_notify_event_t *) ev )->parent;
2467 break;
2469 case XCB_DESTROY_NOTIFY:
2470 w = ( (xcb_destroy_notify_event_t *) ev )->event;
2471 break;
2473 case XCB_UNMAP_NOTIFY:
2474 w = ( (xcb_unmap_notify_event_t *) ev )->event;
2475 break;
2477 case XCB_MAP_NOTIFY:
2478 w = ( (xcb_map_notify_event_t *) ev )->event;
2479 break;
2481 case XCB_MAP_REQUEST:
2482 w = ( (xcb_map_request_event_t *) ev )->parent;
2483 break;
2485 case XCB_REPARENT_NOTIFY:
2486 w = ( (xcb_reparent_notify_event_t *) ev )->event;
2487 break;
2489 case XCB_CONFIGURE_NOTIFY:
2490 w = ( (xcb_configure_notify_event_t *) ev )->event;
2491 break;
2493 case XCB_CONFIGURE_REQUEST:
2494 w = ( (xcb_configure_request_event_t *) ev )->parent;
2495 break;
2497 case XCB_GRAVITY_NOTIFY:
2498 w = ( (xcb_gravity_notify_event_t *) ev )->event;
2499 break;
2501 case XCB_RESIZE_REQUEST:
2502 w = ( (xcb_resize_request_event_t *) ev )->window;
2503 break;
2505 case XCB_CIRCULATE_NOTIFY:
2506 case XCB_CIRCULATE_REQUEST:
2507 w = ( (xcb_circulate_notify_event_t *) ev )->event;
2508 break;
2510 case XCB_PROPERTY_NOTIFY:
2511 w = ( (xcb_property_notify_event_t *) ev )->window;
2512 latest_timestamp = ( (xcb_property_notify_event_t *) ev )->time;
2513 break;
2515 case XCB_SELECTION_CLEAR:
2516 w = ( (xcb_selection_clear_event_t *) ev )->owner;
2517 latest_timestamp = ( (xcb_selection_clear_event_t *) ev )->time;
2518 break;
2520 case XCB_SELECTION_REQUEST:
2521 w = ( (xcb_selection_request_event_t *) ev )->owner;
2522 if( ( (xcb_selection_request_event_t *) ev )->time )
2523 latest_timestamp =
2524 ( (xcb_selection_request_event_t *) ev )->time;
2525 break;
2527 case XCB_SELECTION_NOTIFY:
2528 w = ( (xcb_selection_notify_event_t *) ev )->requestor;
2529 if( ( (xcb_selection_notify_event_t *) ev )->time )
2530 latest_timestamp =
2531 ( (xcb_selection_notify_event_t *) ev )->time;
2532 break;
2534 case XCB_COLORMAP_NOTIFY:
2535 w = ( (xcb_colormap_notify_event_t *) ev )->window;
2536 break;
2538 default:
2539 #if USE_SHAPE
2540 if( event_base_type == extension_event[ EXT_SHAPE ] ) {
2541 w = ( (xcb_shape_notify_event_t *) ev )->affected_window;
2542 event_type = SHAPE_NOTIFY;
2543 break;
2545 #endif
2546 warning( "Unknown event" );
2547 break;
2550 if( ev->response_type & 0x80 )
2551 event_type = SYNTHETIC_EVENT;
2553 /* Global handling for events (before specific window handler). */
2554 switch( event_type ) {
2555 case 0: /* Error */
2556 show_error( (xcb_generic_error_t *) ev );
2557 break;
2559 case 1: /* Reply */
2560 warning( "handle_events: received reply message" );
2561 break;
2563 case XCB_BUTTON_PRESS:
2564 if( initial_press( (xcb_button_press_event_t *) ev )
2565 && !passive_grab )
2566 passive_grab = w;
2567 break;
2569 case XCB_KEYMAP_NOTIFY:
2570 break;
2572 case XCB_SELECTION_CLEAR:
2573 /* We've lost the manager selection (see ICCCM 2.0, section
2574 2.8); close down gracefully. */
2575 free( ev );
2576 return;
2578 case XCB_MAPPING_NOTIFY:
2579 handle_mapping_notify( (xcb_mapping_notify_event_t *) ev );
2580 break;
2582 case SYNTHETIC_EVENT:
2583 if( ev->response_type == ( XCB_CLIENT_MESSAGE | SEND_EVENT_MASK ) )
2584 handle_client_message( (xcb_client_message_event_t *) ev );
2585 break;
2588 if( w != XCB_NONE ) {
2589 struct gwm_window *window;
2590 void ( *handler )( struct gwm_window *, xcb_generic_event_t * );
2592 if( ( window = lookup_window( w ) ) &&
2593 handlers[ window->type ] &&
2594 ( handler = handlers[ window->type ][ event_type ] ) )
2595 handler( window, ev );
2598 /* Global handling for events (after specific window handler). */
2599 switch( event_type ) {
2600 case XCB_BUTTON_RELEASE:
2601 if( final_release( (xcb_button_release_event_t *) ev ) )
2602 passive_grab = XCB_NONE;
2603 break;
2606 /* FIXME other features:
2607 - make override-redirect windows translucent
2608 - desktop menu & other key/pointer bindings
2609 - when in the window menu, turn all windows translucent
2610 except for the selected item (temporarily raise that one?)
2611 - root background/cursor? (no; xsetroot for non composite case)
2612 - standard colourmaps? (no; xstdcmap does that)
2613 - DPMS
2614 - add debugging structure to detect memory leaks, and maybe even
2615 correlating X protocol errors to request call sites
2616 - other extensions... XINERAMA? RANDR? RandR should send
2617 ConfigureNotify to the root when resolution changes, so don't
2618 need the extension... test it. */
2620 free( ev );
2624 static void handle_intern_atom( unsigned int sequence, void *reply,
2625 xcb_generic_error_t *error,
2626 union callback_param p ) {
2628 if( error )
2629 free( error );
2631 if( reply )
2632 free( reply );
2635 static void shutdown_display( void ) {
2637 int i;
2638 uint32_t n;
2639 xcb_intern_atom_cookie_t cookie;
2640 union callback_param cp;
2641 xcb_query_tree_cookie_t *tree_cookies =
2642 alloca( num_screens * sizeof *tree_cookies );
2644 /* Query the window list from the server, so that we can reparent
2645 the windows bottom-to-top and therefore avoid disturbing the
2646 stacking order. Of course, if we kept stacking order information
2647 ourselves, this query would not be necessary. */
2648 for( i = 0; i < num_screens; i++ )
2649 tree_cookies[ i ] = xcb_query_tree( c, screens[ i ]->root );
2651 xcb_flush( c ); /* BLOCK */
2653 for( i = 0; i < num_screens; i++ ) {
2654 xcb_query_tree_reply_t *r;
2655 xcb_window_t *children;
2656 struct gwm_window *window;
2657 int j;
2659 r = xcb_query_tree_reply( c, tree_cookies[ i ], NULL );
2660 children = xcb_query_tree_children( r );
2661 for( j = 0; j < r->children_len; j++ )
2662 if( ( window = lookup_window( children[ j ] ) ) &&
2663 window->type == WINDOW_FRAME )
2664 unmanage_window( window->u.frame.child );
2666 free( r );
2669 /* The ICCCM says that using CurrentTime for SetInputFocus is naughty
2670 (section 4.2.7). But we don't have much alternative in this case:
2671 if we're shutting down in response to a signal, for instance. */
2672 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE, XCB_INPUT_FOCUS_POINTER_ROOT,
2673 XCB_CURRENT_TIME );
2675 n = 0;
2676 for( i = 0; i < num_screens; i++ )
2677 xcb_change_window_attributes( c, screens[ i ]->root,
2678 XCB_CW_EVENT_MASK, &n );
2680 #if USE_RENDER
2681 if( have_extension[ EXT_RENDER ] )
2682 decorate_render_done();
2683 else
2684 #endif
2685 decorate_core_done();
2687 /* A pointless request to generate a reply, so we'll know when we've
2688 processed everything the server will send us. */
2689 cookie = xcb_intern_atom( c, TRUE, 4, "ATOM" );
2690 cp.l = 0;
2691 handle_async_reply( cookie.sequence, handle_intern_atom, cp );
2693 xcb_flush( c );
2695 sync_with_callback( cookie.sequence );
2697 xcb_disconnect( c );
2698 #if DEBUG
2699 /* Be pedantic about deallocating memory, to assist any tools that are
2700 looking for leaks. */
2701 forget_window( fake_window );
2703 retry_root:
2704 for( i = 0; i < windows.used; i++ )
2705 if( windows.values[ i ]->type == WINDOW_ROOT ) {
2706 forget_window( windows.values[ i ] );
2708 goto retry_root;
2711 /* All windows have now been deallocated -- release the tables, too. */
2712 table_destroy( &windows );
2713 table_destroy( &update_windows );
2715 cleanup_keyboard();
2717 free( screens );
2718 free( gwm_screens );
2720 cleanup_utf8();
2722 #if HAVE_MTRACE
2723 muntrace();
2724 #endif
2725 #endif
2728 static void unknown( char *opt ) {
2730 printf( "%s: unknown option %s\n"
2731 "Usage: %s [OPTION]...\n"
2732 "Try \"%s --help\" for more information.\n",
2733 argv0, opt, argv0, argv0 );
2736 extern int main( int argc, char *argv[] ) {
2738 int i, j;
2739 struct sigaction sa;
2740 int which_signal;
2741 static int flag_help, flag_version;
2742 static const struct option {
2743 const char *opt;
2744 int *flag;
2745 } options[] = {
2746 #if DEBUG
2747 { "debug", &flag_debug },
2748 #endif
2749 { "force", &flag_force },
2750 { "help", &flag_help },
2751 { "replace", &flag_replace },
2752 { "version", &flag_version }
2754 #define NUM_OPTIONS ( sizeof options / sizeof *options )
2756 #if DEBUG && HAVE_MTRACE
2757 mtrace();
2758 #endif
2760 argv0 = argv[ 0 ];
2762 for( i = 1; i < argc; i++ )
2763 if( argv[ i ][ 0 ] == '-' ) {
2764 if( argv[ i ][ 1 ] == '-' && argv[ i ][ 2 ] ) {
2765 for( j = 0; j < NUM_OPTIONS; j++ )
2766 if( !strncmp( argv[ i ] + 2, options[ j ].opt,
2767 strlen( argv[ i ] + 2 ) ) ) {
2768 *options[ j ].flag = 1;
2769 break;
2772 if( j == NUM_OPTIONS ) {
2773 unknown( argv[ i ] );
2775 return 1;
2777 } else if( argv[ i ][ 1 ] != '-' ) {
2778 for( j = 0; j < NUM_OPTIONS; j++ )
2779 if( argv[ i ][ 1 ] == options[ j ].opt[ 0 ] &&
2780 !argv[ i ][ 2 ] ) {
2781 *options[ j ].flag = 1;
2782 break;
2785 if( j == NUM_OPTIONS ) {
2786 unknown( argv[ i ] );
2788 return 1;
2791 } else {
2792 unknown( argv[ i ] );
2794 return 1;
2797 if( flag_help ) {
2798 printf( "Usage: %s [OPTION]...\n"
2799 "Options:\n"
2800 #if DEBUG
2801 " -d, --debug Show all events received\n"
2802 #endif
2803 " -f, --force If replacing another window manager, "
2804 "terminate it if necessary\n"
2805 " -h, --help Display this information and exit\n"
2806 " -r, --replace Take over window management if another "
2807 "manager exists\n"
2808 " -v, --version Display version information and exit\n"
2809 "Please send bug reports to <" PACKAGE_BUGREPORT ">.\n",
2810 argv0 );
2812 return 0;
2815 if( flag_version ) {
2816 puts( PACKAGE_STRING "\nPlease send bug reports to <"
2817 PACKAGE_BUGREPORT ">." );
2819 return 0;
2822 sa.sa_handler = catch_signal;
2823 sigemptyset( &sa.sa_mask );
2824 sa.sa_flags = 0;
2825 sigaction( SIGHUP, &sa, NULL );
2826 sigaction( SIGINT, &sa, NULL );
2827 sigaction( SIGTERM, &sa, NULL );
2829 sa.sa_handler = alarm_signal;
2830 sigaction( SIGALRM, &sa, NULL );
2832 sa.sa_handler = child_signal;
2833 sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
2834 sigaction( SIGCHLD, &sa, NULL );
2836 setup_display();
2838 handle_events();
2839 which_signal = signal_caught;
2840 signal_caught = 0; /* acknowledge receipt, so we get normal event
2841 processing during shutdown */
2843 if( xcb_connection_has_error( c ) )
2844 /* Don't bother attempting orderly shutdown. */
2845 fatal( "server connection error" );
2847 shutdown_display();
2849 if( which_signal ) {
2850 raise( which_signal );
2852 return 1;
2855 return 0;
2858 /* FIXME xv moves when changing files (or pressing "n")... investigate. */