diff --git a/overlay/rootfs/usr/bin/dmenu b/overlay/rootfs/usr/bin/dmenu index aedc8f1..f60e220 100755 Binary files a/overlay/rootfs/usr/bin/dmenu and b/overlay/rootfs/usr/bin/dmenu differ diff --git a/overlay/rootfs/usr/bin/dwm b/overlay/rootfs/usr/bin/dwm index 41234a3..8d4ac63 100755 Binary files a/overlay/rootfs/usr/bin/dwm and b/overlay/rootfs/usr/bin/dwm differ diff --git a/overlay/rootfs/usr/bin/xdrawer b/overlay/rootfs/usr/bin/xdrawer index 0e7987e..ce9d09d 100755 Binary files a/overlay/rootfs/usr/bin/xdrawer and b/overlay/rootfs/usr/bin/xdrawer differ diff --git a/prebuilt/sxmo-x11/usr/bin/dmenu b/prebuilt/sxmo-x11/usr/bin/dmenu index aedc8f1..f60e220 100755 Binary files a/prebuilt/sxmo-x11/usr/bin/dmenu and b/prebuilt/sxmo-x11/usr/bin/dmenu differ diff --git a/prebuilt/sxmo-x11/usr/bin/dwm b/prebuilt/sxmo-x11/usr/bin/dwm index 41234a3..8d4ac63 100755 Binary files a/prebuilt/sxmo-x11/usr/bin/dwm and b/prebuilt/sxmo-x11/usr/bin/dwm differ diff --git a/prebuilt/sxmo-x11/usr/bin/xdrawer b/prebuilt/sxmo-x11/usr/bin/xdrawer index 0e7987e..ce9d09d 100755 Binary files a/prebuilt/sxmo-x11/usr/bin/xdrawer and b/prebuilt/sxmo-x11/usr/bin/xdrawer differ diff --git a/src/sxmo-dmenu/dmenu.c b/src/sxmo-dmenu/dmenu.c index 24cb193..89255c8 100644 --- a/src/sxmo-dmenu/dmenu.c +++ b/src/sxmo-dmenu/dmenu.c @@ -956,9 +956,37 @@ query_softkeys_down(void) return mask; } +static void +read_softkey_state(int *mask_out, long long *ms_out) +{ + char buf[64]; + int fd = open("/tmp/sxmo_softkey_state", O_RDONLY | O_CLOEXEC); + *mask_out = 0; + *ms_out = 0; + if (fd >= 0) { + ssize_t n = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (n > 0) { + buf[n] = '\0'; + sscanf(buf, "%d %lld", mask_out, ms_out); + } + } +} + +static void +clear_softkey_state(void) +{ + int fd = open("/tmp/sxmo_softkey_state", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd >= 0) { + (void)write(fd, "0 0\n", 4); + close(fd); + } +} + static void spawn_xdrawer_and_exit(void) { + clear_softkey_state(); write_softkey_suppress(dmenu_now_ms() + 500); cleanup(); if (fork() == 0) { @@ -984,12 +1012,15 @@ 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 init_soft_mask = 0; + long long init_soft_ms = 0; int xfd = ConnectionNumber(dpy); + read_softkey_state(&init_soft_mask, &init_soft_ms); + for (;;) { if (!initial_released && query_softkeys_down() == 0) initial_released = 1; @@ -1004,28 +1035,23 @@ run(void) int is_right = (ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || ev.xkey.keycode == 166); if (is_left || is_right) { long long now = dmenu_now_ms(); + int which = (is_left ? 1 : 0) | (is_right ? 2 : 0); + if (ev.type == KeyPress && init_soft_mask != 0 && + (init_soft_mask | which) == 3 && + (now - init_soft_ms) <= 320) { + spawn_xdrawer_and_exit(); + } 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(); + soft_pending |= which | query_softkeys_down(); if (soft_pending == 3) spawn_xdrawer_and_exit(); - if (soft_deadline == 0) { - soft_first_ms = now; - soft_deadline = now + 200; - } + if (soft_deadline == 0) + soft_deadline = now + 30; continue; } if (!issoftkey(ev.xkey.keycode)) { @@ -1077,19 +1103,15 @@ run(void) 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; + clear_softkey_state(); + write_softkey_suppress(now + 250); + if (soft_pending == 1) { + puts((sel) ? sel->text : text); + cleanup(); + exit(0); } else { - write_softkey_suppress(now + 400); - if (soft_pending == 1) { - puts((sel) ? sel->text : text); - cleanup(); - exit(0); - } else { - cleanup(); - exit(1); - } + cleanup(); + exit(1); } } struct pollfd pfd = { xfd, POLLIN, 0 }; diff --git a/src/sxmo-dwm/dwm.c b/src/sxmo-dwm/dwm.c index 98b6f14..9f73e7e 100644 --- a/src/sxmo-dwm/dwm.c +++ b/src/sxmo-dwm/dwm.c @@ -1449,14 +1449,17 @@ ishold(KeySym keysym) #define SOFTKEY_LEFT 1 #define SOFTKEY_RIGHT 2 -#define SOFTKEY_CHORD_MS 200 -#define SOFTKEY_MAX_HOLD_MS 480 +#define SOFTKEY_CHORD_MS 30 +#define SOFTKEY_CANCEL_WINDOW_MS 320 #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 int last_softkey_mask = 0; +static long long last_softkey_ms = 0; +static pid_t last_menu_pgid = 0; static long long dwm_now_ms(void) @@ -1480,6 +1483,18 @@ write_softkey_suppress(long long until_ms) } } +static void +write_softkey_state(int mask, long long ms) +{ + char buf[64]; + int fd = open("/tmp/sxmo_softkey_state", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd >= 0) { + int len = snprintf(buf, sizeof(buf), "%d %lld\n", mask, ms); + (void)write(fd, buf, len); + close(fd); + } +} + static long long read_softkey_suppress(void) { @@ -1517,13 +1532,34 @@ is_right_softkey(KeySym ks, unsigned int keycode) return ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || keycode == 166; } +static pid_t +spawn_pgid(const Arg *arg) +{ + pid_t pid = fork(); + if (pid == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + _exit(EXIT_SUCCESS); + } + return pid; +} + static void trigger_xdrawer_chord(long long now) { const Arg xdrawercmd = { .v = (const char*[]){ "xdrawer", NULL } }; + if (last_menu_pgid > 0) { + kill(-last_menu_pgid, SIGKILL); + last_menu_pgid = 0; + } softkey_pending = 0; softkey_first_ms = 0; softkey_deadline_ms = 0; + last_softkey_mask = 0; + last_softkey_ms = 0; + write_softkey_state(0, 0); write_softkey_suppress(now + SOFTKEY_SUPPRESS_MS); XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); @@ -1541,10 +1577,10 @@ flush_pending_softkey(void) XFlush(dpy); if (pending == SOFTKEY_LEFT) { const Arg syscmd = { .v = (const char*[]){ "sxmo_appmenu.sh", "sys", NULL } }; - spawn(&syscmd); + last_menu_pgid = spawn_pgid(&syscmd); } else if (pending == SOFTKEY_RIGHT) { const Arg ctxcmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } }; - spawn(&ctxcmd); + last_menu_pgid = spawn_pgid(&ctxcmd); } } @@ -1567,6 +1603,9 @@ keypress(XEvent *e) softkey_pending = 0; softkey_first_ms = 0; softkey_deadline_ms = 0; + last_softkey_mask = 0; + last_softkey_ms = 0; + write_softkey_state(0, 0); write_softkey_suppress(dwm_now_ms() + SOFTKEY_SUPPRESS_MS); XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); @@ -1583,16 +1622,25 @@ keypress(XEvent *e) XFlush(dpy); return; } + int which = 0; if (is_left_softkey(keysym, ev->keycode)) - softkey_pending |= SOFTKEY_LEFT; + which |= 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)) { + which |= SOFTKEY_RIGHT; + int combined = softkey_pending | which | query_softkeys_down(); + if (last_softkey_mask != 0 && (now - last_softkey_ms) <= SOFTKEY_CANCEL_WINDOW_MS) + combined |= last_softkey_mask; + if (combined == (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; + } else { + softkey_pending = which; + last_softkey_mask = which; + last_softkey_ms = now; + write_softkey_state(which, now); + if (softkey_deadline_ms == 0) { + softkey_first_ms = now; + softkey_deadline_ms = now + SOFTKEY_CHORD_MS; + } } return; } @@ -1640,11 +1688,6 @@ 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; } @@ -2100,13 +2143,8 @@ run(void) } 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; - } + flush_pending_softkey(); + continue; } else { timeout_ms = (int)rem; } diff --git a/src/xdrawer/xdrawer.cpp b/src/xdrawer/xdrawer.cpp index 9c732da..9bb244c 100644 --- a/src/xdrawer/xdrawer.cpp +++ b/src/xdrawer/xdrawer.cpp @@ -33,13 +33,12 @@ namespace { constexpr int kScreenWidth = 240; constexpr int kScreenHeight = 320; -constexpr int kTopBarHeight = 26; +constexpr int kTopBarHeight = 24; constexpr int kWinX = 0; constexpr int kWinY = kTopBarHeight; constexpr int kWinWidth = kScreenWidth; -constexpr int kWinHeight = kScreenHeight - kTopBarHeight; // 294px (7 * 42px) +constexpr int kWinHeight = kScreenHeight - kTopBarHeight; // 296px flush below 24px dwm bar constexpr int kRowCount = 7; -constexpr int kRowHeight = kWinHeight / kRowCount; // 42px per row // Nerd Font PUA icons matching sxmo_hook_icons.sh & dwm ("Fira Code, DejaVuSansM Nerd Font") constexpr const char* kIconSpeaker = "\xef\x80\xa8"; // U+F028 speaker @@ -587,18 +586,20 @@ int main() { XSetForeground(dpy, gc, 0x222222); XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight); - // 7 Control rows (42px each, 7 * 42 = 294px exact) + // 7 Control rows filling the full 296px height flush from y=24 to y=320 const auto& items = controller.items(); for (size_t i = 0; i < items.size(); ++i) { - int ry = static_cast(i) * kRowHeight; + int ry = static_cast((i * kWinHeight) / kRowCount); + int nextRy = static_cast(((i + 1) * kWinHeight) / kRowCount); + int rowH = nextRy - ry; bool isSel = (i == selected); XSetForeground(dpy, gc, isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e)); - XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, kRowHeight); + XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH); if (isSel) { XSetForeground(dpy, gc, 0xf4d03f); - XFillRectangle(dpy, pix, gc, 0, ry, 4, kRowHeight); + XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH); } const ControlItem& it = items[i]; @@ -607,8 +608,8 @@ int main() { // Icon column (x = 8..28) + Label column (x = 30..112 for sliders, 30..166 for toggles) int labelMaxW = it.isSlider ? 82 : 136; - drawPango(layoutIcon, isSel ? &colYellow : &colLight, 8, ry, kRowHeight, rowIcon); - drawPango(layoutMain, isSel ? &colWhite : &colLight, 30, ry, kRowHeight, + drawPango(layoutIcon, isSel ? &colYellow : &colLight, 8, ry, rowH, rowIcon); + drawPango(layoutMain, isSel ? &colWhite : &colLight, 30, ry, rowH, it.label, labelMaxW); if (it.isSlider) { @@ -627,7 +628,7 @@ int main() { int barX = 114; int barW = 78; int barH = 12; - int barY = ry + (kRowHeight - barH) / 2; + int barY = ry + (rowH - barH) / 2; XSetForeground(dpy, gc, 0x161616); XFillRectangle(dpy, pix, gc, barX, barY, barW, barH); @@ -650,16 +651,16 @@ int main() { std::string pctStr(pctBuf); int pw = pangoWidth(layoutSmall, pctStr); drawPango(layoutSmall, isSel ? &colWhite : &colLight, kWinWidth - pw - 6, - ry, kRowHeight, pctStr); + ry, rowH, pctStr); } else { // Toggle switch icon + ON/OFF label (right-aligned within x = 160..234) bool on = (it.value != 0); std::string stStr = on ? "ON" : "OFF"; int stW = pangoWidth(layoutMain, stStr); drawPango(layoutMain, on ? (isSel ? &colYellow : &colGreen) : &colMuted, - kWinWidth - stW - 8, ry, kRowHeight, stStr); + kWinWidth - stW - 8, ry, rowH, stStr); drawPango(layoutIcon, on ? (isSel ? &colYellow : &colGreen) : &colMuted, - kWinWidth - stW - 30, ry, kRowHeight, + kWinWidth - stW - 30, ry, rowH, on ? kIconToggleOn : kIconToggleOff); } }