Handle weird reparenting cases.
[gwm.git] / root.c
blob14ece3421840cb068b541a5eda6b06f823ea9f78
1 /*
2 * root.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 <xcb/xcb.h>
28 #include "gwm.h"
30 #include "actions.h"
31 #include "frame.h"
32 #include "keyboard.h"
33 #include "managed.h"
34 #include "root.h"
35 #include "window-table.h"
37 static void root_key_press( struct gwm_window *window,
38 xcb_key_press_event_t *ev ) {
40 int i;
42 for( i = 0; i < num_key_actions; i++ )
43 if( keyboard_map[ ev->detail ][ 0 ] == key_actions[ i ].keysym &&
44 ( ev->state & 0xFF & key_actions[ i ].modifiers ) ==
45 key_actions[ i ].modifiers ) {
46 union callback_param cp;
48 cp.p = NULL;
49 key_actions[ i ].handler( focus_frame ? focus_frame : window,
50 (xcb_generic_event_t *) ev, cp );
52 break;
56 static const struct button_action {
57 int button;
58 xcb_mod_mask_t modifiers;
59 void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev,
60 union callback_param cp );
61 } button_actions[] = {
62 /* FIXME This table should be configurable, of course. */
63 { 1, 0, action_root_menu },
64 { 2, 0, action_window_list_menu },
65 { 3, 0, action_start_xterm }
68 #define NUM_BUTTON_ACTIONS ( sizeof button_actions / sizeof *button_actions )
70 static void root_button_press( struct gwm_window *window,
71 xcb_button_press_event_t *ev ) {
73 int i;
75 if( ev->child )
76 return;
78 for( i = 0; i < NUM_BUTTON_ACTIONS; i++ )
79 if( ev->detail == button_actions[ i ].button &&
80 ( ev->state & 0xFF & button_actions[ i ].modifiers ) ==
81 button_actions[ i ].modifiers ) {
82 union callback_param cp;
84 cp.p = NULL;
85 button_actions[ i ].handler( window, (xcb_generic_event_t *) ev,
86 cp );
88 break;
92 static void root_enter_notify( struct gwm_window *window,
93 xcb_enter_notify_event_t *ev ) {
95 if( focus_frame && ( ev->detail == XCB_NOTIFY_DETAIL_INFERIOR ||
96 ev->detail == XCB_NOTIFY_DETAIL_NONLINEAR ) ) {
97 deactivate_focus_frame();
99 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE,
100 XCB_INPUT_FOCUS_POINTER_ROOT, ev->time );
102 focus_frame = NULL;
105 install_window_colormap( window->screen, NULL, ev->time );
108 static void root_create_notify( struct gwm_window *window,
109 xcb_create_notify_event_t *ev ) {
111 /* We only ever update the window stack in response to notify
112 events from the server. The drawback to this approach is that
113 our stack can become slightly stale (recently transmitted requests
114 might not be reflected in our state). However, this policy has
115 the critical advantage that the window stack is always internally
116 consistent: if we updated it eagerly instead, that would introduce
117 the possibility of non-causal ordering of events. */
118 stack_insert_above( &window_stack, ev->window,
119 stack_lookup( &window_stack,
120 window->w | STACK_END )->lower_window );
123 static void root_destroy_notify( struct gwm_window *window,
124 xcb_destroy_notify_event_t *ev ) {
126 stack_remove( &window_stack, ev->window );
129 static void root_reparent_notify( struct gwm_window *window,
130 xcb_reparent_notify_event_t *ev ) {
132 if( ev->parent == window->w ) {
133 /* It's possible to reparent a window to its current parent,
134 so we can't assume the window is not already in the stack. */
135 if( stack_lookup( &window_stack, ev->window ) )
136 stack_move_above( &window_stack, ev->window,
137 stack_lookup( &window_stack, window->w |
138 STACK_END )->lower_window );
139 else
140 stack_insert_above( &window_stack, ev->window,
141 stack_lookup( &window_stack, window->w |
142 STACK_END )->lower_window );
143 } else
144 stack_remove( &window_stack, ev->window );
147 static void root_configure_notify( struct gwm_window *window,
148 xcb_configure_notify_event_t *ev ) {
150 stack_move_above( &window_stack, ev->window, ev->above_sibling ?
151 ev->above_sibling : window->w | STACK_END );
154 extern void root_circulate_request( struct gwm_window *window,
155 xcb_circulate_request_event_t *ev ) {
157 uint32_t value = ev->place == XCB_PLACE_ON_TOP ? XCB_STACK_MODE_ABOVE :
158 XCB_STACK_MODE_BELOW;
160 /* Circulating children of the root -- honour the request as is. */
161 xcb_configure_window( c, ev->window, XCB_CONFIG_WINDOW_STACK_MODE,
162 &value );
165 static void root_synthetic( struct gwm_window *root,
166 xcb_generic_event_t *ev ) {
168 xcb_unmap_notify_event_t *unmap = (xcb_unmap_notify_event_t *) ev;
169 xcb_configure_request_event_t *config =
170 (xcb_configure_request_event_t *) ev;
171 struct gwm_window *window;
173 switch( ev->response_type & ~SEND_EVENT_MASK ) {
174 case XCB_UNMAP_NOTIFY:
175 /* Handle a synthetic UnmapNotify request to move a window to
176 the Withdrawn state (see ICCCM 2.0, section 4.1.4). */
177 if( ( window = lookup_window( unmap->window ) ) &&
178 window->type == WINDOW_MANAGED )
179 unmanage_window( window );
181 break;
183 case XCB_CONFIGURE_REQUEST:
184 /* Handle a synthetic ConfigureRequest, which should be used
185 only to restack managed windows relative to windows which
186 were originally siblings (see ICCCM 4.1.5). */
187 if( ( config->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) &&
188 ( window = lookup_window( config->window ) ) &&
189 window->type == WINDOW_MANAGED ) {
190 struct gwm_window *sibling;
191 uint32_t values[ 2 ];
193 if( ( sibling = lookup_window( config->sibling ) ) &&
194 sibling->type == WINDOW_MANAGED )
195 sibling = sibling->u.managed.frame;
197 values[ 0 ] = sibling ? sibling->w : config->sibling;
198 values[ 1 ] = config->stack_mode;
200 handle_error_reply( xcb_configure_window_checked(
201 c, window->u.managed.frame->w,
202 XCB_CONFIG_WINDOW_SIBLING |
203 XCB_CONFIG_WINDOW_STACK_MODE,
204 values ),
205 ERR_MASK_VALUE | ERR_MASK_WINDOW |
206 ERR_MASK_MATCH );
209 break;
213 const event_handler root_handlers[] = {
214 NULL, /* Error */
215 NULL, /* Reply */
216 (event_handler) root_key_press,
217 NULL, /* KeyRelease */
218 (event_handler) root_button_press,
219 NULL, /* ButtonRelease */
220 NULL, /* MotionNotify */
221 (event_handler) root_enter_notify,
222 NULL, /* LeaveNotify */
223 NULL, /* FocusIn */
224 NULL, /* FocusOut */
225 NULL, /* KeymapNotify */
226 NULL, /* Expose */
227 NULL, /* GraphicsExpose */
228 NULL, /* NoExposure */
229 NULL, /* VisibilityNotify */
230 (event_handler) root_create_notify,
231 (event_handler) root_destroy_notify,
232 NULL, /* UnmapNotify */
233 NULL, /* MapNotify */
234 (event_handler) withdrawn_map_request,
235 (event_handler) root_reparent_notify,
236 (event_handler) root_configure_notify,
237 (event_handler) withdrawn_configure_request,
238 NULL, /* GravityNotify */
239 NULL, /* ResizeRequest */
240 NULL, /* CirculateNotify */
241 (event_handler) root_circulate_request,
242 NULL, /* PropertyNotify */
243 NULL, /* SelectionClear */
244 NULL, /* SelectionRequest */
245 NULL, /* SelectionNotify */
246 NULL, /* ColormapNotify */
247 NULL, /* ClientMessage */
248 NULL, /* MappingNotify */
249 (event_handler) root_synthetic,
250 NULL /* ShapeNotify */