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