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