]> git.lizzy.rs Git - bspwm.git/blob - events.c
Merge pull request #34 from Stebalien/fixup-control-flags
[bspwm.git] / events.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <xcb/xcb.h>
5 #include <xcb/randr.h>
6 #include <xcb/xcb_event.h>
7 #include <xcb/xcb_icccm.h>
8 #include "types.h"
9 #include "bspwm.h"
10 #include "settings.h"
11 #include "helpers.h"
12 #include "window.h"
13 #include "events.h"
14 #include "tree.h"
15 #include "query.h"
16 #include "rules.h"
17 #include "ewmh.h"
18
19 void handle_event(xcb_generic_event_t *evt)
20 {
21     uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
22     switch (resp_type) {
23         case XCB_MAP_REQUEST:
24             map_request(evt);
25             break;
26         case XCB_DESTROY_NOTIFY:
27             destroy_notify(evt);
28             break;
29         case XCB_UNMAP_NOTIFY:
30             unmap_notify(evt);
31             break;
32         case XCB_CLIENT_MESSAGE:
33             client_message(evt);
34             break;
35         case XCB_CONFIGURE_REQUEST:
36             configure_request(evt);
37             break;
38         case XCB_PROPERTY_NOTIFY:
39             property_notify(evt);
40             break;
41         case XCB_ENTER_NOTIFY:
42             enter_notify(evt);
43             break;
44         case XCB_MOTION_NOTIFY:
45             motion_notify(evt);
46             break;
47         default:
48             if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
49                 import_monitors();
50             break;
51     }
52 }
53
54 void map_request(xcb_generic_event_t *evt)
55 {
56     xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
57
58     PRINTF("map request %X\n", e->window);
59
60     manage_window(mon, mon->desk, e->window);
61 }
62
63 void configure_request(xcb_generic_event_t *evt)
64 {
65     xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
66
67     PRINTF("configure request %X\n", e->window);
68
69     coordinates_t loc;
70     bool is_managed = locate_window(e->window, &loc);
71
72     if (!is_managed || is_floating(loc.node->client)) {
73         uint16_t mask = 0;
74         uint32_t values[7];
75         unsigned short i = 0;
76
77         if (e->value_mask & XCB_CONFIG_WINDOW_X) {
78             mask |= XCB_CONFIG_WINDOW_X;
79             values[i++] = e->x;
80             if (is_managed)
81                 loc.node->client->floating_rectangle.x = e->x;
82         }
83
84         if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
85             mask |= XCB_CONFIG_WINDOW_Y;
86             values[i++] = e->y;
87             if (is_managed)
88                 loc.node->client->floating_rectangle.y = e->y;
89         }
90
91         if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
92             mask |= XCB_CONFIG_WINDOW_WIDTH;
93             values[i++] = e->width;
94             if (is_managed)
95                 loc.node->client->floating_rectangle.width = e->width;
96         }
97
98         if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
99             mask |= XCB_CONFIG_WINDOW_HEIGHT;
100             values[i++] = e->height;
101             if (is_managed)
102                 loc.node->client->floating_rectangle.height = e->height;
103         }
104
105         if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
106             mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
107             values[i++] = e->border_width;
108         }
109
110         if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
111             mask |= XCB_CONFIG_WINDOW_SIBLING;
112             values[i++] = e->sibling;
113         }
114
115         if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
116             mask |= XCB_CONFIG_WINDOW_STACK_MODE;
117             values[i++] = e->stack_mode;
118         }
119
120         xcb_configure_window(dpy, e->window, mask, values);
121         if (is_managed)
122             window_draw_border(loc.node, loc.node == loc.desktop->focus, loc.monitor == mon);
123     } else {
124         xcb_configure_notify_event_t evt;
125         xcb_rectangle_t rect;
126         unsigned int bw;
127         xcb_window_t win = loc.node->client->window;
128
129         if (is_tiled(loc.node->client)) {
130             rect = loc.node->client->tiled_rectangle;
131             bw = border_width;
132         } else {
133             rect = loc.monitor->rectangle;
134             bw = 0;
135         }
136
137         evt.response_type = XCB_CONFIGURE_NOTIFY;
138         evt.event = win;
139         evt.window = win;
140         evt.above_sibling = XCB_NONE;
141         evt.x = rect.x;
142         evt.y = rect.y;
143         evt.width = rect.width;
144         evt.height = rect.height;
145         evt.border_width = bw;
146         evt.override_redirect = false;
147
148         xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
149     }
150 }
151
152 void destroy_notify(xcb_generic_event_t *evt)
153 {
154     xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
155
156     PRINTF("destroy notify %X\n", e->window);
157
158     coordinates_t loc;
159     if (locate_window(e->window, &loc)) {
160         remove_node(loc.desktop, loc.node);
161         arrange(loc.monitor, loc.desktop);
162     }
163 }
164
165 void unmap_notify(xcb_generic_event_t *evt)
166 {
167     xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
168
169     PRINTF("unmap notify %X\n", e->window);
170
171     coordinates_t loc;
172     if (locate_window(e->window, &loc)) {
173         remove_node(loc.desktop, loc.node);
174         arrange(loc.monitor, loc.desktop);
175     }
176 }
177
178 void property_notify(xcb_generic_event_t *evt)
179 {
180     xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
181     xcb_icccm_wm_hints_t hints;
182
183     /* PRINTF("property notify %X\n", e->window); */
184
185     if (e->atom != XCB_ATOM_WM_HINTS)
186         return;
187
188     coordinates_t loc;
189     if (locate_window(e->window, &loc)
190             && xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1)
191         set_urgency(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
192 }
193
194 void client_message(xcb_generic_event_t *evt)
195 {
196     xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
197
198     PRINTF("client message %X %u\n", e->window, e->type);
199
200     if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
201         coordinates_t loc;
202         if (ewmh_locate_desktop(e->data.data32[0], &loc))
203             focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
204         return;
205     }
206
207     coordinates_t loc;
208     if (!locate_window(e->window, &loc))
209         return;
210
211     if (e->type == ewmh->_NET_WM_STATE) {
212         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
213         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
214     } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
215         if (loc.node == mon->desk->focus)
216             return;
217         if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node) {
218             set_fullscreen(loc.desktop, loc.desktop->focus, false);
219             arrange(loc.monitor, loc.desktop);
220         }
221         focus_node(loc.monitor, loc.desktop, loc.node);
222     } else if (e->type == ewmh->_NET_WM_DESKTOP) {
223         coordinates_t dloc;
224         if (ewmh_locate_desktop(e->data.data32[0], &dloc))
225             transfer_node(loc.monitor, loc.desktop, dloc.monitor, dloc.desktop, loc.node);
226     }
227 }
228
229 void enter_notify(xcb_generic_event_t *evt)
230 {
231     xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
232     xcb_window_t win = e->event;
233
234     PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail);
235
236     if (e->mode != XCB_NOTIFY_MODE_NORMAL
237             || (mon->desk->focus != NULL && mon->desk->focus->client->window == win))
238         return;
239
240     enable_motion_recorder();
241 }
242
243 void motion_notify(xcb_generic_event_t *evt)
244 {
245     PUTS("motion notify");
246
247     xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
248
249     int dtime = e->time - last_motion_time;
250     if (dtime > 1000) {
251         last_motion_time = e->time;
252         last_motion_x = e->event_x;
253         last_motion_y = e->event_y;
254         return;
255     }
256
257     int mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);
258     if (mdist < 10)
259         return;
260
261     disable_motion_recorder();
262
263     xcb_window_t win = XCB_NONE;
264     query_pointer(&win, NULL);
265     if (win != XCB_NONE) {
266         bool backup = pointer_follows_monitor;
267         pointer_follows_monitor = false;
268         window_focus(win);
269         pointer_follows_monitor = backup;
270     }
271 }
272
273 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
274 {
275     if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
276         if (action == XCB_EWMH_WM_STATE_ADD)
277             set_fullscreen(d, n, true);
278         else if (action == XCB_EWMH_WM_STATE_REMOVE)
279             set_fullscreen(d, n, false);
280         else if (action == XCB_EWMH_WM_STATE_TOGGLE)
281             set_fullscreen(d, n, !n->client->fullscreen);
282         arrange(m, d);
283     } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
284         if (action == XCB_EWMH_WM_STATE_ADD)
285             set_urgency(m, d, n, true);
286         else if (action == XCB_EWMH_WM_STATE_REMOVE)
287             set_urgency(m, d, n, false);
288         else if (action == XCB_EWMH_WM_STATE_TOGGLE)
289             set_urgency(m, d, n, !n->client->urgent);
290     }
291 }
292
293 void grab_pointer(pointer_action_t pac)
294 {
295     PRINTF("grab pointer %u\n", pac);
296
297     xcb_window_t win = XCB_NONE;
298     xcb_point_t pos;
299
300     query_pointer(&win, &pos);
301
302     if (win == XCB_NONE)
303         return;
304
305     coordinates_t loc;
306     if (locate_window(win, &loc)) {
307         client_t *c = NULL;
308         frozen_pointer->position = pos;
309         frozen_pointer->action = pac;
310         c = loc.node->client;
311         frozen_pointer->monitor = loc.monitor;
312         frozen_pointer->desktop = loc.desktop;
313         frozen_pointer->node = loc.node;
314         frozen_pointer->client = c;
315         frozen_pointer->window = c->window;
316         frozen_pointer->horizontal_fence = NULL;
317         frozen_pointer->vertical_fence = NULL;
318
319         switch (pac)  {
320             case ACTION_FOCUS:
321                 if (loc.node != mon->desk->focus) {
322                     bool backup = pointer_follows_monitor;
323                     pointer_follows_monitor = false;
324                     focus_node(loc.monitor, loc.desktop, loc.node);
325                     pointer_follows_monitor = backup;
326                 }
327                 break;
328             case ACTION_MOVE:
329             case ACTION_RESIZE_SIDE:
330             case ACTION_RESIZE_CORNER:
331                 if (is_tiled(c)) {
332                     frozen_pointer->rectangle = c->tiled_rectangle;
333                     frozen_pointer->is_tiled = true;
334                 } else if (is_floating(c)) {
335                     frozen_pointer->rectangle = c->floating_rectangle;
336                     frozen_pointer->is_tiled = false;
337                 } else {
338                     frozen_pointer->action = ACTION_NONE;
339                     return;
340                 }
341                 if (pac == ACTION_RESIZE_SIDE) {
342                     float W = frozen_pointer->rectangle.width;
343                     float H = frozen_pointer->rectangle.height;
344                     float ratio = W / H;
345                     float x = pos.x - frozen_pointer->rectangle.x;
346                     float y = pos.y - frozen_pointer->rectangle.y;
347                     float diag_a = ratio * y;
348                     float diag_b = W - diag_a;
349                     if (x < diag_a) {
350                         if (x < diag_b)
351                             frozen_pointer->side = SIDE_LEFT;
352                         else
353                             frozen_pointer->side = SIDE_BOTTOM;
354                     } else {
355                         if (x < diag_b)
356                             frozen_pointer->side = SIDE_TOP;
357                         else
358                             frozen_pointer->side = SIDE_RIGHT;
359                     }
360                 } else if (pac == ACTION_RESIZE_CORNER) {
361                     int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
362                     int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
363                     if (pos.x > mid_x) {
364                         if (pos.y > mid_y)
365                             frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
366                         else
367                             frozen_pointer->corner = CORNER_TOP_RIGHT;
368                     } else {
369                         if (pos.y > mid_y)
370                             frozen_pointer->corner = CORNER_BOTTOM_LEFT;
371                         else
372                             frozen_pointer->corner = CORNER_TOP_LEFT;
373                     }
374                 }
375                 if (frozen_pointer->is_tiled) {
376                     if (pac == ACTION_RESIZE_SIDE) {
377                         switch (frozen_pointer->side) {
378                             case SIDE_TOP:
379                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
380                                 break;
381                             case SIDE_RIGHT:
382                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
383                                 break;
384                             case SIDE_BOTTOM:
385                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
386                                 break;
387                             case SIDE_LEFT:
388                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
389                                 break;
390                         }
391                     } else if (pac == ACTION_RESIZE_CORNER) {
392                         switch (frozen_pointer->corner) {
393                             case CORNER_TOP_LEFT:
394                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
395                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
396                                 break;
397                             case CORNER_TOP_RIGHT:
398                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
399                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
400                                 break;
401                             case CORNER_BOTTOM_RIGHT:
402                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
403                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
404                                 break;
405                             case CORNER_BOTTOM_LEFT:
406                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
407                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
408                                 break;
409                         }
410                     }
411                     if (frozen_pointer->horizontal_fence != NULL)
412                         frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
413                     if (frozen_pointer->vertical_fence != NULL)
414                         frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
415                 }
416                 break;
417             case ACTION_NONE:
418                 break;
419         }
420     } else {
421         frozen_pointer->action = ACTION_NONE;
422     }
423 }
424
425 void track_pointer(int root_x, int root_y)
426 {
427     if (frozen_pointer->action == ACTION_NONE)
428         return;
429
430     int16_t delta_x, delta_y, x, y, w, h;
431     uint16_t width, height;
432
433     pointer_action_t pac = frozen_pointer->action;
434     monitor_t *m = frozen_pointer->monitor;
435     desktop_t *d = frozen_pointer->desktop;
436     node_t *n = frozen_pointer->node;
437     client_t *c = frozen_pointer->client;
438     xcb_window_t win = frozen_pointer->window;
439     xcb_rectangle_t rect = frozen_pointer->rectangle;
440     node_t *vertical_fence = frozen_pointer->vertical_fence;
441     node_t *horizontal_fence = frozen_pointer->horizontal_fence;
442
443     x = y = 0;
444     w = h = 1;
445
446     delta_x = root_x - frozen_pointer->position.x;
447     delta_y = root_y - frozen_pointer->position.y;
448
449     switch (pac) {
450         case ACTION_MOVE:
451             if (frozen_pointer->is_tiled) {
452                 xcb_window_t pwin = XCB_NONE;
453                 query_pointer(&pwin, NULL);
454                 if (pwin == win)
455                     return;
456                 coordinates_t loc;
457                 bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
458                 if (is_managed && is_tiled(loc.node->client) && loc.monitor == m) {
459                     swap_nodes(n, loc.node);
460                     arrange(m, d);
461                 } else {
462                     if (is_managed && loc.monitor == m) {
463                         return;
464                     } else if (!is_managed) {
465                         xcb_point_t pt = (xcb_point_t) {root_x, root_y};
466                         monitor_t *pmon = monitor_from_point(pt);
467                         if (pmon == NULL || pmon == m) {
468                             return;
469                         } else {
470                             loc.monitor = pmon;
471                             loc.desktop = pmon->desk;
472                         }
473                     }
474                     transfer_node(m, d, loc.monitor, loc.desktop, n);
475                     frozen_pointer->monitor = loc.monitor;
476                     frozen_pointer->desktop = loc.desktop;
477                 }
478             } else {
479                 x = rect.x + delta_x;
480                 y = rect.y + delta_y;
481                 window_move(win, x, y);
482                 c->floating_rectangle.x = x;
483                 c->floating_rectangle.y = y;
484                 xcb_point_t pt = (xcb_point_t) {root_x, root_y};
485                 monitor_t *pmon = monitor_from_point(pt);
486                 if (pmon == NULL || pmon == m)
487                     return;
488                 transfer_node(m, d, pmon, pmon->desk, n);
489                 frozen_pointer->monitor = pmon;
490                 frozen_pointer->desktop = pmon->desk;
491             }
492             break;
493         case ACTION_RESIZE_SIDE:
494         case ACTION_RESIZE_CORNER:
495             if (frozen_pointer->is_tiled) {
496                 if (vertical_fence != NULL) {
497                     double sr = frozen_pointer->vertical_ratio + (double) delta_x / vertical_fence->rectangle.width;
498                     sr = MAX(0, sr);
499                     sr = MIN(1, sr);
500                     vertical_fence->split_ratio = sr;
501                 }
502                 if (horizontal_fence != NULL) {
503                     double sr = frozen_pointer->horizontal_ratio + (double) delta_y / horizontal_fence->rectangle.height;
504                     sr = MAX(0, sr);
505                     sr = MIN(1, sr);
506                     horizontal_fence->split_ratio = sr;
507                 }
508                 arrange(mon, mon->desk);
509             } else {
510                 if (pac == ACTION_RESIZE_SIDE) {
511                     switch (frozen_pointer->side) {
512                         case SIDE_TOP:
513                             x = rect.x;
514                             y = rect.y + delta_y;
515                             w = rect.width;
516                             h = rect.height - delta_y;
517                             break;
518                         case SIDE_RIGHT:
519                             x = rect.x;
520                             y = rect.y;
521                             w = rect.width + delta_x;
522                             h = rect.height;
523                             break;
524                         case SIDE_BOTTOM:
525                             x = rect.x;
526                             y = rect.y;
527                             w = rect.width;
528                             h = rect.height + delta_y;
529                             break;
530                         case SIDE_LEFT:
531                             x = rect.x + delta_x;
532                             y = rect.y;
533                             w = rect.width - delta_x;
534                             h = rect.height;
535                             break;
536                     }
537                     width = MAX(1, w);
538                     height = MAX(1, h);
539                     window_move_resize(win, x, y, width, height);
540                     c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
541                     window_draw_border(n, d->focus == n, mon == m);
542                 } else if (pac == ACTION_RESIZE_CORNER) {
543                     switch (frozen_pointer->corner) {
544                         case CORNER_TOP_LEFT:
545                             x = rect.x + delta_x;
546                             y = rect.y + delta_y;
547                             w = rect.width - delta_x;
548                             h = rect.height - delta_y;
549                             break;
550                         case CORNER_TOP_RIGHT:
551                             x = rect.x;
552                             y = rect.y + delta_y;
553                             w = rect.width + delta_x;
554                             h = rect.height - delta_y;
555                             break;
556                         case CORNER_BOTTOM_LEFT:
557                             x = rect.x + delta_x;
558                             y = rect.y;
559                             w = rect.width - delta_x;
560                             h = rect.height + delta_y;
561                             break;
562                         case CORNER_BOTTOM_RIGHT:
563                             x = rect.x;
564                             y = rect.y;
565                             w = rect.width + delta_x;
566                             h = rect.height + delta_y;
567                             break;
568                     }
569                     width = MAX(1, w);
570                     height = MAX(1, h);
571                     window_move_resize(win, x, y, width, height);
572                     c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
573                     window_draw_border(n, d->focus == n, mon == m);
574                 }
575             }
576             break;
577         case ACTION_FOCUS:
578         case ACTION_NONE:
579             break;
580     }
581 }