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