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