]> git.lizzy.rs Git - bspwm.git/blob - events.c
Window dragging: transfer node to underlying monitor
[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(mon, mon->desk, 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 = loc.monitor->rectangle;
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             || (pointer_position.x == e->root_x && pointer_position.y == e->root_y))
210         return;
211
212     window_location_t loc;
213     if (locate_window(e->event, &loc)) {
214         select_monitor(loc.monitor);
215         focus_node(loc.monitor, loc.desktop, loc.node, true);
216         pointer_position = (xcb_point_t) {e->root_x, e->root_y};
217     }
218 }
219
220 void client_message(xcb_generic_event_t *evt)
221 {
222     xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
223
224     PRINTF("client message %X\n", e->window);
225
226     window_location_t loc;
227
228     if (!locate_window(e->window, &loc))
229         return;
230
231     if (e->type == ewmh->_NET_WM_STATE) {
232         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
233         handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
234     } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
235         if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node)
236             toggle_fullscreen(loc.monitor, loc.desktop->focus->client);
237         select_monitor(loc.monitor);
238         select_desktop(loc.desktop);
239         focus_node(loc.monitor, loc.desktop, loc.node, true);
240         arrange(loc.monitor, loc.desktop);
241     }
242 }
243
244 void button_press(xcb_generic_event_t *evt)
245 {
246     xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
247     xcb_window_t win = e->child;
248
249     PRINTF("button press %u %X\n", e->detail, win);
250
251     window_location_t loc;
252     if (locate_window(win, &loc)) {
253         client_t *c = loc.node->client;
254         switch (e->detail)  {
255             case XCB_BUTTON_INDEX_2:
256                 focus_node(loc.monitor, loc.desktop, loc.node, true);
257                 break;
258             case XCB_BUTTON_INDEX_1:
259             case XCB_BUTTON_INDEX_3:
260                 if (is_tiled(loc.node->client)) {
261                     loc.node->client->floating_rectangle = loc.node->client->tiled_rectangle;
262                     toggle_floating(loc.node);
263                     arrange(loc.monitor, loc.desktop);
264                 } else if (loc.node->client->fullscreen) {
265                     return;
266                 }
267                 frozen_pointer->monitor = loc.monitor;
268                 frozen_pointer->desktop = loc.desktop;
269                 frozen_pointer->node = loc.node;
270                 frozen_pointer->rectangle = c->floating_rectangle;
271                 frozen_pointer->position = (xcb_point_t) {e->root_x, e->root_y};
272                 frozen_pointer->button = e->detail;
273                 if (e->detail == XCB_BUTTON_INDEX_3) {
274                     int16_t mid_x, mid_y;
275                     mid_x = c->floating_rectangle.x + (c->floating_rectangle.width / 2);
276                     mid_y = c->floating_rectangle.y + (c->floating_rectangle.height / 2);
277                     if (e->root_x > mid_x) {
278                         if (e->root_y > mid_y)
279                             frozen_pointer->corner = BOTTOM_RIGHT;
280                         else
281                             frozen_pointer->corner = TOP_RIGHT;
282                     } else {
283                         if (e->root_y > mid_y)
284                             frozen_pointer->corner = BOTTOM_LEFT;
285                         else
286                             frozen_pointer->corner = TOP_LEFT;
287                     }
288                 }
289                 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);
290                 break;
291         }
292     }
293 }
294
295 void motion_notify(xcb_generic_event_t *evt)
296 {
297     xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
298
299     int16_t delta_x, delta_y, x, y, w, h;
300     uint16_t width, height;
301
302     monitor_t *m = frozen_pointer->monitor;
303     desktop_t *d = frozen_pointer->desktop;
304     node_t *n = frozen_pointer->node;
305     client_t *c = n->client;
306     xcb_rectangle_t rect = frozen_pointer->rectangle;
307     xcb_window_t win = c->window;
308
309     x = y = 0;
310     w = h = 1;
311
312     /* PRINTF("motion notify %X\n", win); */
313
314     delta_x = e->root_x - frozen_pointer->position.x;
315     delta_y = e->root_y - frozen_pointer->position.y;
316
317     switch (frozen_pointer->button) {
318         case XCB_BUTTON_INDEX_1:
319             x = rect.x + delta_x;
320             y = rect.y + delta_y;
321             window_move(win, x, y);
322             break;
323         case XCB_BUTTON_INDEX_3:
324             switch (frozen_pointer->corner) {
325                 case TOP_LEFT:
326                     x = rect.x + delta_x;
327                     y = rect.y + delta_y;
328                     w = rect.width - delta_x;
329                     h = rect.height - delta_y;
330                     break;
331                 case TOP_RIGHT:
332                     x = rect.x;
333                     y = rect.y + delta_y;
334                     w = rect.width + delta_x;
335                     h = rect.height - delta_y;
336                     break;
337                 case BOTTOM_LEFT:
338                     x = rect.x + delta_x;
339                     y = rect.y;
340                     w = rect.width - delta_x;
341                     h = rect.height + delta_y;
342                     break;
343                 case BOTTOM_RIGHT:
344                     x = rect.x;
345                     y = rect.y;
346                     w = rect.width + delta_x;
347                     h = rect.height + delta_y;
348                     break;
349             }
350             width = MAX(1, w);
351             height = MAX(1, h);
352             window_move_resize(win, x, y, width, height);
353             c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
354             window_draw_border(n, d->focus == n, mon == m);
355             break;
356     }
357 }
358
359 void button_release(void)
360 {
361     PUTS("button release");
362
363     xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
364     update_floating_rectangle(frozen_pointer->node->client);
365     monitor_t *m = underlying_monitor(frozen_pointer->node->client);
366     if (m != NULL && m != frozen_pointer->monitor) {
367         transfer_node(frozen_pointer->monitor, frozen_pointer->desktop, m, m->desk, frozen_pointer->node);
368         select_monitor(m);
369     }
370 }
371
372 void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
373 {
374     if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
375         bool fs = n->client->fullscreen;
376         if (action == XCB_EWMH_WM_STATE_TOGGLE
377                 || (fs && action == XCB_EWMH_WM_STATE_REMOVE)
378                 || (!fs && action == XCB_EWMH_WM_STATE_ADD)) {
379             toggle_fullscreen(m, n->client);
380             arrange(m, d);
381         }
382     }
383 }