Add support for the zoomed state to managed windows.
[gwm.git] / root.c
blob83176c1240f833d9fe658f6e2b2ad7528da00b53
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 #if USE_RANDR
27 #include <xcb/randr.h>
28 #endif
29 #include <xcb/xcb.h>
31 #include "gwm.h"
33 #include "actions.h"
34 #include "frame.h"
35 #include "keyboard.h"
36 #include "managed.h"
37 #include "root.h"
38 #include "window-table.h"
40 static void root_key_press( struct gwm_window *window,
41 xcb_key_press_event_t *ev ) {
43 int i;
45 for( i = 0; i < num_key_actions; i++ )
46 if( keyboard_map[ ev->detail ][ 0 ] == key_actions[ i ].keysym &&
47 ( ev->state & 0xFF & key_actions[ i ].modifiers ) ==
48 key_actions[ i ].modifiers ) {
49 union callback_param cp;
51 cp.p = NULL;
52 key_actions[ i ].handler( focus_frame ? focus_frame : window,
53 (xcb_generic_event_t *) ev, cp );
55 break;
59 static const struct button_action {
60 int button;
61 xcb_mod_mask_t modifiers;
62 void ( *handler )( struct gwm_window *window, xcb_generic_event_t *ev,
63 union callback_param cp );
64 } button_actions[] = {
65 /* FIXME This table should be configurable, of course. */
66 { 1, 0, action_root_menu },
67 { 2, 0, action_window_list_menu },
68 { 3, 0, action_start_xterm }
71 #define NUM_BUTTON_ACTIONS ( sizeof button_actions / sizeof *button_actions )
73 static void root_button_press( struct gwm_window *window,
74 xcb_button_press_event_t *ev ) {
76 int i;
78 if( ev->child )
79 return;
81 for( i = 0; i < NUM_BUTTON_ACTIONS; i++ )
82 if( ev->detail == button_actions[ i ].button &&
83 ( ev->state & 0xFF & button_actions[ i ].modifiers ) ==
84 button_actions[ i ].modifiers ) {
85 union callback_param cp;
87 cp.p = NULL;
88 button_actions[ i ].handler( window, (xcb_generic_event_t *) ev,
89 cp );
91 break;
95 static void root_enter_notify( struct gwm_window *window,
96 xcb_enter_notify_event_t *ev ) {
98 if( focus_frame && ( ev->detail == XCB_NOTIFY_DETAIL_INFERIOR ||
99 ev->detail == XCB_NOTIFY_DETAIL_NONLINEAR ) ) {
100 deactivate_focus_frame();
102 xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE,
103 XCB_INPUT_FOCUS_POINTER_ROOT, ev->time );
105 focus_frame = NULL;
108 install_window_colormap( window->screen, NULL, ev->time );
111 static void root_create_notify( struct gwm_window *window,
112 xcb_create_notify_event_t *ev ) {
114 /* We only ever update the window stack in response to notify
115 events from the server. The drawback to this approach is that
116 our stack can become slightly stale (recently transmitted requests
117 might not be reflected in our state). However, this policy has
118 the critical advantage that the window stack is always internally
119 consistent: if we updated it eagerly instead, that would introduce
120 the possibility of non-causal ordering of events. */
121 stack_insert_above( &window_stack, ev->window,
122 stack_lookup( &window_stack,
123 window->w | STACK_END )->lower_window );
126 static void root_destroy_notify( struct gwm_window *window,
127 xcb_destroy_notify_event_t *ev ) {
129 stack_remove( &window_stack, ev->window );
132 static void root_reparent_notify( struct gwm_window *window,
133 xcb_reparent_notify_event_t *ev ) {
135 if( ev->parent == window->w ) {
136 /* It's possible to reparent a window to its current parent,
137 so we can't assume the window is not already in the stack. */
138 if( stack_lookup( &window_stack, ev->window ) )
139 stack_move_above( &window_stack, ev->window,
140 stack_lookup( &window_stack, window->w |
141 STACK_END )->lower_window );
142 else
143 stack_insert_above( &window_stack, ev->window,
144 stack_lookup( &window_stack, window->w |
145 STACK_END )->lower_window );
146 } else
147 stack_remove( &window_stack, ev->window );
150 static void root_configure_notify( struct gwm_window *window,
151 xcb_configure_notify_event_t *ev ) {
153 if( ev->event == ev->window ) {
154 /* The root window itself was configured. */
155 if( screens[ window->screen ]->width_in_pixels !=
156 ev->width ||
157 screens[ window->screen ]->height_in_pixels !=
158 ev->height ) {
159 uint32_t values[ 4 ];
161 screens[ window->screen ]->width_in_pixels = ev->width;
162 screens[ window->screen ]->height_in_pixels = ev->height;
164 values[ 0 ] = ev->width;
165 values[ 1 ] = ev->height;
166 xcb_change_property( c, XCB_PROP_MODE_REPLACE, ev->window,
167 atoms[ ATOM__NET_DESKTOP_GEOMETRY ], CARDINAL,
168 32, 2, values );
170 values[ 0 ] = 0;
171 values[ 1 ] = 0;
172 values[ 2 ] = ev->width;
173 values[ 3 ] = ev->height;
174 xcb_change_property( c, XCB_PROP_MODE_REPLACE, ev->window,
175 atoms[ ATOM__NET_WORKAREA ], CARDINAL, 32, 4,
176 values );
178 } else
179 /* A child of the root was configured. */
180 stack_move_above( &window_stack, ev->window, ev->above_sibling ?
181 ev->above_sibling : window->w | STACK_END );
184 extern void root_circulate_request( struct gwm_window *window,
185 xcb_circulate_request_event_t *ev ) {
187 uint32_t value = ev->place == XCB_PLACE_ON_TOP ? XCB_STACK_MODE_ABOVE :
188 XCB_STACK_MODE_BELOW;
190 /* Circulating children of the root -- honour the request as is. */
191 xcb_configure_window( c, ev->window, XCB_CONFIG_WINDOW_STACK_MODE,
192 &value );
195 static void root_synthetic( struct gwm_window *root,
196 xcb_generic_event_t *ev ) {
198 xcb_unmap_notify_event_t *unmap = (xcb_unmap_notify_event_t *) ev;
199 xcb_configure_request_event_t *config =
200 (xcb_configure_request_event_t *) ev;
201 struct gwm_window *window;
203 switch( ev->response_type & ~SEND_EVENT_MASK ) {
204 case XCB_UNMAP_NOTIFY:
205 /* Handle a synthetic UnmapNotify request to move a window to
206 the Withdrawn state (see ICCCM 2.0, section 4.1.4). */
207 if( ( window = lookup_window( unmap->window ) ) &&
208 window->type == WINDOW_MANAGED )
209 unmanage_window( window );
211 break;
213 case XCB_CONFIGURE_REQUEST:
214 /* Handle a synthetic ConfigureRequest, which should be used
215 only to restack managed windows relative to windows which
216 were originally siblings (see ICCCM 4.1.5). */
217 if( ( config->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) &&
218 ( window = lookup_window( config->window ) ) &&
219 window->type == WINDOW_MANAGED ) {
220 struct gwm_window *sibling;
221 uint32_t values[ 2 ];
223 if( ( sibling = lookup_window( config->sibling ) ) &&
224 sibling->type == WINDOW_MANAGED )
225 sibling = sibling->u.managed.frame;
227 values[ 0 ] = sibling ? sibling->w : config->sibling;
228 values[ 1 ] = config->stack_mode;
230 handle_error_reply( xcb_configure_window_checked(
231 c, window->u.managed.frame->w,
232 XCB_CONFIG_WINDOW_SIBLING |
233 XCB_CONFIG_WINDOW_STACK_MODE,
234 values ),
235 ERR_MASK_VALUE | ERR_MASK_WINDOW |
236 ERR_MASK_MATCH );
239 break;
243 #if USE_RANDR
244 static void root_rr_crtc_change_notify( struct gwm_window *window,
245 xcb_randr_notify_event_t *ev ) {
247 int rotated = ev->u.cc.rotation & ( XCB_RANDR_ROTATION_ROTATE_90 |
248 XCB_RANDR_ROTATION_ROTATE_270 );
250 update_crtc( window->screen, ev->u.cc.crtc,
251 rotated ? ev->u.cc.height : ev->u.cc.width,
252 rotated ? ev->u.cc.width : ev->u.cc.height,
253 ev->u.cc.x, ev->u.cc.y );
255 #endif
257 const event_handler root_handlers[ NUM_EXTENDED_EVENTS ] = {
258 NULL, /* Error */
259 NULL, /* Reply */
260 (event_handler) root_key_press,
261 NULL, /* KeyRelease */
262 (event_handler) root_button_press,
263 NULL, /* ButtonRelease */
264 NULL, /* MotionNotify */
265 (event_handler) root_enter_notify,
266 NULL, /* LeaveNotify */
267 NULL, /* FocusIn */
268 NULL, /* FocusOut */
269 NULL, /* KeymapNotify */
270 NULL, /* Expose */
271 NULL, /* GraphicsExpose */
272 NULL, /* NoExposure */
273 NULL, /* VisibilityNotify */
274 (event_handler) root_create_notify,
275 (event_handler) root_destroy_notify,
276 NULL, /* UnmapNotify */
277 NULL, /* MapNotify */
278 (event_handler) withdrawn_map_request,
279 (event_handler) root_reparent_notify,
280 (event_handler) root_configure_notify,
281 (event_handler) withdrawn_configure_request,
282 NULL, /* GravityNotify */
283 NULL, /* ResizeRequest */
284 NULL, /* CirculateNotify */
285 (event_handler) root_circulate_request,
286 NULL, /* PropertyNotify */
287 NULL, /* SelectionClear */
288 NULL, /* SelectionRequest */
289 NULL, /* SelectionNotify */
290 NULL, /* ColormapNotify */
291 NULL, /* ClientMessage */
292 NULL, /* MappingNotify */
293 (event_handler) root_synthetic,
294 #if USE_RANDR
295 (event_handler) root_rr_crtc_change_notify, /* RRCrtcChangeNotify */
296 #else
297 NULL, /* RRCrtcChangeNotify */
298 #endif
299 NULL /* ShapeNotify */