Applies the multikey patch

This allows me to have the same key do different things based on the
number of times it is pushed.

This is going to be useful on the pinephone so I can have double click
or click and hold on the limited number of physical buttons
master
Jonathan Hodgson 4 years ago
parent 449d90b2e3
commit c0a50b817c
  1. 134
      config.def.h
  2. 2
      config.mk
  3. 104
      dwm.c

@ -37,7 +37,7 @@ static const Rule rules[] = {
{ "xterm-256color", NULL, NULL, 0, 0, 1, 1, 0, 0, -1 }, { "xterm-256color", NULL, NULL, 0, 0, 1, 1, 0, 0, -1 },
{ "Thunderbird", NULL, NULL, 1 << 8 , 0, 0, 0, 0, 0, -1 }, { "Thunderbird", NULL, NULL, 1 << 8 , 0, 0, 0, 0, 0, -1 },
{ NULL, NULL, "noswallow", 0, 0, 0, 1, 0, 0, -1 }, { NULL, NULL, "noswallow", 0, 0, 0, 1, 0, 0, -1 },
{ "svkbd", NULL, NULL, 0, 1, 0, 1, 0, 1, -1 }, { "svkbd", NULL, NULL, 0, 1, 0, 1, 0, 1, -1 },
{ "Onboard-settings", NULL, NULL, 0, 0, 0, 0, 0, 0, -1 } { "Onboard-settings", NULL, NULL, 0, 0, 0, 0, 0, 0, -1 }
}; };
@ -63,10 +63,10 @@ static const Layout layouts[] = {
/* key definitions */ /* key definitions */
#define MODKEY Mod4Mask #define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \ #define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \ { MODKEY, KEY, 0, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|ControlMask, KEY, 0, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|ShiftMask, KEY, 0, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, { MODKEY|ControlMask|ShiftMask, KEY, 0, toggletag, {.ui = 1 << TAG} },
/* helper for spawning shell commands in the pre dwm-5.0 fashion */ /* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
@ -125,69 +125,76 @@ static const char *backgroundDetails[] = { "background", "--only-notify", NULL }
static const char *powerMenu[] = { "rofi-shutdown", NULL }; static const char *powerMenu[] = { "rofi-shutdown", NULL };
#include "movestack.c" #include "movestack.c"
#define MULTIKEY_THRESHOLD_MS_PRESS 200
#define MULTIKEY_THRESHOLD_MS_HOLD 700
static Key keys[] = { static Key keys[] = {
/* modifier key function argument */ /* modifier key count function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, { MODKEY, XK_p, 0, spawn, {.v = dmenucmd } },
{ MODKEY, XK_Return, spawn, {.v = termcmd } }, { MODKEY, XK_Return, 0, spawn, {.v = termcmd } },
{ MODKEY|ControlMask, XK_Return, spawn, {.v = lfcmd } }, { MODKEY|ControlMask, XK_Return, 0, spawn, {.v = lfcmd } },
{ MODKEY, XK_b, togglebar, {0} }, { MODKEY, XK_b, 0, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } }, { MODKEY, XK_j, 0, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } }, { MODKEY, XK_k, 0, focusstack, {.i = -1 } },
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, { MODKEY|ShiftMask, XK_j, 0, movestack, {.i = +1 } },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, { MODKEY|ShiftMask, XK_k, 0, movestack, {.i = -1 } },
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, { MODKEY, XK_i, 0, incnmaster, {.i = +1 } },
{ MODKEY|ShiftMask, XK_i, incnmaster, {.i = -1 } }, { MODKEY|ShiftMask, XK_i, 0, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} }, { MODKEY, XK_h, 0, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} }, { MODKEY, XK_l, 0, setmfact, {.f = +0.05} },
{ MODKEY|ShiftMask, XK_l, spawn, {.v = logout} }, { MODKEY|ShiftMask, XK_l, 0, spawn, {.v = logout} },
{ MODKEY|ShiftMask, XK_Return, zoom, {0} }, { MODKEY|ShiftMask, XK_Return, 0, zoom, {0} },
{ MODKEY, XK_Tab, toggleAttachBelow, {0} }, { MODKEY, XK_Tab, 0, toggleAttachBelow, {0} },
{ MODKEY, XK_q, killclient, {0} }, { MODKEY, XK_q, 0, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XK_t, 0, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XK_m, 0, setlayout, {.v = &layouts[2]} },
{ MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[3]} }, { MODKEY|ShiftMask, XK_t, 0, setlayout, {.v = &layouts[3]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[4]} }, { MODKEY, XK_f, 0, setlayout, {.v = &layouts[4]} },
{ MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[5]} }, { MODKEY|ShiftMask, XK_f, 0, setlayout, {.v = &layouts[5]} },
{ MODKEY, XK_space, setlayout, {0} }, { MODKEY, XK_space, 0, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} }, { MODKEY|ShiftMask, XK_space, 0, togglefloating, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } }, { MODKEY, XK_0, 0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, { MODKEY|ShiftMask, XK_0, 0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } }, { MODKEY, XK_comma, 0, focusmon, {.i = -1 } },
{ MODKEY, XK_period, focusmon, {.i = +1 } }, { MODKEY, XK_period, 0, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, { MODKEY|ShiftMask, XK_comma, 0, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, { MODKEY|ShiftMask, XK_period, 0, tagmon, {.i = +1 } },
{ 0, XK_Print, spawn, {.v = fullscreenshot } }, { 0, XK_Print, 0, spawn, {.v = fullscreenshot } },
{ ControlMask, XK_Print, spawn, {.v = activescreenshot } }, { ControlMask, XK_Print, 0, spawn, {.v = activescreenshot } },
{ ShiftMask, XK_Print, spawn, {.v = selectscreenshot } }, { ShiftMask, XK_Print, 0, spawn, {.v = selectscreenshot } },
{ 0, XF86XK_AudioPlay, spawn, {.v = playpause } }, { 0, XF86XK_AudioPlay, 0, spawn, {.v = playpause } },
{ MODKEY|ShiftMask, XK_d, spawn, {.v = date } }, { MODKEY|ShiftMask, XK_d, 0, spawn, {.v = date } },
{ MODKEY|ShiftMask, XK_b, spawn, {.v = battery} }, { MODKEY|ShiftMask, XK_b, 0, spawn, {.v = battery} },
{ MODKEY|ShiftMask, XK_Insert, spawn, {.v = greenclip } }, { MODKEY|ShiftMask, XK_Insert, 0, spawn, {.v = greenclip } },
//Applications //Applications
{ MODKEY|ShiftMask, XK_q, spawn, {.v = qutebrowser } }, { MODKEY|ShiftMask, XK_q, 0, spawn, {.v = qutebrowser } },
{ MODKEY , XK_s, spawn, {.v = surf } }, { MODKEY, XK_s, 0, spawn, {.v = surf } },
{ MODKEY, XK_c, spawn, {.v = firefox } }, { MODKEY, XK_c, 0, spawn, {.v = firefox } },
{ MODKEY|ShiftMask, XK_c, spawn, {.v = chromium } }, { MODKEY|ShiftMask, XK_c, 0, spawn, {.v = chromium } },
//Dmenu / Rofi //Dmenu / Rofi
{ MODKEY, XK_u, spawn, {.v = unicode } }, { MODKEY, XK_u, 0, spawn, {.v = unicode } },
{ MODKEY, XK_y, spawn, {.v = youtube } }, { MODKEY, XK_y, 0, spawn, {.v = youtube } },
{ MODKEY, XK_a, spawn, {.v = offlineArchWiki } }, { MODKEY, XK_a, 0, spawn, {.v = offlineArchWiki } },
{ MODKEY|ShiftMask, XK_a, spawn, {.v = screenlayout } }, { MODKEY|ShiftMask, XK_a, 0, spawn, {.v = screenlayout } },
{ MODKEY|ShiftMask, XK_m, spawn, {.v = manPages } }, { MODKEY|ShiftMask, XK_m, 0, spawn, {.v = manPages } },
{ MODKEY|ShiftMask, XK_p, spawn, {.v = ports } }, { MODKEY|ShiftMask, XK_p, 0, spawn, {.v = ports } },
{ MODKEY, XK_w, spawn, {.v = whichproject } }, { MODKEY, XK_w, 0, spawn, {.v = whichproject } },
{ MODKEY|ShiftMask, XK_w, spawn, {.v = project } }, { MODKEY|ShiftMask, XK_w, 0, spawn, {.v = project } },
{ MODKEY, XK_d, spawn, {.v = pass } }, { MODKEY, XK_d, 0, spawn, {.v = pass } },
//Background //Background
{ MODKEY, XK_e, spawn, {.v = setBackgroundRandom } }, { MODKEY, XK_e, 0, spawn, {.v = setBackgroundRandom } },
{ MODKEY|ControlMask, XK_e, spawn, {.v = backgroundDetails } }, { MODKEY|ControlMask, XK_e, 0, spawn, {.v = backgroundDetails } },
//Special keys //Special keys
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = volumeUp } }, { 0, XF86XK_AudioRaiseVolume, 0, spawn, {.v = volumeUp } },
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = volumeDown } }, { 0, XF86XK_AudioLowerVolume, 0, spawn, {.v = volumeDown } },
{ 0, XF86XK_AudioMute, spawn, {.v = volumeToggle } }, { 0, XF86XK_AudioMute, 0, spawn, {.v = volumeToggle } },
{ 0, XF86XK_MonBrightnessUp, spawn, {.v = brightnessUp } }, { 0, XF86XK_MonBrightnessUp, 0, spawn, {.v = brightnessUp } },
{ 0, XF86XK_MonBrightnessDown, spawn, {.v = brightnessDown } }, { 0, XF86XK_MonBrightnessDown, 0, spawn, {.v = brightnessDown } },
{ 0, XF86XK_PowerOff, spawn, {.v = powerMenu } }, { 0, XF86XK_PowerOff, 0, spawn, {.v = powerMenu } },
{ MODKEY|ShiftMask, XK_r, 0, quit, {0} },
TAGKEYS( XK_1, 0) TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1) TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2) TAGKEYS( XK_3, 2)
@ -197,7 +204,6 @@ static Key keys[] = {
TAGKEYS( XK_7, 6) TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7) TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8) TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_r, quit, {0} },
}; };
/* button definitions */ /* button definitions */

@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2
# includes and libs # includes and libs
INCS = -I${X11INC} -I${FREETYPEINC} INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res LIBS = -L${X11LIB} -lrt -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res
# flags # flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}

104
dwm.c

@ -30,6 +30,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h>
#include <X11/cursorfont.h> #include <X11/cursorfont.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -40,6 +41,7 @@
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */ #endif /* XINERAMA */
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#include <X11/XKBlib.h>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <xcb/res.h> #include <xcb/res.h>
@ -106,6 +108,7 @@ struct Client {
typedef struct { typedef struct {
unsigned int mod; unsigned int mod;
KeySym keysym; KeySym keysym;
unsigned int npresses;
void (*func)(const Arg *); void (*func)(const Arg *);
const Arg arg; const Arg arg;
} Key; } Key;
@ -187,6 +190,10 @@ static void grabbuttons(Client *c, int focused);
static void grabkeys(void); static void grabkeys(void);
static void incnmaster(const Arg *arg); static void incnmaster(const Arg *arg);
static void keypress(XEvent *e); static void keypress(XEvent *e);
static void keypresstimerdispatch(int msduration, int data);
static void keypresstimerdone(union sigval timer_data);
static void keypresstimerdonesync(int idx);
static void keyrelease(XEvent *e);
static void killclient(const Arg *arg); static void killclient(const Arg *arg);
static void manage(Window w, XWindowAttributes *wa); static void manage(Window w, XWindowAttributes *wa);
static void mappingnotify(XEvent *e); static void mappingnotify(XEvent *e);
@ -273,13 +280,14 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[Expose] = expose, [Expose] = expose,
[FocusIn] = focusin, [FocusIn] = focusin,
[KeyPress] = keypress, [KeyPress] = keypress,
[KeyRelease] = keyrelease,
[MappingNotify] = mappingnotify, [MappingNotify] = mappingnotify,
[MapRequest] = maprequest, [MapRequest] = maprequest,
[MotionNotify] = motionnotify, [MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify, [PropertyNotify] = propertynotify,
[UnmapNotify] = unmapnotify [UnmapNotify] = unmapnotify
}; };
static Atom wmatom[WMLast], netatom[NetLast]; static Atom timeratom, wmatom[WMLast], netatom[NetLast];
static int running = 1; static int running = 1;
static Cur *cursor[CurLast]; static Cur *cursor[CurLast];
static Clr **scheme; static Clr **scheme;
@ -290,6 +298,10 @@ static Window root, wmcheckwin;
static xcb_connection_t *xcon; static xcb_connection_t *xcon;
static int multikeypendingindex = -1;
static timer_t multikeypendingtimer = NULL;
static int multikeyup = 1;
/* configuration, allows nested code to access above variables */ /* configuration, allows nested code to access above variables */
#include "config.h" #include "config.h"
@ -604,6 +616,11 @@ clientmessage(XEvent *e)
XClientMessageEvent *cme = &e->xclient; XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window); Client *c = wintoclient(cme->window);
if (cme->message_type == timeratom) {
keypresstimerdonesync(cme->data.s[0]);
return;
}
if (!c) if (!c)
return; return;
if (cme->message_type == netatom[NetWMState]) { if (cme->message_type == netatom[NetWMState]) {
@ -1070,11 +1087,88 @@ keypress(XEvent *e) {
ev = &e->xkey; ev = &e->xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
for (i = 0; i < LENGTH(keys); i++) for (i = 0; i < LENGTH(keys); i++){
if (keysym == keys[i].keysym if (keysym == keys[i].keysym
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
&& keys[i].func) && keys[i].func){
keys[i].func(&(keys[i].arg)); // E.g. Normal functionality case - npresses 0 == keydown immediate fn
if (keys[i].npresses == 0) {
keys[i].func(&(keys[i].arg));
break;
}
// Multikey functionality - find index of key, set global, & dispatch
if (
(multikeypendingindex == -1 && multikeyup && keys[i].npresses == 1) ||
(multikeypendingindex != -1 && keys[multikeypendingindex].npresses + 1 == keys[i].npresses)
) {
multikeyup = 0;
multikeypendingindex = i;
keypresstimerdispatch(MULTIKEY_THRESHOLD_MS_PRESS, i);
break;
}
}
}
}
void
keypresstimerdispatch(int msduration, int data)
{
struct sigevent timer_signal_event;
struct itimerspec timer_period;
// Clear out the old timer if any set,and dispatch new timer
if (multikeypendingtimer != NULL) timer_delete(multikeypendingtimer);
timer_signal_event.sigev_notify = SIGEV_THREAD;
timer_signal_event.sigev_notify_function = keypresstimerdone;
timer_signal_event.sigev_value.sival_int = data;
timer_signal_event.sigev_notify_attributes = NULL;
timer_create(CLOCK_MONOTONIC, &timer_signal_event, &multikeypendingtimer);
timer_period.it_value.tv_sec = 0;
timer_period.it_value.tv_nsec = msduration * 1000000;
timer_period.it_interval.tv_sec = 0;
timer_period.it_interval.tv_nsec = 0;
timer_settime(multikeypendingtimer, 0, &timer_period, NULL);
}
void
keypresstimerdone(union sigval timer_data)
{
XEvent ev;
memset(&ev, 0, sizeof ev);
ev.xclient.type = ClientMessage;
ev.xclient.window = root;
ev.xclient.message_type = timeratom;
ev.xclient.format = 16;
ev.xclient.data.s[0] = ((short) timer_data.sival_int);
XSendEvent(dpy, root, False, SubstructureRedirectMask, &ev);
XSync(dpy, False);
}
void
keypresstimerdonesync(int idx)
{
int i, maxidx;
if (keys[idx].npresses == 1 && !multikeyup) {
// Dispatch hold key
maxidx = -1;
for (i = 0; i < LENGTH(keys); i++)
if (keys[i].keysym == keys[idx].keysym) maxidx = i;
if (maxidx != -1)
keypresstimerdispatch(
MULTIKEY_THRESHOLD_MS_HOLD - MULTIKEY_THRESHOLD_MS_PRESS,
maxidx
);
} else if (keys[idx].func) {
// Run the actual keys' fn
keys[idx].func(&(keys[idx].arg));
multikeypendingindex = -1;
}
}
void
keyrelease(XEvent *e)
{
multikeyup = 1;
} }
void void
@ -2439,6 +2533,7 @@ zoom(const Arg *arg)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
XInitThreads();
if (argc == 2 && !strcmp("-v", argv[1])) if (argc == 2 && !strcmp("-v", argv[1]))
die("dwm-"VERSION); die("dwm-"VERSION);
else if (argc != 1) else if (argc != 1)
@ -2449,6 +2544,7 @@ main(int argc, char *argv[])
die("dwm: cannot open display"); die("dwm: cannot open display");
if (!(xcon = XGetXCBConnection(dpy))) if (!(xcon = XGetXCBConnection(dpy)))
die("dwm: cannot get xcb connection\n"); die("dwm: cannot get xcb connection\n");
XkbSetDetectableAutoRepeat(dpy, True, NULL);
checkotherwm(); checkotherwm();
setup(); setup();
#ifdef __OpenBSD__ #ifdef __OpenBSD__

Loading…
Cancel
Save