feat(x11): add XDrawer global phone controls drawer, softkey chord detection, and st/T9 scrollback

This commit is contained in:
Luxferre
2026-10-21 09:26:41 +03:00
parent da24f0d9ac
commit 8045929c87
23 changed files with 1265 additions and 55 deletions
+167 -7
View File
@@ -124,7 +124,7 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
typedef struct HoldKey {
struct HoldKey {
KeySym keysym;
struct HoldKey *next;
};
@@ -1447,6 +1447,107 @@ ishold(KeySym keysym)
return 0;
}
#define SOFTKEY_LEFT 1
#define SOFTKEY_RIGHT 2
#define SOFTKEY_CHORD_MS 200
#define SOFTKEY_MAX_HOLD_MS 480
#define SOFTKEY_SUPPRESS_MS 500
static int softkey_pending = 0;
static long long softkey_first_ms = 0;
static long long softkey_deadline_ms = 0;
static long long softkey_suppress_until_ms = 0;
static long long
dwm_now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (long long)ts.tv_sec * 1000LL + (long long)(ts.tv_nsec / 1000000L);
}
static void
write_softkey_suppress(long long until_ms)
{
char buf[32];
int fd, len;
softkey_suppress_until_ms = until_ms;
fd = open("/tmp/sxmo_softkey_suppress", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd >= 0) {
len = snprintf(buf, sizeof(buf), "%lld\n", until_ms);
(void)write(fd, buf, len);
close(fd);
}
}
static long long
read_softkey_suppress(void)
{
char buf[32];
read_sysfs_str("/tmp/sxmo_softkey_suppress", buf, sizeof(buf));
long long v = atoll(buf);
if (v > softkey_suppress_until_ms)
softkey_suppress_until_ms = v;
return softkey_suppress_until_ms;
}
static int
query_softkeys_down(void)
{
char km[32];
int mask = 0;
if (!XQueryKeymap(dpy, km))
return 0;
if (km[135 / 8] & (1 << (135 % 8)))
mask |= SOFTKEY_LEFT;
if (km[166 / 8] & (1 << (166 % 8)))
mask |= SOFTKEY_RIGHT;
return mask;
}
static int
is_left_softkey(KeySym ks, unsigned int keycode)
{
return ks == XK_Menu || ks == XF86XK_KbdLcdMenu1 || keycode == 135;
}
static int
is_right_softkey(KeySym ks, unsigned int keycode)
{
return ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || keycode == 166;
}
static void
trigger_xdrawer_chord(long long now)
{
const Arg xdrawercmd = { .v = (const char*[]){ "xdrawer", NULL } };
softkey_pending = 0;
softkey_first_ms = 0;
softkey_deadline_ms = 0;
write_softkey_suppress(now + SOFTKEY_SUPPRESS_MS);
XUngrabKeyboard(dpy, CurrentTime);
XFlush(dpy);
spawn(&xdrawercmd);
}
static void
flush_pending_softkey(void)
{
int pending = softkey_pending;
softkey_pending = 0;
softkey_first_ms = 0;
softkey_deadline_ms = 0;
XUngrabKeyboard(dpy, CurrentTime);
XFlush(dpy);
if (pending == SOFTKEY_LEFT) {
const Arg syscmd = { .v = (const char*[]){ "sxmo_appmenu.sh", "sys", NULL } };
spawn(&syscmd);
} else if (pending == SOFTKEY_RIGHT) {
const Arg ctxcmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } };
spawn(&ctxcmd);
}
}
void
keypress(XEvent *e)
{
@@ -1463,6 +1564,10 @@ keypress(XEvent *e)
keysym == XF86XK_KbdLcdMenu1 || keysym == XF86XK_KbdLcdMenu2) &&
is_phone_locked()) {
const Arg unlockcmd = SHCMD("xset dpms 0 0 0; xset dpms force on; sxmo_state.sh set unlock");
softkey_pending = 0;
softkey_first_ms = 0;
softkey_deadline_ms = 0;
write_softkey_suppress(dwm_now_ms() + SOFTKEY_SUPPRESS_MS);
XUngrabKeyboard(dpy, CurrentTime);
XFlush(dpy);
set_hw_backlight(1);
@@ -1470,6 +1575,28 @@ keypress(XEvent *e)
return;
}
if (CLEANMASK(ev->state) == 0 &&
(is_left_softkey(keysym, ev->keycode) || is_right_softkey(keysym, ev->keycode))) {
long long now = dwm_now_ms();
if (now < read_softkey_suppress()) {
XUngrabKeyboard(dpy, CurrentTime);
XFlush(dpy);
return;
}
if (is_left_softkey(keysym, ev->keycode))
softkey_pending |= SOFTKEY_LEFT;
if (is_right_softkey(keysym, ev->keycode))
softkey_pending |= SOFTKEY_RIGHT;
softkey_pending |= query_softkeys_down();
if (softkey_pending == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) {
trigger_xdrawer_chord(now);
} else if (softkey_deadline_ms == 0) {
softkey_first_ms = now;
softkey_deadline_ms = now + SOFTKEY_CHORD_MS;
}
return;
}
if (!selmon->sel && CLEANMASK(ev->state) == 0) {
if (keysym == XK_Return) {
const Arg entercmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } };
@@ -1512,6 +1639,15 @@ keyrelease(XEvent *e)
markreleased(keysym);
if (is_left_softkey(keysym, ev->keycode) || is_right_softkey(keysym, ev->keycode)) {
if (softkey_pending != 0 && softkey_pending != (SOFTKEY_LEFT | SOFTKEY_RIGHT)) {
long long grace = dwm_now_ms() + 65;
if (grace > softkey_deadline_ms)
softkey_deadline_ms = grace;
}
return;
}
for (i = 0; i < LENGTH(keys); i++) {
if (keysym == keys[i].keysym
&& KeyRelease == keys[i].type
@@ -1953,7 +2089,29 @@ run(void)
fds[1].revents = 0;
nfds = 2;
}
if (poll(fds, nfds, -1) < 0) {
int timeout_ms = -1;
if (softkey_pending && softkey_deadline_ms > 0) {
long long now = dwm_now_ms();
int hw_down = query_softkeys_down();
softkey_pending |= hw_down;
if (softkey_pending == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) {
trigger_xdrawer_chord(now);
continue;
}
long long rem = softkey_deadline_ms - now;
if (rem <= 0) {
if (hw_down != 0 && (now - softkey_first_ms) < SOFTKEY_MAX_HOLD_MS) {
softkey_deadline_ms = now + 25;
timeout_ms = 25;
} else {
flush_pending_softkey();
continue;
}
} else {
timeout_ms = (int)rem;
}
}
if (poll(fds, nfds, timeout_ms) < 0) {
if (errno == EINTR)
continue;
break;
@@ -1974,10 +2132,12 @@ run(void)
}
}
if (fds[0].revents & (POLLIN | POLLHUP | POLLERR)) {
if (XNextEvent(dpy, &ev))
return;
if (handler[ev.type])
handler[ev.type](&ev);
while (XPending(dpy)) {
if (XNextEvent(dpy, &ev))
return;
if (handler[ev.type])
handler[ev.type](&ev);
}
}
}
}
@@ -2194,7 +2354,7 @@ setup(void)
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
|ButtonPressMask|PointerMotionMask|EnterWindowMask
|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask|KeyPressMask;
|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask|KeyPressMask|KeyReleaseMask;
XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
XSelectInput(dpy, root, wa.event_mask);
lidfd = open("/dev/input/by-path/platform-gpio-keys-event", O_RDONLY | O_NONBLOCK | O_CLOEXEC);