feat(x11): add XDrawer global phone controls drawer, softkey chord detection, and st/T9 scrollback
This commit is contained in:
+167
-7
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user