From: Matthias Kruk Date: Wed, 19 May 2021 21:19:59 +0000 (+0900) Subject: kbptr: Implement keyboard pointer X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=ac270766b78a43284eae4b73d2f46ed92ba241f4;p=mwm kbptr: Implement keyboard pointer The pointer is typically very awkward to use, especially on devices with mice or similar input devices. This commit adds an experimental keyboard pointer, which allows the user to move the pointer and perform clicks through the keyboard. The keyboard pointer follows this basic idea: The user can move the pointer in four directions: North, East, South, West. The pointer has a horizontal stride and a vertical stride (the size of a step in horizontal or vertical direction, respectively). With each step, the respective stride is reduced by half. The origin of the pointer is the center of the focused window, and the initial horizontal and vertical stride are a quarter of the window's width and height, respectively. For example in a 100x100px window (the point (0, 0) being the top left corner), the pointer's origin is (50, 50). If the pointer makes one step north, it will be at (50, 25). Moving another step north will put the pointer at (50, 12.5). The user can also hold the shift key while moving, which will cause the stride to be reduced once before the movement and once after the movement (this seemed useful for targets that are close to the pointer). This pointer behavior has two implications: 1. If the stride reaches zero, the pointer can't be moved anymore 2. The pointer can never leave the window (it asymptotically approaches the outermost pixel, strictly speaking) Re-centering the pointer also replenishes the stride. If a user misses their target, they have to start over from the center of the window. If a user wants to point at another window, they have to move the focus to that window first. --- diff --git a/Makefile b/Makefile index 426a7fb..abb74bc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OUTPUT = mwm -OBJECTS = main.o x.o monitor.o mwm.o workspace.o loop.o client.o common.o layout.o config.o +OBJECTS = main.o x.o monitor.o mwm.o workspace.o loop.o client.o common.o layout.o config.o kbptr.o ifeq ($(PREFIX), ) PREFIX = /usr/local diff --git a/config.c b/config.c index 9f5a5ab..5d3cd83 100644 --- a/config.c +++ b/config.c @@ -2,6 +2,7 @@ #include "theme.h" #include "keys.h" #include "mwm.h" +#include "kbptr.h" #define MODKEY Mod4Mask @@ -86,5 +87,18 @@ struct key_binding config_keybindings[] = { { MODKEY, XK_e, MWM_CMD_SHIFT_MONITOR_FOCUS, (void*)+1 }, { MODKEY | ShiftMask, XK_e, MWM_CMD_SHIFT_WORKSPACE, (void*)+1 }, + { MODKEY, XK_u, MWM_CMD_KBPTR_MOVE, (void*)KBPTR_CENTER }, + { MODKEY, XK_i, MWM_CMD_KBPTR_MOVE, (void*)KBPTR_NORTH }, + { MODKEY, XK_l, MWM_CMD_KBPTR_MOVE, (void*)KBPTR_EAST }, + { MODKEY, XK_k, MWM_CMD_KBPTR_MOVE, (void*)KBPTR_SOUTH }, + { MODKEY, XK_j, MWM_CMD_KBPTR_MOVE, (void*)KBPTR_WEST }, + { MODKEY | ShiftMask, XK_i, MWM_CMD_KBPTR_MOVE, (void*)(KBPTR_NORTH | KBPTR_HALFSTEP) }, + { MODKEY | ShiftMask, XK_l, MWM_CMD_KBPTR_MOVE, (void*)(KBPTR_EAST | KBPTR_HALFSTEP) }, + { MODKEY | ShiftMask, XK_k, MWM_CMD_KBPTR_MOVE, (void*)(KBPTR_SOUTH | KBPTR_HALFSTEP) }, + { MODKEY | ShiftMask, XK_j, MWM_CMD_KBPTR_MOVE, (void*)(KBPTR_WEST | KBPTR_HALFSTEP) }, + { MODKEY, XK_semicolon, MWM_CMD_KBPTR_CLICK, (void*)KBPTR_LEFT }, + { MODKEY | ShiftMask, XK_semicolon, MWM_CMD_KBPTR_CLICK, (void*)KBPTR_RIGHT }, + { MODKEY | ControlMask, XK_semicolon, MWM_CMD_KBPTR_CLICK, (void*)KBPTR_MIDDLE }, + { 0, 0, MWM_CMD_MAX, 0 } }; diff --git a/kbptr.c b/kbptr.c new file mode 100644 index 0000000..43e12d5 --- /dev/null +++ b/kbptr.c @@ -0,0 +1,148 @@ +#define _DEFAULT_SOURCE +#include +#include +#include "mwm.h" +#include "client.h" +#include "kbptr.h" +#include "common.h" + +struct kbptr { + int x; + int y; + int hstride; + int vstride; + struct client *last_client; +}; + +static struct kbptr kbptr = { + .last_client = NULL +}; + +static void do_button(struct mwm *mwm, + struct client *client, + const unsigned int button, + const unsigned int pressrelease) +{ + XButtonEvent event; + unsigned int mask; + unsigned int state; + Display *display; + Window window; + + display = mwm_get_display(mwm); + window = client_get_window(client); + + switch(pressrelease) { + case ButtonPress: + mask = ButtonPressMask; + state = 0; + break; + + case ButtonReleaseMask: + /* + * The button must have been set if it was released, + * hence the button's state mask must be set. + */ + mask = ButtonReleaseMask; + state = button << 8; + break; + + default: + return; + } + + if(XQueryPointer(display, window, + &event.root, &event.window, + &event.x_root, &event.y_root, + &event.x, &event.y, + &event.state) < 0) { + return; + } + + event.type = pressrelease; + event.display = display; + event.window = window; + event.subwindow = None; + event.time = CurrentTime; + + event.state = state; + event.button = button; + event.same_screen = True; + + if(XSendEvent(display, window, True, mask, (XEvent*)&event) >= 0) { + XFlush(display); + } + + return; +} + +void kbptr_move(struct mwm *mwm, struct client *client, long direction) +{ + struct geom client_geom; + unsigned int dir; + unsigned int stepsize; + + client_get_geometry(client, &client_geom); + + if(client != kbptr.last_client) { + kbptr.x = client_geom.w / 2; + kbptr.y = client_geom.h / 2; + kbptr.hstride = kbptr.x / 2; + kbptr.vstride = kbptr.y / 2; + kbptr.last_client = client; + } + + dir = direction & KBPTR_DMASK; + stepsize = direction & KBPTR_HALFSTEP; + + switch(dir) { + case KBPTR_CENTER: + kbptr.x = client_geom.w / 2; + kbptr.y = client_geom.h / 2; + kbptr.hstride = kbptr.x / 2; + kbptr.vstride = kbptr.y / 2; + break; + + case KBPTR_NORTH: + kbptr.vstride >>= stepsize; + kbptr.y -= kbptr.vstride; + kbptr.vstride /= 2; + break; + + case KBPTR_EAST: + kbptr.hstride >>= stepsize; + kbptr.x += kbptr.hstride; + kbptr.hstride /= 2; + break; + + case KBPTR_SOUTH: + kbptr.vstride >>= stepsize; + kbptr.y += kbptr.vstride; + kbptr.vstride /= 2; + break; + + case KBPTR_WEST: + kbptr.hstride >>= stepsize; + kbptr.x -= kbptr.hstride; + kbptr.hstride /= 2; + break; + + default: + return; + } + + XWarpPointer(mwm_get_display(mwm), None, client_get_window(client), + 0, 0, 0, 0, + kbptr.x, kbptr.y); + + return; +} + +void kbptr_click(struct mwm *mwm, struct client *client, long button) +{ + do_button(mwm, client, button, ButtonPress); + usleep(100000); + do_button(mwm, client, button, ButtonRelease); + + return; +} diff --git a/kbptr.h b/kbptr.h new file mode 100644 index 0000000..4a8e99e --- /dev/null +++ b/kbptr.h @@ -0,0 +1,22 @@ +#ifndef MWM_KBPTR_H +#define MWM_KBPTR_H + +struct mwm; +struct client; + +#define KBPTR_CENTER 0 +#define KBPTR_NORTH (1 << 1) +#define KBPTR_EAST (1 << 2) +#define KBPTR_SOUTH (1 << 3) +#define KBPTR_WEST (1 << 4) +#define KBPTR_DMASK (KBPTR_NORTH | KBPTR_EAST | KBPTR_SOUTH | KBPTR_WEST) +#define KBPTR_HALFSTEP (1 << 0) + +#define KBPTR_LEFT Button1 +#define KBPTR_MIDDLE Button2 +#define KBPTR_RIGHT Button3 + +void kbptr_move(struct mwm*, struct client *client, long direction); +void kbptr_click(struct mwm*, struct client *client, long button); + +#endif /* MWM_KBPTR_H */ diff --git a/mwm.c b/mwm.c index 23e9ddb..28203b2 100644 --- a/mwm.c +++ b/mwm.c @@ -24,6 +24,7 @@ #include "common.h" #include "client.h" #include "theme.h" +#include "kbptr.h" typedef void (_mwm_xhandler_t)(struct mwm*, XEvent*); @@ -850,6 +851,36 @@ static void _cmd_quit(struct mwm *mwm, void *arg) return; } +static void _cmd_kbptr_move(struct mwm *mwm, void *arg) +{ + struct client *client; + long dir; + + dir = (long)arg; + client = mwm_get_focused_client(mwm); + + if(client) { + kbptr_move(mwm, client, dir); + } + + return; +} + +static void _cmd_kbptr_click(struct mwm *mwm, void *arg) +{ + struct client *client; + long button; + + button = (long)arg; + client = mwm_get_focused_client(mwm); + + if(client) { + kbptr_click(mwm, client, button); + } + + return; +} + static int _xerror_startup(Display *display, XErrorEvent *event) { fprintf(stderr, "Looks like I'm not your only window manager\n"); @@ -972,6 +1003,8 @@ int mwm_init(struct mwm *mwm) mwm->commands[MWM_CMD_SHIFT_CLIENT] = _cmd_shift_client; mwm->commands[MWM_CMD_SHIFT_MONITOR_FOCUS] = _cmd_shift_monitor_focus; mwm->commands[MWM_CMD_SHIFT_WORKSPACE] = _cmd_shift_workspace; + mwm->commands[MWM_CMD_KBPTR_MOVE] = _cmd_kbptr_move; + mwm->commands[MWM_CMD_KBPTR_CLICK] = _cmd_kbptr_click; return(0); } diff --git a/mwm.h b/mwm.h index 37ded4f..6481e78 100644 --- a/mwm.h +++ b/mwm.h @@ -20,6 +20,8 @@ typedef enum { MWM_CMD_SHIFT_CLIENT, MWM_CMD_SHIFT_WORKSPACE, MWM_CMD_SHIFT_MONITOR_FOCUS, + MWM_CMD_KBPTR_MOVE, + MWM_CMD_KBPTR_CLICK, MWM_CMD_MAX } mwm_cmd_t;