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