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
+174 -39
View File
@@ -4,6 +4,7 @@
#define _EVDEVK(_v) (0x10081000 + (_v))
#endif
#include <ctype.h>
#include <fcntl.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
@@ -556,11 +557,12 @@ insert:
sel = matchend;
break;
case XK_Escape:
case XF86XK_Back:
case XF86XK_KbdLcdMenu2:
case XF86XK_PowerOff:
cleanup();
exit(1);
case XF86XK_Back:
case XF86XK_KbdLcdMenu2:
return;
case XK_Home:
if (sel == matches) {
cursor = 0;
@@ -610,6 +612,7 @@ insert:
break;
case XK_Menu:
case XF86XK_KbdLcdMenu1:
return;
case XF86XK_Phone:
case XF86XK_PickupPhone:
case XK_Return:
@@ -910,6 +913,8 @@ readstdin(void)
lines = MIN(lines, i);
}
#include <poll.h>
static int
issoftkey(unsigned int keycode)
{
@@ -917,51 +922,181 @@ issoftkey(unsigned int keycode)
return keycode == 124 || keycode == 135 || keycode == 166 || keycode == 177;
}
static long long
dmenu_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 = open("/tmp/sxmo_softkey_suppress", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd >= 0) {
int len = snprintf(buf, sizeof(buf), "%lld\n", until_ms);
(void)write(fd, buf, len);
close(fd);
}
}
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 |= 1;
if (km[166 / 8] & (1 << (166 % 8)))
mask |= 2;
return mask;
}
static void
spawn_xdrawer_and_exit(void)
{
write_softkey_suppress(dmenu_now_ms() + 500);
cleanup();
if (fork() == 0) {
int nullfd = open("/dev/null", O_RDWR);
if (nullfd >= 0) {
dup2(nullfd, 0);
dup2(nullfd, 1);
dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
setsid();
execlp("xdrawer", "xdrawer", (char *)NULL);
execl("/home/user/.local/bin/xdrawer", "xdrawer", (char *)NULL);
execl("/usr/bin/xdrawer", "xdrawer", (char *)NULL);
_exit(0);
}
exit(1);
}
static void
run(void)
{
XEvent ev;
int soft_pending = 0;
long long soft_first_ms = 0;
long long soft_deadline = 0;
long long start_ignore = dmenu_now_ms() + 40;
int initial_released = (query_softkeys_down() == 0);
int xfd = ConnectionNumber(dpy);
while (!XNextEvent(dpy, &ev)) {
if (ev.type == KeyPress || ev.type == KeyRelease) {
if (!issoftkey(ev.xkey.keycode)) {
ev.xkey.window = win;
if (XFilterEvent(&ev, None))
for (;;) {
if (!initial_released && query_softkeys_down() == 0)
initial_released = 1;
while (XPending(dpy)) {
if (XNextEvent(dpy, &ev))
return;
if (!initial_released && query_softkeys_down() == 0)
initial_released = 1;
if (ev.type == KeyPress || ev.type == KeyRelease) {
KeySym ks = XLookupKeysym(&ev.xkey, 0);
int is_left = (ks == XK_Menu || ks == XF86XK_KbdLcdMenu1 || ev.xkey.keycode == 135);
int is_right = (ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || ev.xkey.keycode == 166);
if (is_left || is_right) {
long long now = dmenu_now_ms();
if (ev.type == KeyRelease) {
initial_released = 1;
if (soft_pending != 0 && soft_pending != 3) {
long long grace = now + 65;
if (grace > soft_deadline)
soft_deadline = grace;
}
continue;
}
if (!initial_released || now < start_ignore)
continue;
if (is_left)
soft_pending |= 1;
if (is_right)
soft_pending |= 2;
soft_pending |= query_softkeys_down();
if (soft_pending == 3)
spawn_xdrawer_and_exit();
if (soft_deadline == 0) {
soft_first_ms = now;
soft_deadline = now + 200;
}
continue;
}
if (!issoftkey(ev.xkey.keycode)) {
ev.xkey.window = win;
if (XFilterEvent(&ev, None))
continue;
}
} else if (XFilterEvent(&ev, None)) {
continue;
}
} else if (XFilterEvent(&ev, None)) {
continue;
}
switch(ev.type) {
case MotionNotify:
motionnotify(&ev);
break;
case ButtonRelease:
buttonpress(&ev);
break;
case DestroyNotify:
if (ev.xdestroywindow.window != win)
switch(ev.type) {
case MotionNotify:
motionnotify(&ev);
break;
cleanup();
exit(1);
case Expose:
if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh);
break;
case FocusIn:
/* regrab focus from parent window */
if (ev.xfocus.window != win)
grabfocus();
if (xic)
XSetICFocus(xic);
break;
case KeyPress:
keypress(&ev.xkey);
break;
case SelectionNotify:
if (ev.xselection.property == utf8)
paste();
break;
case ButtonRelease:
buttonpress(&ev);
break;
case DestroyNotify:
if (ev.xdestroywindow.window != win)
break;
cleanup();
exit(1);
case Expose:
if (ev.xexpose.count == 0)
drw_map(drw, win, 0, 0, mw, mh);
break;
case FocusIn:
/* regrab focus from parent window */
if (ev.xfocus.window != win)
grabfocus();
if (xic)
XSetICFocus(xic);
break;
case KeyPress:
keypress(&ev.xkey);
break;
case SelectionNotify:
if (ev.xselection.property == utf8)
paste();
break;
}
}
if (soft_pending && soft_deadline > 0) {
long long now = dmenu_now_ms();
int hw_down = query_softkeys_down();
soft_pending |= hw_down;
if (soft_pending == 3)
spawn_xdrawer_and_exit();
long long rem = soft_deadline - now;
if (rem <= 0) {
if (hw_down != 0 && (now - soft_first_ms) < 480) {
soft_deadline = now + 25;
rem = 25;
} else {
write_softkey_suppress(now + 400);
if (soft_pending == 1) {
puts((sel) ? sel->text : text);
cleanup();
exit(0);
} else {
cleanup();
exit(1);
}
}
}
struct pollfd pfd = { xfd, POLLIN, 0 };
poll(&pfd, 1, (int)rem);
} else {
struct pollfd pfd = { xfd, POLLIN, 0 };
poll(&pfd, 1, initial_released ? -1 : 40);
}
}
}