]> git.lizzy.rs Git - bspwm.git/blob - events.c
Added support for multiple monitors
[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_icccm.h>
6 #include <xcb/xcb_event.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_BUTTON_PRESS:
39             button_press(evt);
40             break;
41         case XCB_MOTION_NOTIFY:
42             motion_notify(evt);
43             break;
44         case XCB_BUTTON_RELEASE:
45             button_release();
46             break;
47         default:
48             break;
49     }
50 }
51
52 void map_request(xcb_generic_event_t *evt)
53 {
54     xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
55
56     PRINTF("map request %X\n", e->window);
57
58     xcb_window_t win = e->window;
59     window_location_t loc;
60     xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
61     uint8_t override_redirect = 0;
62
63     if (wa != NULL) {
64         override_redirect = wa->override_redirect;
65         free(wa);
66     }
67
68     if (override_redirect || locate_window(win, &loc))
69         return;
70
71     bool floating = false, transient = false, fullscreen = false, takes_focus = true, manage = true;
72
73     handle_rules(win, &floating, &transient, &fullscreen, &takes_focus, &manage);
74
75     if (!manage) {
76         window_show(win);
77         return;
78     }
79
80     desktop_t *desk = mon->desk;
81     client_t *c = make_client(win);
82     update_floating_rectangle(c);
83
84     xcb_icccm_get_wm_class_reply_t reply;
85     if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
86         strncpy(c->class_name, reply.class_name, sizeof(c->class_name));
87         xcb_icccm_get_wm_class_reply_wipe(&reply);
88     }
89
90     if (c->transient)
91         floating = true;
92
93     node_t *birth = make_node();
94     birth->client = c;
95
96     if (floating)
97         split_mode = MODE_MANUAL;
98
99     insert_node(desk, birth);
100
101     if (floating)
102         toggle_floating(birth);
103
104     if (desk->focus != NULL && desk->focus->client->fullscreen)
105         toggle_fullscreen(desk->focus->client);
106
107     if (fullscreen)
108         toggle_fullscreen(birth->client);
109
110     c->transient = transient;
111
112     if (takes_focus)
113         focus_node(mon, desk, birth, false);
114
115     fit_monitor(mon, birth->client);
116     arrange(mon, desk);
117     window_show(c->window);
118
119     if (takes_focus)
120         xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
121
122     uint32_t values[] = {CLIENT_EVENT_MASK};
123     xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values);
124
125     num_clients++;
126     ewmh_set_wm_desktop(birth, desk);
127     ewmh_update_client_list();
128 }
129
130 void configure_request(xcb_generic_event_t *evt)
131 {
132     xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
133
134     PRINTF("configure request %X\n", e->window);
135
136     window_location_t loc;
137     bool is_managed = locate_window(e->window, &loc);
138
139     if (!is_managed || is_floating(loc.node->client)) {
140         uint16_t mask = 0;
141         uint32_t values[7];
142         unsigned short i = 0;
143
144         if (e->value_mask & XCB_CONFIG_WINDOW_X) {
145             mask |= XCB_CONFIG_WINDOW_X;
146             values[i++] = e->x;
147             if (is_managed)
148                 loc.node->client->floating_rectangle.x = e->x;
149         }
150
151         if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
152             mask |= XCB_CONFIG_WINDOW_Y;
153             values[i++] = e->y;
154             if (is_managed)
155                 loc.node->client->floating_rectangle.y = e->y;
156         }
157
158         if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
159             mask |= XCB_CONFIG_WINDOW_WIDTH;
160             values[i++] = e->width;
161             if (is_managed)
162                 loc.node->client->floating_rectangle.width = e->width;
163         }
164
165         if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
166             mask |= XCB_CONFIG_WINDOW_HEIGHT;
167             values[i++] = e->height;
168             if (is_managed)
169                 loc.node->client->floating_rectangle.height = e->height;
170         }
171
172         if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
173             mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
174             values[i++] = e->border_width;
175         }
176
177         if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
178             mask |= XCB_CONFIG_WINDOW_SIBLING;
179             values[i++] = e->sibling;
180         }
181
182         if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
183             mask |= XCB_CONFIG_WINDOW_STACK_MODE;
184             values[i++] = e->stack_mode;
185         }
186
187         xcb_configure_window(dpy, e->window, mask, values);
188         if (is_managed)
189             window_draw_border(loc.node, loc.node == loc.desktop->focus, loc.monitor == mon);
190     } else {
191         xcb_configure_notify_event_t evt;
192         xcb_rectangle_t rect;
193         unsigned int bw;
194         xcb_window_t win = loc.node->client->window;
195
196         if (is_tiled(loc.node->client)) {
197             rect = loc.node->client->tiled_rectangle;
198             bw = border_width;
199         } else {
200             rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
201             bw = 0;
202         }
203
204         evt.response_type = XCB_CONFIGURE_NOTIFY;
205         evt.event = win;
206         evt.window = win;
207         evt.above_sibling = XCB_NONE;
208         evt.x = rect.x;
209         evt.y = rect.y;
210         evt.width = rect.width;
211         evt.height = rect.height;
212         evt.border_width = bw;
213         evt.override_redirect = false;
214
215         xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
216     }
217 }
218
219 void destroy_notify(xcb_generic_event_t *evt)
220 {
221     xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
222
223     PRINTF("destroy notify %X\n", e->window);
224
225     window_location_t loc;
226     if (locate_window(e->window, &loc)) {
227         remove_node(loc.desktop, loc.node);
228         arrange(loc.monitor, loc.desktop);
229     }
230 }
231
232 void unmap_notify(xcb_generic_event_t *evt)
233 {
234     xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
235
236     PRINTF("unmap notify %X\n", e->window);
237
238     window_location_t loc;
239     if (locate_window(e->window, &loc)) {
240         remove_node(loc.desktop, loc.node);
241         arrange(loc.monitor, loc.desktop);
242     }
243 }
244
245 void property_notify(xcb_generic_event_t *evt)
246 {
247     xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
248     xcb_icccm_wm_hints_t hints;
249
250     /* PRINTF("property notify %X\n", e->window); */
251
252     if (e->atom != XCB_ATOM_WM_HINTS)
253         return;
254
255     window_location_t loc;
256     if (locate_window(e->window, &loc)) {
257         if (loc.node == loc.desktop->focus)
258             return;
259         if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1) {
260             loc.node->client->urgent = (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY);
261             if (loc.monitor->desk == loc.desktop)
262                 arrange(loc.monitor, loc.desktop);
263         }
264     }
265 }
266
267 void client_message(xcb_generic_event_t *evt)
268 {
269     xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
270
271     PRINTF("client message %X\n", e->window);
272
273     window_location_t loc;
274
275     if (!locate_window(e->window, &loc))
276         return;
277
278     if (e->type == ewmh->_NET_WM_STATE) {
279         handle_state(loc.node, e->data.data32[1], e->data.data32[0]);
280         handle_state(loc.node, e->data.data32[2], e->data.data32[0]);
281     } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
282         if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node)
283             toggle_fullscreen(loc.desktop->focus->client);
284         if (loc.monitor->desk != loc.desktop) {
285             arrange(loc.monitor, loc.desktop);
286             select_desktop(loc.desktop);
287         }
288         focus_node(loc.monitor, loc.desktop, loc.node, true);
289     }
290 }
291
292 void button_press(xcb_generic_event_t *evt)
293 {
294     xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
295     xcb_window_t win = e->child;
296
297     PRINTF("button press %u %X\n", e->detail, win);
298
299     window_location_t loc;
300     if (locate_window(win, &loc)) {
301         client_t *c = loc.node->client;
302         switch (e->detail)  {
303             case XCB_BUTTON_INDEX_2:
304                 focus_node(loc.monitor, loc.desktop, loc.node, true);
305                 break;
306             case XCB_BUTTON_INDEX_1:
307             case XCB_BUTTON_INDEX_3:
308                 if (!is_floating(loc.node->client))
309                     return;
310                 frozen_pointer->desktop = loc.desktop;
311                 frozen_pointer->node = loc.node;
312                 frozen_pointer->rectangle = c->floating_rectangle;
313                 frozen_pointer->position = (xcb_point_t) {e->root_x, e->root_y};
314                 frozen_pointer->button = e->detail;
315                 if (e->detail == XCB_BUTTON_INDEX_3) {
316                     int16_t mid_x, mid_y;
317                     mid_x = c->floating_rectangle.x + (c->floating_rectangle.width / 2);
318                     mid_y = c->floating_rectangle.y + (c->floating_rectangle.height / 2);
319                     if (e->root_x > mid_x) {
320                         if (e->root_y > mid_y)
321                             frozen_pointer->corner = BOTTOM_RIGHT;
322                         else
323                             frozen_pointer->corner = TOP_RIGHT;
324                     } else {
325                         if (e->root_y > mid_y)
326                             frozen_pointer->corner = BOTTOM_LEFT;
327                         else
328                             frozen_pointer->corner = TOP_LEFT;
329                     }
330                 }
331                 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);
332                 break;
333         }
334     }
335 }
336
337 void motion_notify(xcb_generic_event_t *evt)
338 {
339     xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
340
341     int16_t delta_x, delta_y, x, y, w, h;
342     uint16_t width, height;
343
344     monitor_t *m = frozen_pointer->monitor;
345     desktop_t *d = frozen_pointer->desktop;
346     node_t *n = frozen_pointer->node;
347     client_t *c = n->client;
348     xcb_rectangle_t rect = frozen_pointer->rectangle;
349     xcb_window_t win = c->window;
350
351     /* PRINTF("motion notify %X\n", win); */
352
353     delta_x = e->root_x - frozen_pointer->position.x;
354     delta_y = e->root_y - frozen_pointer->position.y;
355
356     switch (frozen_pointer->button) {
357         case XCB_BUTTON_INDEX_1:
358             x = rect.x + delta_x;
359             y = rect.y + delta_y;
360             window_move(win, x, y);
361             break;
362         case XCB_BUTTON_INDEX_3:
363             switch (frozen_pointer->corner) {
364                 case TOP_LEFT:
365                     x = rect.x + delta_x;
366                     y = rect.y + delta_y;
367                     w = rect.width - delta_x;
368                     h = rect.height - delta_y;
369                     break;
370                 case TOP_RIGHT:
371                     x = rect.x;
372                     y = rect.y + delta_y;
373                     w = rect.width + delta_x;
374                     h = rect.height - delta_y;
375                     break;
376                 case BOTTOM_LEFT:
377                     x = rect.x + delta_x;
378                     y = rect.y;
379                     w = rect.width - delta_x;
380                     h = rect.height + delta_y;
381                     break;
382                 case BOTTOM_RIGHT:
383                 default:
384                     x = rect.x;
385                     y = rect.y;
386                     w = rect.width + delta_x;
387                     h = rect.height + delta_y;
388                     break;
389             }
390             width = MAX(1, w);
391             height = MAX(1, h);
392             window_move_resize(win, x, y, width, height);
393             c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
394             window_draw_border(n, d->focus == n, mon == m);
395     }
396 }
397
398 void button_release(void)
399 {
400     PUTS("button release");
401
402     xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
403     update_floating_rectangle(frozen_pointer->node->client);
404 }
405
406 void handle_state(node_t *n, xcb_atom_t state, unsigned int action)
407 {
408     if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
409         bool fs = n->client->fullscreen;
410         if (action == XCB_EWMH_WM_STATE_TOGGLE
411                 || (fs && action == XCB_EWMH_WM_STATE_REMOVE)
412                 || (!fs && action == XCB_EWMH_WM_STATE_ADD)) {
413             toggle_fullscreen(n->client);
414             arrange(mon, mon->desk);
415         }
416     }
417 }