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.
31 #include "subscribe.h"
36 void handle_event(xcb_generic_event_t *evt)
38 uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
43 case XCB_DESTROY_NOTIFY:
46 case XCB_UNMAP_NOTIFY:
49 case XCB_CLIENT_MESSAGE:
52 case XCB_CONFIGURE_REQUEST:
53 configure_request(evt);
55 case XCB_PROPERTY_NOTIFY:
58 case XCB_ENTER_NOTIFY:
61 case XCB_MOTION_NOTIFY:
71 if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
77 void map_request(xcb_generic_event_t *evt)
79 xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
81 schedule_window(e->window);
84 void configure_request(xcb_generic_event_t *evt)
86 xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
89 bool is_managed = locate_window(e->window, &loc);
90 client_t *c = (is_managed ? loc.node->client : NULL);
93 if (is_managed && !IS_FLOATING(c)) {
94 if (e->value_mask & XCB_CONFIG_WINDOW_X)
95 c->floating_rectangle.x = e->x;
96 if (e->value_mask & XCB_CONFIG_WINDOW_Y)
97 c->floating_rectangle.y = e->y;
98 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
100 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
104 restrain_floating_width(c, &w);
105 c->floating_rectangle.width = w;
109 restrain_floating_height(c, &h);
110 c->floating_rectangle.height = h;
113 xcb_configure_notify_event_t evt;
114 xcb_window_t win = c->window;
115 unsigned int bw = c->border_width;
117 xcb_rectangle_t rect = get_rectangle(loc.monitor, c);
119 evt.response_type = XCB_CONFIGURE_NOTIFY;
122 evt.above_sibling = XCB_NONE;
125 evt.width = rect.width;
126 evt.height = rect.height;
127 evt.border_width = bw;
128 evt.override_redirect = false;
130 xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
132 if (c->state == STATE_PSEUDO_TILED) {
133 arrange(loc.monitor, loc.desktop);
138 unsigned short i = 0;
140 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
141 mask |= XCB_CONFIG_WINDOW_X;
144 c->floating_rectangle.x = e->x;
147 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
148 mask |= XCB_CONFIG_WINDOW_Y;
151 c->floating_rectangle.y = e->y;
154 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
155 mask |= XCB_CONFIG_WINDOW_WIDTH;
158 restrain_floating_width(c, &w);
159 c->floating_rectangle.width = w;
164 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
165 mask |= XCB_CONFIG_WINDOW_HEIGHT;
168 restrain_floating_height(c, &h);
169 c->floating_rectangle.height = h;
174 if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
175 mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
176 values[i++] = e->border_width;
179 if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
180 mask |= XCB_CONFIG_WINDOW_SIBLING;
181 values[i++] = e->sibling;
184 if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
185 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
186 values[i++] = e->stack_mode;
189 xcb_configure_window(dpy, e->window, mask, values);
191 if (is_managed && (mask & XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT)) {
192 xcb_rectangle_t r = c->floating_rectangle;
193 put_status(SBSC_MASK_WINDOW_GEOMETRY, "window_geometry %s %s 0x%X %ux%u+%i+%i\n", loc.monitor->name, loc.desktop->name, c->window, r.width, r.height, r.x, r.y);
198 translate_client(monitor_from_client(c), loc.monitor, c);
202 void destroy_notify(xcb_generic_event_t *evt)
204 xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
206 unmanage_window(e->window);
209 void unmap_notify(xcb_generic_event_t *evt)
211 xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
213 unmanage_window(e->window);
216 void property_notify(xcb_generic_event_t *evt)
218 xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
220 if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
225 if (!locate_window(e->window, &loc))
228 if (e->atom == XCB_ATOM_WM_HINTS) {
229 xcb_icccm_wm_hints_t hints;
230 if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
231 (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
232 set_urgency(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
233 } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
234 client_t *c = loc.node->client;
235 xcb_size_hints_t size_hints;
236 if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &size_hints, NULL) == 1 &&
237 (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE))) {
238 c->min_width = size_hints.min_width;
239 c->max_width = size_hints.max_width;
240 c->min_height = size_hints.min_height;
241 c->max_height = size_hints.max_height;
242 int w = c->floating_rectangle.width;
243 int h = c->floating_rectangle.height;
244 restrain_floating_size(c, &w, &h);
245 c->floating_rectangle.width = w;
246 c->floating_rectangle.height = h;
247 arrange(loc.monitor, loc.desktop);
252 void client_message(xcb_generic_event_t *evt)
254 xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
256 if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
258 if (ewmh_locate_desktop(e->data.data32[0], &loc))
259 focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
264 if (!locate_window(e->window, &loc))
267 if (e->type == ewmh->_NET_WM_STATE) {
268 handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
269 handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
270 } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
271 if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
272 loc.node == mon->desk->focus)
274 focus_node(loc.monitor, loc.desktop, loc.node);
275 } else if (e->type == ewmh->_NET_WM_DESKTOP) {
277 if (ewmh_locate_desktop(e->data.data32[0], &dloc))
278 transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
279 } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
280 window_close(loc.node);
284 void focus_in(xcb_generic_event_t *evt)
286 xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
288 if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
289 || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
290 || e->detail == XCB_NOTIFY_DETAIL_NONE) {
294 if (mon->desk->focus != NULL && e->event == mon->desk->focus->client->window) {
299 if (locate_window(e->event, &loc)) {
300 // prevent input focus stealing
301 update_input_focus();
305 void enter_notify(xcb_generic_event_t *evt)
307 xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
308 xcb_window_t win = e->event;
310 if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
311 (mon->desk->focus != NULL &&
312 mon->desk->focus->client->window == win)) {
316 xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, motion_recorder), NULL);
322 if (wa->map_state == XCB_MAP_STATE_UNMAPPED) {
323 enable_motion_recorder();
325 disable_motion_recorder();
329 void motion_notify(xcb_generic_event_t *evt)
331 xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
333 int dtime = e->time - last_motion_time;
335 last_motion_time = e->time;
336 last_motion_x = e->event_x;
337 last_motion_y = e->event_y;
341 int mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);
346 xcb_window_t win = XCB_NONE;
347 xcb_point_t pt = {e->root_x, e->root_y};
348 query_pointer(&win, NULL);
350 bool pfm_backup = pointer_follows_monitor;
351 bool pff_backup = pointer_follows_focus;
353 pointer_follows_monitor = false;
354 pointer_follows_focus = false;
356 if (locate_window(win, &loc)) {
357 if (loc.node != mon->desk->focus) {
358 focus_node(loc.monitor, loc.desktop, loc.node);
361 monitor_t *m = monitor_from_point(pt);
362 if (m != NULL && m != mon) {
363 focus_node(m, m->desk, m->desk->focus);
366 pointer_follows_monitor = pfm_backup;
367 pointer_follows_focus = pff_backup;
370 disable_motion_recorder();
373 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
375 if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
376 if (action == XCB_EWMH_WM_STATE_ADD) {
377 set_state(m, d, n, STATE_FULLSCREEN);
378 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
379 set_state(m, d, n, n->client->last_state);
380 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
381 set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
384 } else if (state == ewmh->_NET_WM_STATE_BELOW) {
385 if (action == XCB_EWMH_WM_STATE_ADD) {
386 set_layer(m, d, n, LAYER_BELOW);
387 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
388 set_layer(m, d, n, LAYER_NORMAL);
389 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
390 set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
392 } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
393 if (action == XCB_EWMH_WM_STATE_ADD) {
394 set_layer(m, d, n, LAYER_ABOVE);
395 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
396 set_layer(m, d, n, n->client->last_layer);
397 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
398 set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
400 } else if (state == ewmh->_NET_WM_STATE_STICKY) {
401 if (action == XCB_EWMH_WM_STATE_ADD) {
402 set_sticky(m, d, n, true);
403 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
404 set_sticky(m, d, n, false);
405 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
406 set_sticky(m, d, n, !n->client->sticky);
408 } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
409 if (action == XCB_EWMH_WM_STATE_ADD) {
410 set_urgency(m, d, n, true);
411 } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
412 set_urgency(m, d, n, false);
413 } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
414 set_urgency(m, d, n, !n->client->urgent);
419 void process_error(xcb_generic_event_t *evt)
421 xcb_request_error_t *e = (xcb_request_error_t *) evt;
422 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);