mirror of
https://github.com/vale981/bspwm
synced 2025-03-05 09:51:38 -05:00
284 lines
8.3 KiB
C
284 lines
8.3 KiB
C
/* Copyright (c) 2012, Bastien Dejean
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <xcb/xcb_keysyms.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include "bspwm.h"
|
|
#include "query.h"
|
|
#include "settings.h"
|
|
#include "stack.h"
|
|
#include "tree.h"
|
|
#include "monitor.h"
|
|
#include "subscribe.h"
|
|
#include "events.h"
|
|
#include "window.h"
|
|
#include "pointer.h"
|
|
|
|
void pointer_init(void)
|
|
{
|
|
num_lock = modfield_from_keysym(XK_Num_Lock);
|
|
caps_lock = modfield_from_keysym(XK_Caps_Lock);
|
|
scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
|
|
if (caps_lock == XCB_NO_SYMBOL) {
|
|
caps_lock = XCB_MOD_MASK_LOCK;
|
|
}
|
|
grabbing = false;
|
|
grabbed_node = NULL;
|
|
}
|
|
|
|
void grab_buttons(void)
|
|
{
|
|
if (click_to_focus) {
|
|
grab_button(XCB_BUTTON_INDEX_1, XCB_NONE);
|
|
}
|
|
uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
|
|
for (unsigned int i = 0; i < LENGTH(buttons); i++) {
|
|
grab_button(buttons[i], pointer_modifier);
|
|
}
|
|
}
|
|
|
|
void grab_button(uint8_t button, uint16_t modifier)
|
|
{
|
|
#define GRAB(b, m) \
|
|
xcb_grab_button(dpy, false, root, XCB_EVENT_MASK_BUTTON_PRESS, \
|
|
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
|
|
GRAB(button, modifier);
|
|
if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
|
|
}
|
|
if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | num_lock | caps_lock);
|
|
}
|
|
if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | caps_lock | scroll_lock);
|
|
}
|
|
if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | num_lock | scroll_lock);
|
|
}
|
|
if (num_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | num_lock);
|
|
}
|
|
if (caps_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | caps_lock);
|
|
}
|
|
if (scroll_lock != XCB_NO_SYMBOL) {
|
|
GRAB(button, modifier | scroll_lock);
|
|
}
|
|
#undef GRAB
|
|
}
|
|
|
|
void ungrab_buttons(void)
|
|
{
|
|
xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, root, XCB_MOD_MASK_ANY);
|
|
}
|
|
|
|
int16_t modfield_from_keysym(xcb_keysym_t keysym)
|
|
{
|
|
uint16_t modfield = 0;
|
|
xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
|
|
xcb_get_modifier_mapping_reply_t *reply = NULL;
|
|
xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
|
|
|
|
if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
|
|
(reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
|
|
reply->keycodes_per_modifier < 1 ||
|
|
(mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
|
|
goto end;
|
|
}
|
|
|
|
unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
|
|
for (unsigned int i = 0; i < num_mod; i++) {
|
|
for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
|
|
xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
|
|
if (mk == XCB_NO_SYMBOL) {
|
|
continue;
|
|
}
|
|
for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
|
|
if (*k == mk) {
|
|
modfield |= (1 << i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
xcb_key_symbols_free(symbols);
|
|
free(keycodes);
|
|
free(reply);
|
|
return modfield;
|
|
}
|
|
|
|
resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
|
|
{
|
|
resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
|
|
xcb_rectangle_t rect = get_rectangle(NULL, n);
|
|
if (pac == ACTION_RESIZE_SIDE) {
|
|
float W = rect.width;
|
|
float H = rect.height;
|
|
float ratio = W / H;
|
|
float x = pos.x - rect.x;
|
|
float y = pos.y - rect.y;
|
|
float diag_a = ratio * y;
|
|
float diag_b = W - diag_a;
|
|
if (x < diag_a) {
|
|
if (x < diag_b) {
|
|
rh = HANDLE_LEFT;
|
|
} else {
|
|
rh = HANDLE_BOTTOM;
|
|
}
|
|
} else {
|
|
if (x < diag_b) {
|
|
rh = HANDLE_TOP;
|
|
} else {
|
|
rh = HANDLE_RIGHT;
|
|
}
|
|
}
|
|
} else if (pac == ACTION_RESIZE_CORNER) {
|
|
int16_t mid_x = rect.x + (rect.width / 2);
|
|
int16_t mid_y = rect.y + (rect.height / 2);
|
|
if (pos.x > mid_x) {
|
|
if (pos.y > mid_y) {
|
|
rh = HANDLE_BOTTOM_RIGHT;
|
|
} else {
|
|
rh = HANDLE_TOP_RIGHT;
|
|
}
|
|
} else {
|
|
if (pos.y > mid_y) {
|
|
rh = HANDLE_BOTTOM_LEFT;
|
|
} else {
|
|
rh = HANDLE_TOP_LEFT;
|
|
}
|
|
}
|
|
}
|
|
return rh;
|
|
}
|
|
|
|
void grab_pointer(pointer_action_t pac)
|
|
{
|
|
xcb_window_t win = XCB_NONE;
|
|
xcb_point_t pos;
|
|
|
|
query_pointer(&win, &pos);
|
|
|
|
coordinates_t loc;
|
|
|
|
if (!locate_window(win, &loc)) {
|
|
if (pac == ACTION_FOCUS) {
|
|
monitor_t *m = monitor_from_point(pos);
|
|
if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
|
|
focus_node(m, m->desk, m->desk->focus);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (pac == ACTION_FOCUS) {
|
|
if (loc.node != mon->desk->focus) {
|
|
focus_node(loc.monitor, loc.desktop, loc.node);
|
|
} else if (focus_follows_pointer) {
|
|
stack(loc.desktop, loc.node, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (loc.node->client->state == STATE_FULLSCREEN) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
track_pointer(loc, pac, pos);
|
|
}
|
|
|
|
void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
|
|
{
|
|
node_t *n = loc.node;
|
|
resize_handle_t rh = get_handle(loc.node, pos, pac);
|
|
|
|
uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
|
|
xcb_timestamp_t last_motion_time = 0;
|
|
|
|
xcb_generic_event_t *evt = NULL;
|
|
|
|
grabbing = true;
|
|
grabbed_node = n;
|
|
|
|
do {
|
|
free(evt);
|
|
while ((evt = xcb_wait_for_event(dpy)) == NULL) {
|
|
xcb_flush(dpy);
|
|
}
|
|
uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
|
|
if (resp_type == XCB_MOTION_NOTIFY) {
|
|
xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
|
|
int64_t dtime = e->time - last_motion_time;
|
|
if (dtime < 20) {
|
|
continue;
|
|
}
|
|
last_motion_time = e->time;
|
|
int16_t dx = e->root_x - last_motion_x;
|
|
int16_t dy = e->root_y - last_motion_y;
|
|
if (pac == ACTION_MOVE) {
|
|
move_client(&loc, dx, dy);
|
|
} else {
|
|
resize_client(&loc, rh, dx, dy);
|
|
}
|
|
last_motion_x = e->root_x;
|
|
last_motion_y = e->root_y;
|
|
xcb_flush(dpy);
|
|
} else if (resp_type == XCB_BUTTON_RELEASE) {
|
|
grabbing = false;
|
|
} else {
|
|
handle_event(evt);
|
|
}
|
|
} while (grabbing && grabbed_node != NULL);
|
|
|
|
xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
|
|
|
|
if (grabbed_node == NULL) {
|
|
grabbing = false;
|
|
return;
|
|
}
|
|
|
|
xcb_rectangle_t r = get_rectangle(NULL, n);
|
|
|
|
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);
|
|
|
|
if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
|
|
((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
|
|
n->client->state == STATE_TILED)) {
|
|
for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
|
|
if (f == n || f->client == NULL || !IS_TILED(f->client)) {
|
|
continue;
|
|
}
|
|
xcb_rectangle_t r = f->client->tiled_rectangle;
|
|
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);
|
|
}
|
|
}
|
|
}
|