]> git.lizzy.rs Git - bspwm.git/blob - pointer.c
Don't needlessly ungrab/grab the buttons
[bspwm.git] / 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 grab_buttons(void)
52 {
53 #define GRAB(b, m) \
54         xcb_grab_button(dpy, false, root, XCB_EVENT_MASK_BUTTON_PRESS, \
55                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
56         uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
57         for (unsigned int i = 0; i < LENGTH(buttons); i++) {
58                 uint8_t button = buttons[i];
59                 if (click_to_focus && button == XCB_BUTTON_INDEX_1) {
60                         GRAB(button, XCB_MOD_MASK_ANY);
61                         continue;
62                 }
63                 GRAB(button, pointer_modifier);
64                 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
65                         GRAB(button, pointer_modifier | num_lock | caps_lock | scroll_lock);
66                 }
67                 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
68                         GRAB(button, pointer_modifier | num_lock | caps_lock);
69                 }
70                 if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
71                         GRAB(button, pointer_modifier | caps_lock | scroll_lock);
72                 }
73                 if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
74                         GRAB(button, pointer_modifier | num_lock | scroll_lock);
75                 }
76                 if (num_lock != XCB_NO_SYMBOL) {
77                         GRAB(button, pointer_modifier | num_lock);
78                 }
79                 if (caps_lock != XCB_NO_SYMBOL) {
80                         GRAB(button, pointer_modifier | caps_lock);
81                 }
82                 if (scroll_lock != XCB_NO_SYMBOL) {
83                         GRAB(button, pointer_modifier | scroll_lock);
84                 }
85         }
86 #undef GRAB
87 }
88
89 void ungrab_buttons(void)
90 {
91         xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, root, XCB_MOD_MASK_ANY);
92 }
93
94 int16_t modfield_from_keysym(xcb_keysym_t keysym)
95 {
96         uint16_t modfield = 0;
97         xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
98         xcb_get_modifier_mapping_reply_t *reply = NULL;
99         xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
100
101         if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
102             (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
103             reply->keycodes_per_modifier < 1 ||
104             (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
105                 goto end;
106         }
107
108         unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
109         for (unsigned int i = 0; i < num_mod; i++) {
110                 for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
111                         xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
112                         if (mk == XCB_NO_SYMBOL) {
113                                 continue;
114                         }
115                         for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
116                                 if (*k == mk) {
117                                         modfield |= (1 << i);
118                                 }
119                         }
120                 }
121         }
122
123 end:
124         xcb_key_symbols_free(symbols);
125         free(keycodes);
126         free(reply);
127         return modfield;
128 }
129
130 resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
131 {
132         resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
133         xcb_rectangle_t rect = get_rectangle(NULL, n);
134         if (pac == ACTION_RESIZE_SIDE) {
135                 float W = rect.width;
136                 float H = rect.height;
137                 float ratio = W / H;
138                 float x = pos.x - rect.x;
139                 float y = pos.y - rect.y;
140                 float diag_a = ratio * y;
141                 float diag_b = W - diag_a;
142                 if (x < diag_a) {
143                         if (x < diag_b) {
144                                 rh = HANDLE_LEFT;
145                         } else {
146                                 rh = HANDLE_BOTTOM;
147                         }
148                 } else {
149                         if (x < diag_b) {
150                                 rh = HANDLE_TOP;
151                         } else {
152                                 rh = HANDLE_RIGHT;
153                         }
154                 }
155         } else if (pac == ACTION_RESIZE_CORNER) {
156                 int16_t mid_x = rect.x + (rect.width / 2);
157                 int16_t mid_y = rect.y + (rect.height / 2);
158                 if (pos.x > mid_x) {
159                         if (pos.y > mid_y) {
160                                 rh = HANDLE_BOTTOM_RIGHT;
161                         } else {
162                                 rh = HANDLE_TOP_RIGHT;
163                         }
164                 } else {
165                         if (pos.y > mid_y) {
166                                 rh = HANDLE_BOTTOM_LEFT;
167                         } else {
168                                 rh = HANDLE_TOP_LEFT;
169                         }
170                 }
171         }
172         return rh;
173 }
174
175 void grab_pointer(pointer_action_t pac)
176 {
177         xcb_window_t win = XCB_NONE;
178         xcb_point_t pos;
179
180         query_pointer(&win, &pos);
181
182         coordinates_t loc;
183
184         if (!locate_window(win, &loc)) {
185                 if (pac == ACTION_FOCUS) {
186                         monitor_t *m = monitor_from_point(pos);
187                         if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
188                                 focus_node(m, m->desk, m->desk->focus);
189                         }
190                 }
191                 return;
192         }
193
194         if (pac == ACTION_FOCUS) {
195                 if (loc.node != mon->desk->focus) {
196                         focus_node(loc.monitor, loc.desktop, loc.node);
197                 } else if (focus_follows_pointer) {
198                         stack(loc.desktop, loc.node, true);
199                 }
200                 return;
201         }
202
203         if (loc.node->client->state == STATE_FULLSCREEN) {
204                 return;
205         }
206
207         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);
208
209         if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
210                 return;
211         }
212
213         track_pointer(loc, pac, pos);
214 }
215
216 void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
217 {
218         node_t *n = loc.node;
219         resize_handle_t rh = get_handle(loc.node, pos, pac);
220
221         uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
222         xcb_timestamp_t last_motion_time = 0;
223
224         xcb_generic_event_t *evt = NULL;
225
226         grabbing = true;
227         grabbed_node = n;
228
229         do {
230                 free(evt);
231                 while ((evt = xcb_wait_for_event(dpy)) == NULL) {
232                         xcb_flush(dpy);
233                 }
234                 uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
235                 if (resp_type == XCB_MOTION_NOTIFY) {
236                         xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
237                         int64_t dtime = e->time - last_motion_time;
238                         if (dtime < 20) {
239                                 continue;
240                         }
241                         last_motion_time = e->time;
242                         int16_t dx = e->root_x - last_motion_x;
243                         int16_t dy = e->root_y - last_motion_y;
244                         if (pac == ACTION_MOVE) {
245                                 move_client(&loc, dx, dy);
246                         } else {
247                                 resize_client(&loc, rh, dx, dy);
248                         }
249                         last_motion_x = e->root_x;
250                         last_motion_y = e->root_y;
251                         xcb_flush(dpy);
252                 } else if (resp_type == XCB_BUTTON_RELEASE) {
253                         grabbing = false;
254                 } else {
255                         handle_event(evt);
256                 }
257         } while (grabbing && grabbed_node != NULL);
258
259         xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
260
261         if (grabbed_node == NULL) {
262                 grabbing = false;
263                 return;
264         }
265
266         xcb_rectangle_t r = get_rectangle(NULL, n);
267
268         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);
269 }