]> git.lizzy.rs Git - bspwm.git/blob - events.c
Merge branch 'master' into status
[bspwm.git] / events.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <xcb/xcb.h>
5 #include <xcb/xcb_event.h>
6 #include <xcb/xcb_icccm.h>
7 #include "types.h"
8 #include "bspwm.h"
9 #include "settings.h"
10 #include "helpers.h"
11 #include "window.h"
12 #include "events.h"
13 #include "tree.h"
14 #include "rules.h"
15 #include "ewmh.h"
16
17 void handle_event(xcb_generic_event_t *evt)
18 {
19     switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
20         case XCB_MAP_REQUEST:
21             map_request(evt);
22             break;
23         case XCB_DESTROY_NOTIFY:
24             destroy_notify(evt);
25             break;
26         case XCB_UNMAP_NOTIFY:
27             unmap_notify(evt);
28             break;
29         case XCB_CLIENT_MESSAGE:
30             client_message(evt);
31             break;
32         case XCB_CONFIGURE_REQUEST:
33             configure_request(evt);
34             break;
35         case XCB_PROPERTY_NOTIFY:
36             property_notify(evt);
37             break;
38         case XCB_ENTER_NOTIFY:
39             enter_notify(evt);
40             break;
41         case XCB_BUTTON_PRESS:
42             button_press(evt);
43             break;
44         case XCB_MOTION_NOTIFY:
45             motion_notify(evt);
46             break;
47         case XCB_BUTTON_RELEASE:
48             button_release();
49             break;
50         default:
51             break;
52     }
53 }
54
55 void map_request(xcb_generic_event_t *evt)
56 {
57     xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
58
59     PRINTF("map request %X\n", e->window);
60
61     manage_window(mon, mon->desk, e->window);
62 }
63
64 void configure_request(xcb_generic_event_t *evt)
65 {
66     xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
67
68     PRINTF("configure request %X\n", e->window);
69
70     window_location_t loc;
71     bool is_managed = locate_window(e->window, &loc);
72
73     if (!is_managed || is_floating(loc.node->client)) {
74         uint16_t mask = 0;
75         uint32_t values[7];
76         unsigned short i = 0;
77
78         if (e->value_mask & XCB_CONFIG_WINDOW_X) {
79             mask |= XCB_CONFIG_WINDOW_X;
80             values[i++] = e->x;
81             if (is_managed)
82                 loc.node->client->floating_rectangle.x = e->x;
83         }
84
85         if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
86             mask |= XCB_CONFIG_WINDOW_Y;
87             values[i++] = e->y;
88             if (is_managed)
89                 loc.node->client->floating_rectangle.y = e->y;
90         }
91
92         if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
93             mask |= XCB_CONFIG_WINDOW_WIDTH;
94             values[i++] = e->width;
95             if (is_managed)
96                 loc.node->client->floating_rectangle.width = e->width;
97         }
98
99         if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
100             mask |= XCB_CONFIG_WINDOW_HEIGHT;
101             values[i++] = e->height;
102             if (is_managed)
103                 loc.node->client->floating_rectangle.height = e->height;
104         }
105
106         if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
107             mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
108             values[i++] = e->border_width;
109         }
110
111         if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
112             mask |= XCB_CONFIG_WINDOW_SIBLING;
113             values[i++] = e->sibling;
114         }
115
116         if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
117             mask |= XCB_CONFIG_WINDOW_STACK_MODE;
118             values[i++] = e->stack_mode;
119         }
120
121         xcb_configure_window(dpy, e->window, mask, values);
122         if (is_managed)
123             window_draw_border(loc.node, loc.node == loc.desktop->focus, loc.monitor == mon);
124     } else {
125         xcb_configure_notify_event_t evt;
126         xcb_rectangle_t rect;
127         unsigned int bw;
128         xcb_window_t win = loc.node->client->window;
129
130         if (is_tiled(loc.node->client)) {
131             rect = loc.node->client->tiled_rectangle;
132             bw = border_width;
133         } else {
134             rect = loc.monitor->rectangle;
135             bw = 0;
136         }
137
138         evt.response_type = XCB_CONFIGURE_NOTIFY;
139         evt.event = win;
140         evt.window = win;
141         evt.above_sibling = XCB_NONE;
142         evt.x = rect.x;
143         evt.y = rect.y;
144         evt.width = rect.width;
145         evt.height = rect.height;
146         evt.border_width = bw;
147         evt.override_redirect = false;
148
149         xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
150     }
151 }
152
153 void destroy_notify(xcb_generic_event_t *evt)
154 {
155     xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
156
157     PRINTF("destroy notify %X\n", e->window);
158
159     window_location_t loc;
160     if (locate_window(e->window, &loc)) {
161         remove_node(loc.desktop, loc.node);
162         arrange(loc.monitor, loc.desktop);
163     }
164 }
165
166 void unmap_notify(xcb_generic_event_t *evt)
167 {
168     xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
169
170     PRINTF("unmap notify %X\n", e->window);
171
172     window_location_t loc;
173     if (locate_window(e->window, &loc)) {
174         remove_node(loc.desktop, loc.node);
175         arrange(loc.monitor, loc.desktop);
176     }
177 }
178
179 void property_notify(xcb_generic_event_t *evt)
180 {
181     xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
182     xcb_icccm_wm_hints_t hints;
183
184     /* PRINTF("property notify %X\n", e->window); */
185
186     if (e->atom != XCB_ATOM_WM_HINTS)
187         return;
188
189     window_location_t loc;
190     if (locate_window(e->window, &loc)) {
191         if (loc.node == loc.desktop->focus)
192             return;
193         if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1) {
194             loc.node->client->urgent = (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY);
195             put_status();
196             if (loc.monitor->desk == loc.desktop)
197                 arrange(loc.monitor, loc.desktop);
198         }
199     }
200 }
201
202 void enter_notify(xcb_generic_event_t *evt)
203 {
204     xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
205
206     PRINTF("enter notify %X %d %d\n", e->event, e->mode, e->detail);
207
208     if (!focus_follows_mouse 
209             || (e->mode != XCB_NOTIFY_MODE_NORMAL && e->detail == XCB_NOTIFY_DETAIL_INFERIOR)
210             || (pointer_position.x == e->root_x && pointer_position.y == e->root_y))
211         return;
212
213     window_location_t loc;
214     if (locate_window(e->event, &loc)) {
215         select_monitor(loc.monitor);
216         focus_node(loc.monitor, loc.desktop, loc.node, true);
217         pointer_position = (xcb_point_t) {e->root_x, e->root_y};
218     }
219 }
220
221 void client_message(xcb_generic_event_t *evt)
222 {
223     xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
224
225     PRINTF("client message %X %u\n", e->window, e->type);
226
227     if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
228         desktop_location_t loc;
229         if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
230             select_monitor(loc.monitor);
231             select_desktop(loc.desktop);
232         }
233         return;
234     }
235
236     window_location_t loc;
237     if (!locate_window(e->window, &loc))
238         return;
239
240     if (e->type == ewmh->_NET_WM_STATE) {
241         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
242         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
243     } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
244         if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node)
245             toggle_fullscreen(loc.monitor, loc.desktop->focus->client);
246         select_monitor(loc.monitor);
247         select_desktop(loc.desktop);
248         focus_node(loc.monitor, loc.desktop, loc.node, true);
249         arrange(loc.monitor, loc.desktop);
250     }
251 }
252
253 void button_press(xcb_generic_event_t *evt)
254 {
255     xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
256     xcb_window_t win = e->child;
257
258     PRINTF("button press %u %u %X\n", e->detail, e->state, win);
259
260     window_location_t loc;
261     if (locate_window(win, &loc)) {
262         client_t *c = loc.node->client;
263         switch (e->detail)  {
264             case XCB_BUTTON_INDEX_2:
265                 focus_node(loc.monitor, loc.desktop, loc.node, true);
266                 break;
267             case XCB_BUTTON_INDEX_1:
268             case XCB_BUTTON_INDEX_3:
269                 if (is_tiled(loc.node->client)) {
270                     loc.node->client->floating_rectangle = loc.node->client->tiled_rectangle;
271                     toggle_floating(loc.node);
272                     arrange(loc.monitor, loc.desktop);
273                 } else if (loc.node->client->fullscreen) {
274                     return;
275                 }
276                 frozen_pointer->monitor = loc.monitor;
277                 frozen_pointer->desktop = loc.desktop;
278                 frozen_pointer->node = loc.node;
279                 frozen_pointer->rectangle = c->floating_rectangle;
280                 frozen_pointer->position = (xcb_point_t) {e->root_x, e->root_y};
281                 frozen_pointer->button = e->detail;
282                 if (e->detail == XCB_BUTTON_INDEX_3) {
283                     int16_t mid_x, mid_y;
284                     mid_x = c->floating_rectangle.x + (c->floating_rectangle.width / 2);
285                     mid_y = c->floating_rectangle.y + (c->floating_rectangle.height / 2);
286                     if (e->root_x > mid_x) {
287                         if (e->root_y > mid_y)
288                             frozen_pointer->corner = BOTTOM_RIGHT;
289                         else
290                             frozen_pointer->corner = TOP_RIGHT;
291                     } else {
292                         if (e->root_y > mid_y)
293                             frozen_pointer->corner = BOTTOM_LEFT;
294                         else
295                             frozen_pointer->corner = TOP_LEFT;
296                     }
297                 }
298                 xcb_grab_pointer(dpy, false, screen->root, XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME);
299                 break;
300         }
301     }
302 }
303
304 void motion_notify(xcb_generic_event_t *evt)
305 {
306     xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
307
308     int16_t delta_x, delta_y, x, y, w, h;
309     uint16_t width, height;
310
311     monitor_t *m = frozen_pointer->monitor;
312     desktop_t *d = frozen_pointer->desktop;
313     node_t *n = frozen_pointer->node;
314     client_t *c = n->client;
315     xcb_rectangle_t rect = frozen_pointer->rectangle;
316     xcb_window_t win = c->window;
317
318     x = y = 0;
319     w = h = 1;
320
321     /* PRINTF("motion notify %X\n", win); */
322
323     delta_x = e->root_x - frozen_pointer->position.x;
324     delta_y = e->root_y - frozen_pointer->position.y;
325
326     switch (frozen_pointer->button) {
327         case XCB_BUTTON_INDEX_1:
328             x = rect.x + delta_x;
329             y = rect.y + delta_y;
330             window_move(win, x, y);
331             break;
332         case XCB_BUTTON_INDEX_3:
333             switch (frozen_pointer->corner) {
334                 case TOP_LEFT:
335                     x = rect.x + delta_x;
336                     y = rect.y + delta_y;
337                     w = rect.width - delta_x;
338                     h = rect.height - delta_y;
339                     break;
340                 case TOP_RIGHT:
341                     x = rect.x;
342                     y = rect.y + delta_y;
343                     w = rect.width + delta_x;
344                     h = rect.height - delta_y;
345                     break;
346                 case BOTTOM_LEFT:
347                     x = rect.x + delta_x;
348                     y = rect.y;
349                     w = rect.width - delta_x;
350                     h = rect.height + delta_y;
351                     break;
352                 case BOTTOM_RIGHT:
353                     x = rect.x;
354                     y = rect.y;
355                     w = rect.width + delta_x;
356                     h = rect.height + delta_y;
357                     break;
358             }
359             width = MAX(1, w);
360             height = MAX(1, h);
361             window_move_resize(win, x, y, width, height);
362             c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
363             window_draw_border(n, d->focus == n, mon == m);
364             break;
365     }
366 }
367
368 void button_release(void)
369 {
370     PUTS("button release");
371
372     xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
373     update_floating_rectangle(frozen_pointer->node->client);
374     monitor_t *m = underlying_monitor(frozen_pointer->node->client);
375     if (m != NULL && m != frozen_pointer->monitor) {
376         transfer_node(frozen_pointer->monitor, frozen_pointer->desktop, m, m->desk, frozen_pointer->node);
377         select_monitor(m);
378     }
379 }
380
381 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
382 {
383     if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
384         bool fs = n->client->fullscreen;
385         if (action == XCB_EWMH_WM_STATE_TOGGLE
386                 || (fs && action == XCB_EWMH_WM_STATE_REMOVE)
387                 || (!fs && action == XCB_EWMH_WM_STATE_ADD)) {
388             toggle_fullscreen(m, n->client);
389             arrange(m, d);
390         }
391     }
392 }