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

@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2
# includes and libs
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
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 <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
@ -40,6 +41,7 @@
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
#include <X11/XKBlib.h>
#include <X11/Xlib-xcb.h>
#include <xcb/res.h>
@ -106,6 +108,7 @@ struct Client {
typedef struct {
unsigned int mod;
KeySym keysym;
unsigned int npresses;
void (*func)(const Arg *);
const Arg arg;
} Key;
@ -187,6 +190,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);
@ -273,13 +280,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;
@ -290,6 +298,10 @@ static Window root, wmcheckwin;
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 */
#include "config.h"
@ -604,6 +616,11 @@ 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]) {
@ -1070,11 +1087,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
@ -2439,6 +2533,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)
@ -2449,6 +2544,7 @@ main(int argc, char *argv[])
die("dwm: cannot open display");
if (!(xcon = XGetXCBConnection(dpy)))
die("dwm: cannot get xcb connection\n");
XkbSetDetectableAutoRepeat(dpy, True, NULL);
checkotherwm();
setup();
#ifdef __OpenBSD__

Loading…
Cancel
Save