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