sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 776de741fc95513dbf2d7f5f70f9b6ab0961f21c
parent b0e8f3acab672ee75c470f59ff323804b06d8249
Author: Miles Alan <m@milesalan.com>
Date:   Sat, 18 Apr 2020 20:33:34 -0500

[dwm][patch] multikey: Add patch; allows multiple actions for single keybinding

Diffstat:
Adwm.suckless.org/patches/multikey/dwm-multikey-6.2.diff | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/multikey/index.md | 30++++++++++++++++++++++++++++++
2 files changed, 353 insertions(+), 0 deletions(-)

diff --git a/dwm.suckless.org/patches/multikey/dwm-multikey-6.2.diff b/dwm.suckless.org/patches/multikey/dwm-multikey-6.2.diff @@ -0,0 +1,323 @@ +From eedcb256b8b83257716aa03d76b0709328a2171e Mon Sep 17 00:00:00 2001 +From: Miles Alan <m@milesalan.com> +Date: Sat, 18 Apr 2020 19:25:29 -0500 +Subject: [PATCH] Multikey: Run different actions for single keybinding based + on # of keypresses + +Changed keypress code to allow keybindings to be selectively dispatched when +tapped a specific # of times as specified by the new npresses field on the +Key struct. + +In the example added to the config.def.h, the tiling layout is set when +Mod+w is tapped once, float layout is set when Mod+w is tapped twice, +and monocole layout is set when Mod+w is tapped three times (or held down). +--- + config.def.h | 84 ++++++++++++++++++++++------------------- + config.mk | 2 +- + dwm.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 146 insertions(+), 43 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..dc945b4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -46,10 +46,10 @@ static const Layout layouts[] = { + /* key definitions */ + #define MODKEY Mod1Mask + #define TAGKEYS(KEY,TAG) \ +- { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ +- { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++ { 0, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ ++ { 0, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ ++ { 0, MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ ++ { 0, MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +@@ -59,41 +59,49 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++#define MULTIKEY_THRESHOLD_MS_PRESS 200 ++#define MULTIKEY_THRESHOLD_MS_HOLD 700 ++ + static Key keys[] = { +- /* modifier key function argument */ +- { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +- { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, +- { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, +- { MODKEY, XK_i, incnmaster, {.i = +1 } }, +- { MODKEY, XK_d, incnmaster, {.i = -1 } }, +- { MODKEY, XK_h, setmfact, {.f = -0.05} }, +- { MODKEY, XK_l, setmfact, {.f = +0.05} }, +- { MODKEY, XK_Return, zoom, {0} }, +- { MODKEY, XK_Tab, view, {0} }, +- { MODKEY|ShiftMask, XK_c, killclient, {0} }, +- { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, +- { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, +- { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, +- { MODKEY, XK_space, setlayout, {0} }, +- { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, +- { MODKEY, XK_0, view, {.ui = ~0 } }, +- { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, +- { MODKEY, XK_comma, focusmon, {.i = -1 } }, +- { MODKEY, XK_period, focusmon, {.i = +1 } }, +- { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, +- { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, +- TAGKEYS( XK_1, 0) +- TAGKEYS( XK_2, 1) +- TAGKEYS( XK_3, 2) +- TAGKEYS( XK_4, 3) +- TAGKEYS( XK_5, 4) +- TAGKEYS( XK_6, 5) +- TAGKEYS( XK_7, 6) +- TAGKEYS( XK_8, 7) +- TAGKEYS( XK_9, 8) +- { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ /* npresses, modifier key function argument */ ++ { 0, MODKEY, XK_p, spawn, {.v = dmenucmd } }, ++ { 0, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, ++ { 0, MODKEY, XK_b, togglebar, {0} }, ++ { 0, MODKEY, XK_j, focusstack, {.i = +1 } }, ++ { 0, MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { 0, MODKEY, XK_i, incnmaster, {.i = +1 } }, ++ { 0, MODKEY, XK_d, incnmaster, {.i = -1 } }, ++ { 0, MODKEY, XK_h, setmfact, {.f = -0.05} }, ++ { 0, MODKEY, XK_l, setmfact, {.f = +0.05} }, ++ { 0, MODKEY, XK_Return, zoom, {0} }, ++ { 0, MODKEY, XK_Tab, view, {0} }, ++ { 0, MODKEY|ShiftMask, XK_c, killclient, {0} }, ++ { 0, MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { 0, MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, ++ { 0, MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, ++ { 0, MODKEY, XK_space, setlayout, {0} }, ++ { 0, MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { 0, MODKEY, XK_0, view, {.ui = ~0 } }, ++ { 0, MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, ++ { 0, MODKEY, XK_comma, focusmon, {.i = -1 } }, ++ { 0, MODKEY, XK_period, focusmon, {.i = +1 } }, ++ { 0, MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, ++ { 0, MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ ++ { 1, MODKEY, XK_w, setlayout, {.v = &layouts[0]} }, ++ { 2, MODKEY, XK_w, setlayout, {.v = &layouts[1]} }, ++ { 3, MODKEY, XK_w, setlayout, {.v = &layouts[2]} }, ++ ++ TAGKEYS( XK_1, 0) ++ TAGKEYS( XK_2, 1) ++ TAGKEYS( XK_3, 2) ++ TAGKEYS( XK_4, 3) ++ TAGKEYS( XK_5, 4) ++ TAGKEYS( XK_6, 5) ++ TAGKEYS( XK_7, 6) ++ TAGKEYS( XK_8, 7) ++ TAGKEYS( XK_9, 8) ++ { 0, MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + + /* button definitions */ +diff --git a/config.mk b/config.mk +index 6d36cb7..fe0a2ec 100644 +--- a/config.mk ++++ b/config.mk +@@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lrt -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index 4465af1..e8cc191 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -27,6 +27,7 @@ + #include <stdio.h> + #include <stdlib.h> + #include <string.h> ++#include <time.h> + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> +@@ -40,6 +41,7 @@ + #include <X11/extensions/Xinerama.h> + #endif /* XINERAMA */ + #include <X11/Xft/Xft.h> ++#include <X11/XKBlib.h> + + #include "drw.h" + #include "util.h" +@@ -100,6 +102,7 @@ struct Client { + }; + + typedef struct { ++ unsigned int npresses; + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); +@@ -176,6 +179,10 @@ static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); + 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 manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); +@@ -253,13 +260,14 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, ++ [KeyRelease] = keyrelease, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom timeratom, wmatom[WMLast], netatom[NetLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -268,6 +276,10 @@ static Drw *drw; + static Monitor *mons, *selmon; + static Window root, wmcheckwin; + ++static int multikeypendingindex = -1; ++static timer_t multikeypendingtimer = NULL; ++static int multikeyup = 1; ++ + /* configuration, allows nested code to access above variables */ + #include "config.h" + +@@ -515,6 +527,10 @@ clientmessage(XEvent *e) + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (cme->message_type == timeratom) { ++ keypresstimerdonesync(cme->data.s[0]); ++ return; ++ } + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -991,11 +1007,88 @@ keypress(XEvent *e) + + ev = &e->xkey; + 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 + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) +- && keys[i].func) +- keys[i].func(&(keys[i].arg)); ++ && keys[i].func) { ++ // 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 +@@ -2127,6 +2220,7 @@ zoom(const Arg *arg) + int + main(int argc, char *argv[]) + { ++ XInitThreads(); + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) +@@ -2135,6 +2229,7 @@ main(int argc, char *argv[]) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); ++ XkbSetDetectableAutoRepeat(dpy, True, NULL); + checkotherwm(); + setup(); + #ifdef __OpenBSD__ +-- +2.23.1 + diff --git a/dwm.suckless.org/patches/multikey/index.md b/dwm.suckless.org/patches/multikey/index.md @@ -0,0 +1,30 @@ +multikey +======== + +Description +----------- +This patch allows you to use a single key combination to trigger different +functions based on the number of times you press the key combination +consecutively within a short period of time. This is accomplished by modifying +the `Key` struct to add a new int field `npresses` which can be: + + 0 = Trigger keybinding on 1 keypress (ignoring multikey functionality) + 1 = Trigger keybinding on 1 keypress + 2 = Trigger keybinding on 2 successive keypresess + 3 = Trigger keybinding on 3 successive keypresess + ...n = Trigger keybinding on n successive keypresses + +The maximum / last value set for the key combination can also be triggered by +holding the key down. + +In the example added to the config.def.h, the tiling layout is set when +Mod+w is tapped once, float layout is set when Mod+w is tapped twice, and +monocole layout is set when Mod+w is tapped three times (or held down). + +Download +-------- +* [dwm-multikey-6.2.diff](dwm-multikey-6.2.diff) + +Authors +------- +* Miles Alan - <m@milesalan.com>