1 /* Copyright (c) 2012, Bastien Dejean
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
25 #include <xcb/xcb_keysyms.h>
34 #include "subscribe.h"
46 void pointer_init(void)
48 num_lock = modfield_from_keysym(XK_Num_Lock);
49 caps_lock = modfield_from_keysym(XK_Caps_Lock);
50 scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
51 if (caps_lock == XCB_NO_SYMBOL) {
52 caps_lock = XCB_MOD_MASK_LOCK;
58 void window_grab_buttons(xcb_window_t win)
60 for (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {
61 if (click_to_focus == (int8_t) XCB_BUTTON_INDEX_ANY || click_to_focus == (int8_t) BUTTONS[i]) {
62 window_grab_button(win, BUTTONS[i], XCB_NONE);
64 if (pointer_actions[i] != ACTION_NONE) {
65 window_grab_button(win, BUTTONS[i], pointer_modifier);
70 void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
73 xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
74 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
75 GRAB(button, modifier);
76 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
77 GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
79 if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
80 GRAB(button, modifier | num_lock | caps_lock);
82 if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
83 GRAB(button, modifier | caps_lock | scroll_lock);
85 if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
86 GRAB(button, modifier | num_lock | scroll_lock);
88 if (num_lock != XCB_NO_SYMBOL) {
89 GRAB(button, modifier | num_lock);
91 if (caps_lock != XCB_NO_SYMBOL) {
92 GRAB(button, modifier | caps_lock);
94 if (scroll_lock != XCB_NO_SYMBOL) {
95 GRAB(button, modifier | scroll_lock);
100 void grab_buttons(void)
102 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
103 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
104 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
105 window_grab_buttons(n->id);
106 if (n->presel != NULL) {
107 window_grab_buttons(n->presel->feedback);
114 void ungrab_buttons(void)
116 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
117 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
118 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
119 xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
125 int16_t modfield_from_keysym(xcb_keysym_t keysym)
127 uint16_t modfield = 0;
128 xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
129 xcb_get_modifier_mapping_reply_t *reply = NULL;
130 xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
132 if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
133 (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
134 reply->keycodes_per_modifier < 1 ||
135 (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
139 unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
140 for (unsigned int i = 0; i < num_mod; i++) {
141 for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
142 xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
143 if (mk == XCB_NO_SYMBOL) {
146 for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
148 modfield |= (1 << i);
155 xcb_key_symbols_free(symbols);
161 resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
163 resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
164 xcb_rectangle_t rect = get_rectangle(NULL, NULL, n);
165 if (pac == ACTION_RESIZE_SIDE) {
166 float W = rect.width;
167 float H = rect.height;
169 float x = pos.x - rect.x;
170 float y = pos.y - rect.y;
171 float diag_a = ratio * y;
172 float diag_b = W - diag_a;
186 } else if (pac == ACTION_RESIZE_CORNER) {
187 int16_t mid_x = rect.x + (rect.width / 2);
188 int16_t mid_y = rect.y + (rect.height / 2);
191 rh = HANDLE_BOTTOM_RIGHT;
193 rh = HANDLE_TOP_RIGHT;
197 rh = HANDLE_BOTTOM_LEFT;
199 rh = HANDLE_TOP_LEFT;
206 bool grab_pointer(pointer_action_t pac)
208 xcb_window_t win = XCB_NONE;
211 query_pointer(&win, &pos);
215 if (!locate_window(win, &loc)) {
216 if (pac == ACTION_FOCUS) {
217 monitor_t *m = monitor_from_point(pos);
218 if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
219 focus_node(m, m->desk, m->desk->focus);
226 if (pac == ACTION_FOCUS) {
227 if (loc.node != mon->desk->focus) {
228 focus_node(loc.monitor, loc.desktop, loc.node);
230 } else if (focus_follows_pointer) {
231 stack(loc.desktop, loc.node, true);
236 if (loc.node->client->state == STATE_FULLSCREEN) {
240 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);
242 if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
248 if (pac == ACTION_MOVE) {
249 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);
250 } else if (pac == ACTION_RESIZE_CORNER) {
251 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);
252 } else if (pac == ACTION_RESIZE_SIDE) {
253 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);
256 track_pointer(loc, pac, pos);
261 void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
263 node_t *n = loc.node;
264 resize_handle_t rh = get_handle(loc.node, pos, pac);
266 uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
267 xcb_timestamp_t last_motion_time = 0;
269 xcb_generic_event_t *evt = NULL;
276 while ((evt = xcb_wait_for_event(dpy)) == NULL) {
279 uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
280 if (resp_type == XCB_MOTION_NOTIFY) {
281 xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
282 uint32_t dtime = e->time - last_motion_time;
283 if (dtime < pointer_motion_interval) {
286 last_motion_time = e->time;
287 int16_t dx = e->root_x - last_motion_x;
288 int16_t dy = e->root_y - last_motion_y;
289 if (pac == ACTION_MOVE) {
290 move_client(&loc, dx, dy);
292 if (honor_size_hints) {
293 resize_client(&loc, rh, e->root_x, e->root_y, false);
295 resize_client(&loc, rh, dx, dy, true);
298 last_motion_x = e->root_x;
299 last_motion_y = e->root_y;
301 } else if (resp_type == XCB_BUTTON_RELEASE) {
306 } while (grabbing && grabbed_node != NULL);
309 xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
311 if (grabbed_node == NULL) {
316 if (pac == ACTION_MOVE) {
317 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);
318 } else if (pac == ACTION_RESIZE_CORNER) {
319 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);
320 } else if (pac == ACTION_RESIZE_SIDE) {
321 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);
324 xcb_rectangle_t r = get_rectangle(NULL, NULL, n);
326 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);
328 if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
329 ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
330 n->client->state == STATE_TILED)) {
331 for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
332 if (f == n || f->client == NULL || !IS_TILED(f->client)) {
335 xcb_rectangle_t r = f->client->tiled_rectangle;
336 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);