]> git.lizzy.rs Git - bspwm.git/blob - src/pointer.c
*click_to_focus* is now a button name
[bspwm.git] / src / pointer.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  *    list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  *    this list of conditions and the following disclaimer in the documentation
11  *    and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <xcb/xcb_keysyms.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include "bspwm.h"
29 #include "query.h"
30 #include "settings.h"
31 #include "stack.h"
32 #include "tree.h"
33 #include "monitor.h"
34 #include "subscribe.h"
35 #include "events.h"
36 #include "window.h"
37 #include "pointer.h"
38
39 void pointer_init(void)
40 {
41         num_lock = modfield_from_keysym(XK_Num_Lock);
42         caps_lock = modfield_from_keysym(XK_Caps_Lock);
43         scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
44         if (caps_lock == XCB_NO_SYMBOL) {
45                 caps_lock = XCB_MOD_MASK_LOCK;
46         }
47         grabbing = false;
48         grabbed_node = NULL;
49 }
50
51 void window_grab_buttons(xcb_window_t win)
52 {
53         for (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {
54                 if (click_to_focus == XCB_BUTTON_INDEX_ANY || click_to_focus == BUTTONS[i]) {
55                         window_grab_button(win, BUTTONS[i], XCB_NONE);
56                 }
57                 if (pointer_actions[i] != ACTION_NONE) {
58                         window_grab_button(win, BUTTONS[i], pointer_modifier);
59                 }
60         }
61 }
62
63 void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
64 {
65 #define GRAB(b, m) \
66         xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
67                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
68                 GRAB(button, modifier);
69                 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
70                         GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
71                 }
72                 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
73                         GRAB(button, modifier | num_lock | caps_lock);
74                 }
75                 if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
76                         GRAB(button, modifier | caps_lock | scroll_lock);
77                 }
78                 if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
79                         GRAB(button, modifier | num_lock | scroll_lock);
80                 }
81                 if (num_lock != XCB_NO_SYMBOL) {
82                         GRAB(button, modifier | num_lock);
83                 }
84                 if (caps_lock != XCB_NO_SYMBOL) {
85                         GRAB(button, modifier | caps_lock);
86                 }
87                 if (scroll_lock != XCB_NO_SYMBOL) {
88                         GRAB(button, modifier | scroll_lock);
89                 }
90 #undef GRAB
91 }
92
93 void grab_buttons(void)
94 {
95         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
96                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
97                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
98                                 window_grab_buttons(n->id);
99                                 if (n->presel != NULL) {
100                                         window_grab_buttons(n->presel->feedback);
101                                 }
102                         }
103                 }
104         }
105 }
106
107 void ungrab_buttons(void)
108 {
109         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
110                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
111                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
112                                 xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
113                         }
114                 }
115         }
116 }
117
118 int16_t modfield_from_keysym(xcb_keysym_t keysym)
119 {
120         uint16_t modfield = 0;
121         xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
122         xcb_get_modifier_mapping_reply_t *reply = NULL;
123         xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
124
125         if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
126             (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
127             reply->keycodes_per_modifier < 1 ||
128             (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
129                 goto end;
130         }
131
132         unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
133         for (unsigned int i = 0; i < num_mod; i++) {
134                 for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
135                         xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
136                         if (mk == XCB_NO_SYMBOL) {
137                                 continue;
138                         }
139                         for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
140                                 if (*k == mk) {
141                                         modfield |= (1 << i);
142                                 }
143                         }
144                 }
145         }
146
147 end:
148         xcb_key_symbols_free(symbols);
149         free(keycodes);
150         free(reply);
151         return modfield;
152 }
153
154 resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
155 {
156         resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
157         xcb_rectangle_t rect = get_rectangle(NULL, NULL, n);
158         if (pac == ACTION_RESIZE_SIDE) {
159                 float W = rect.width;
160                 float H = rect.height;
161                 float ratio = W / H;
162                 float x = pos.x - rect.x;
163                 float y = pos.y - rect.y;
164                 float diag_a = ratio * y;
165                 float diag_b = W - diag_a;
166                 if (x < diag_a) {
167                         if (x < diag_b) {
168                                 rh = HANDLE_LEFT;
169                         } else {
170                                 rh = HANDLE_BOTTOM;
171                         }
172                 } else {
173                         if (x < diag_b) {
174                                 rh = HANDLE_TOP;
175                         } else {
176                                 rh = HANDLE_RIGHT;
177                         }
178                 }
179         } else if (pac == ACTION_RESIZE_CORNER) {
180                 int16_t mid_x = rect.x + (rect.width / 2);
181                 int16_t mid_y = rect.y + (rect.height / 2);
182                 if (pos.x > mid_x) {
183                         if (pos.y > mid_y) {
184                                 rh = HANDLE_BOTTOM_RIGHT;
185                         } else {
186                                 rh = HANDLE_TOP_RIGHT;
187                         }
188                 } else {
189                         if (pos.y > mid_y) {
190                                 rh = HANDLE_BOTTOM_LEFT;
191                         } else {
192                                 rh = HANDLE_TOP_LEFT;
193                         }
194                 }
195         }
196         return rh;
197 }
198
199 bool grab_pointer(pointer_action_t pac)
200 {
201         xcb_window_t win = XCB_NONE;
202         xcb_point_t pos;
203
204         query_pointer(&win, &pos);
205
206         coordinates_t loc;
207
208         if (!locate_window(win, &loc)) {
209                 if (pac == ACTION_FOCUS) {
210                         monitor_t *m = monitor_from_point(pos);
211                         if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
212                                 focus_node(m, m->desk, m->desk->focus);
213                                 return true;
214                         }
215                 }
216                 return false;
217         }
218
219         if (pac == ACTION_FOCUS) {
220                 if (loc.node != mon->desk->focus) {
221                         focus_node(loc.monitor, loc.desktop, loc.node);
222                         return true;
223                 } else if (focus_follows_pointer) {
224                         stack(loc.desktop, loc.node, true);
225                 }
226                 return false;
227         }
228
229         if (loc.node->client->state == STATE_FULLSCREEN) {
230                 return true;
231         }
232
233         xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);
234
235         if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
236                 free(reply);
237                 return true;
238         }
239         free(reply);
240
241         if (pac == ACTION_MOVE) {
242                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
243         } else if (pac == ACTION_RESIZE_CORNER) {
244                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
245         } else if (pac == ACTION_RESIZE_SIDE) {
246                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
247         }
248
249         track_pointer(loc, pac, pos);
250
251         return true;
252 }
253
254 void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
255 {
256         node_t *n = loc.node;
257         resize_handle_t rh = get_handle(loc.node, pos, pac);
258
259         uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
260         xcb_timestamp_t last_motion_time = 0;
261
262         xcb_generic_event_t *evt = NULL;
263
264         grabbing = true;
265         grabbed_node = n;
266
267         do {
268                 free(evt);
269                 while ((evt = xcb_wait_for_event(dpy)) == NULL) {
270                         xcb_flush(dpy);
271                 }
272                 uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
273                 if (resp_type == XCB_MOTION_NOTIFY) {
274                         xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
275                         uint32_t dtime = e->time - last_motion_time;
276                         if (dtime < pointer_motion_interval) {
277                                 continue;
278                         }
279                         last_motion_time = e->time;
280                         int16_t dx = e->root_x - last_motion_x;
281                         int16_t dy = e->root_y - last_motion_y;
282                         if (pac == ACTION_MOVE) {
283                                 move_client(&loc, dx, dy);
284                         } else {
285                                 resize_client(&loc, rh, dx, dy);
286                         }
287                         last_motion_x = e->root_x;
288                         last_motion_y = e->root_y;
289                         xcb_flush(dpy);
290                 } else if (resp_type == XCB_BUTTON_RELEASE) {
291                         grabbing = false;
292                 } else {
293                         handle_event(evt);
294                 }
295         } while (grabbing && grabbed_node != NULL);
296         free(evt);
297
298         xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
299
300         if (grabbed_node == NULL) {
301                 grabbing = false;
302                 return;
303         }
304
305         if (pac == ACTION_MOVE) {
306                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id);
307         } else if (pac == ACTION_RESIZE_CORNER) {
308                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\n", loc.monitor->id, loc.desktop->id, n->id);
309         } else if (pac == ACTION_RESIZE_SIDE) {
310                 put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side end\n", loc.monitor->id, loc.desktop->id, n->id);
311         }
312
313         xcb_rectangle_t r = get_rectangle(NULL, NULL, n);
314
315         put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);
316
317         if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
318             ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
319              n->client->state == STATE_TILED)) {
320                 for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
321                         if (f == n || f->client == NULL || !IS_TILED(f->client)) {
322                                 continue;
323                         }
324                         xcb_rectangle_t r = f->client->tiled_rectangle;
325                         put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);
326                 }
327         }
328 }