1 /* Copyright (c) 2012, Bastien Dejean
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "subscribe.h"
37 void handle_event(xcb_generic_event_t *evt)
39 uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
44 case XCB_DESTROY_NOTIFY:
47 case XCB_UNMAP_NOTIFY:
50 case XCB_CLIENT_MESSAGE:
53 case XCB_CONFIGURE_REQUEST:
54 configure_request(evt);
56 case XCB_PROPERTY_NOTIFY:
59 case XCB_ENTER_NOTIFY:
62 case XCB_MOTION_NOTIFY:
72 if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
79 void map_request(xcb_generic_event_t *evt)
81 xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
83 schedule_window(e->window);
86 void configure_request(xcb_generic_event_t *evt)
88 xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
91 bool is_managed = locate_window(e->window, &loc);
92 client_t *c = (is_managed ? loc.node->client : NULL);
95 if (is_managed && !IS_FLOATING(c)) {
96 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
97 c->floating_rectangle.x = e->x;
99 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
100 c->floating_rectangle.y = e->y;
102 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
105 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
110 restrain_floating_width(c, &w);
111 c->floating_rectangle.width = w;
115 restrain_floating_height(c, &h);
116 c->floating_rectangle.height = h;
119 xcb_configure_notify_event_t evt;
120 unsigned int bw = c->border_width;
122 xcb_rectangle_t rect = get_rectangle(loc.monitor, loc.desktop, loc.node);
124 evt.response_type = XCB_CONFIGURE_NOTIFY;
125 evt.event = e->window;
126 evt.window = e->window;
127 evt.above_sibling = XCB_NONE;
130 evt.width = rect.width;
131 evt.height = rect.height;
132 evt.border_width = bw;
133 evt.override_redirect = false;
135 xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
137 if (c->state == STATE_PSEUDO_TILED) {
138 arrange(loc.monitor, loc.desktop);
143 unsigned short i = 0;
145 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
146 mask |= XCB_CONFIG_WINDOW_X;
149 c->floating_rectangle.x = e->x;
153 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
154 mask |= XCB_CONFIG_WINDOW_Y;
157 c->floating_rectangle.y = e->y;
161 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
162 mask |= XCB_CONFIG_WINDOW_WIDTH;
165 restrain_floating_width(c, &w);
166 c->floating_rectangle.width = w;
171 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
172 mask |= XCB_CONFIG_WINDOW_HEIGHT;
175 restrain_floating_height(c, &h);
176 c->floating_rectangle.height = h;
181 if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
182 mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
183 values[i++] = e->border_width;
186 if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
187 mask |= XCB_CONFIG_WINDOW_SIBLING;
188 values[i++] = e->sibling;
191 if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
192 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
193 values[i++] = e->stack_mode;
196 xcb_configure_window(dpy, e->window, mask, values);
198 if (is_managed && mask & XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT) {
199 xcb_rectangle_t r = c->floating_rectangle;
200 put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", loc.monitor->name, loc.desktop->name, e->window, r.width, r.height, r.x, r.y);
205 monitor_t *m = monitor_from_client(c);
206 adapt_geometry(&m->rectangle, &loc.monitor->rectangle, loc.node);
210 void destroy_notify(xcb_generic_event_t *evt)
212 xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
214 unmanage_window(e->window);
217 void unmap_notify(xcb_generic_event_t *evt)
219 xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
221 unmanage_window(e->window);
224 void property_notify(xcb_generic_event_t *evt)
226 xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
228 if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
233 if (!locate_window(e->window, &loc)) {
237 if (e->atom == XCB_ATOM_WM_HINTS) {
238 xcb_icccm_wm_hints_t hints;
239 if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
240 (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
241 set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
242 } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
243 client_t *c = loc.node->client;
244 xcb_size_hints_t size_hints;
245 if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &size_hints, NULL) == 1 &&
246 (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE))) {
247 c->min_width = size_hints.min_width;
248 c->max_width = size_hints.max_width;
249 c->min_height = size_hints.min_height;
250 c->max_height = size_hints.max_height;
251 int w = c->floating_rectangle.width;
252 int h = c->floating_rectangle.height;
253 restrain_floating_size(c, &w, &h);
254 c->floating_rectangle.width = w;
255 c->floating_rectangle.height = h;
256 arrange(loc.monitor, loc.desktop);
261 void client_message(xcb_generic_event_t *evt)
263 xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
265 if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
267 if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
268 focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
274 if (!locate_window(e->window, &loc)) {
278 if (e->type == ewmh->_NET_WM_STATE) {
279 handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
280 handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
281 } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
282 if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
283 loc.node == mon->desk->focus) {
286 focus_node(loc.monitor, loc.desktop, loc.node);
287 } else if (e->type == ewmh->_NET_WM_DESKTOP) {
289 if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
290 transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
292 } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
293 window_close(loc.node);
297 void focus_in(xcb_generic_event_t *evt)
299 xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
301 if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
302 || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
303 || e->detail == XCB_NOTIFY_DETAIL_NONE) {
307 if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
312 if (locate_window(e->event, &loc)) {
313 // prevent input focus stealing
314 update_input_focus();
318 void enter_notify(xcb_generic_event_t *evt)
320 xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
321 xcb_window_t win = e->event;
323 if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
324 (mon->desk->focus != NULL &&
325 mon->desk->focus->id == win)) {
329 xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, motion_recorder), NULL);
335 if (wa->map_state == XCB_MAP_STATE_UNMAPPED) {
336 enable_motion_recorder();
338 disable_motion_recorder();
342 void motion_notify(xcb_generic_event_t *evt)
344 xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
346 int dtime = e->time - last_motion_time;
348 last_motion_time = e->time;
349 last_motion_x = e->event_x;
350 last_motion_y = e->event_y;
354 int mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);
359 xcb_window_t win = XCB_NONE;
360 xcb_point_t pt = {e->root_x, e->root_y};
361 query_pointer(&win, NULL);
363 bool pfm_backup = pointer_follows_monitor;
364 bool pff_backup = pointer_follows_focus;
366 pointer_follows_monitor = false;
367 pointer_follows_focus = false;
369 if (locate_window(win, &loc)) {
370 if (loc.node != mon->desk->focus) {
371 focus_node(loc.monitor, loc.desktop, loc.node);
374 monitor_t *m = monitor_from_point(pt);
375 if (m != NULL && m != mon) {
376 focus_node(m, m->desk, m->desk->focus);
379 pointer_follows_monitor = pfm_backup;
380 pointer_follows_focus = pff_backup;
383 disable_motion_recorder();
386 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
388 if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
389 if (action == XCB_EWMH_WM_STATE_ADD) {
390 set_state(m, d, n, STATE_FULLSCREEN);
391 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
392 if (n->client->state == STATE_FULLSCREEN) {
393 set_state(m, d, n, n->client->last_state);
395 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
396 set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
399 } else if (state == ewmh->_NET_WM_STATE_BELOW) {
400 if (action == XCB_EWMH_WM_STATE_ADD) {
401 set_layer(m, d, n, LAYER_BELOW);
402 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
403 if (n->client->layer == LAYER_BELOW) {
404 set_layer(m, d, n, n->client->last_layer);
406 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
407 set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
409 } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
410 if (action == XCB_EWMH_WM_STATE_ADD) {
411 set_layer(m, d, n, LAYER_ABOVE);
412 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
413 if (n->client->layer == LAYER_ABOVE) {
414 set_layer(m, d, n, n->client->last_layer);
416 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
417 set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
419 } else if (state == ewmh->_NET_WM_STATE_STICKY) {
420 if (action == XCB_EWMH_WM_STATE_ADD) {
421 set_sticky(m, d, n, true);
422 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
423 set_sticky(m, d, n, false);
424 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
425 set_sticky(m, d, n, !n->sticky);
427 } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
428 if (action == XCB_EWMH_WM_STATE_ADD) {
429 set_urgent(m, d, n, true);
430 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
431 set_urgent(m, d, n, false);
432 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
433 set_urgent(m, d, n, !n->client->urgent);
438 void process_error(xcb_generic_event_t *evt)
440 xcb_request_error_t *e = (xcb_request_error_t *) evt;
441 warn("Failed request: %s, %s: %d.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);