]> git.corax.cc Git - mwm/commitdiff
kbptr: Implement keyboard pointer
authorMatthias Kruk <m@m10k.eu>
Wed, 19 May 2021 21:19:59 +0000 (06:19 +0900)
committerMatthias Kruk <m@m10k.eu>
Wed, 19 May 2021 21:19:59 +0000 (06:19 +0900)
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.

Makefile
config.c
kbptr.c [new file with mode: 0644]
kbptr.h [new file with mode: 0644]
mwm.c
mwm.h

index 426a7fb6de2b01d0e66d3c8fe3dd667d4c10bdc8..abb74bce6dbac4f937153c16302b4fc6e6c932c3 100644 (file)
--- 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
index 9f5a5ab101b0bc87c5f9d2bc8c21848fdbb1d222..5d3cd83fdde77e92c32d22ffcd37a503ec2159c2 100644 (file)
--- 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 (file)
index 0000000..43e12d5
--- /dev/null
+++ b/kbptr.c
@@ -0,0 +1,148 @@
+#define _DEFAULT_SOURCE
+#include <X11/Xlib.h>
+#include <unistd.h>
+#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 (file)
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 23e9ddb8121b764828d6243f053dc1c2aa39707f..28203b23db4b7a1b39188040fb75d5309c319469 100644 (file)
--- 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 37ded4f0085ea796f537eae9306c3aad43ff5ddb..6481e781579106ce216099cf7c75ce31575c13c7 100644 (file)
--- 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;