Preload DWM plugins (dialer, calllog, xdrawer) with resident X11/Pango state and FIFO IPC

This commit is contained in:
Luxferre
2026-10-21 11:57:23 +03:00
parent 0a02400505
commit 996e80b095
16 changed files with 753 additions and 407 deletions
+151 -104
View File
@@ -26,6 +26,7 @@
#include <ctime>
#include <fcntl.h>
#include <poll.h>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
@@ -435,55 +436,47 @@ class PhoneController {
} // namespace
static int xdrawer_run_main(int, char**) {
static int xdrawer_run_main(int argc, char** argv) {
gRunning = 1;
int64_t startMs = nowMs();
bool isPreload = false;
for (int i = 1; i < argc; ++i) {
if (argv[i] && std::strcmp(argv[i], "--preload") == 0) {
isPreload = true;
}
}
std::string daemonPidPath = "/tmp/dwm_plugin_xdrawer.pid";
std::string fifoPath = "/tmp/dwm_plugin_xdrawer.fifo";
std::string pidPath = getRuntimeFile("xdrawer.pid");
std::string startPath = getRuntimeFile("xdrawer.start");
int existingPid = readIntFile(pidPath, -1);
if (existingPid > 0 && existingPid != static_cast<int>(::getpid())) {
if (::kill(existingPid, 0) == 0) {
int fd = ::open(startPath.c_str(), O_RDONLY | O_CLOEXEC);
int64_t prevStartMs = 0;
if (fd >= 0) {
char buf[64];
ssize_t n = ::read(fd, buf, sizeof(buf) - 1);
::close(fd);
if (n > 0) {
buf[n] = '\0';
prevStartMs = std::atoll(buf);
}
int existingDaemon = readIntFile(daemonPidPath, -1);
if (existingDaemon > 0 && existingDaemon != static_cast<int>(::getpid()) &&
::kill(existingDaemon, 0) == 0) {
if (!isPreload) {
int ffd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (ffd >= 0) {
(void)::write(ffd, "TOGGLE\n", 7);
::close(ffd);
}
if (startMs - prevStartMs < 480) {
return 0;
}
::kill(existingPid, SIGTERM);
return 0;
}
}
writeIntFile(pidPath, static_cast<int>(::getpid()));
{
int fd = ::open(startPath.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0644);
if (fd >= 0) {
char buf[32];
int len = std::snprintf(buf, sizeof(buf), "%lld\n",
static_cast<long long>(startMs));
(void)::write(fd, buf, len);
::close(fd);
}
return 0;
}
writeIntFile(daemonPidPath, static_cast<int>(::getpid()));
::mkfifo(fifoPath.c_str(), 0666);
::chmod(fifoPath.c_str(), 0666);
int fifoFd = ::open(fifoPath.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC);
struct sigaction sa {};
sa.sa_handler = handleSignal;
sigemptyset(&sa.sa_mask);
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
runAsyncCmd("pkill -x dmenu >/dev/null 2>&1");
Display* dpy = XOpenDisplay(nullptr);
if (!dpy) {
::unlink(pidPath.c_str());
if (fifoFd >= 0) ::close(fifoFd);
::unlink(daemonPidPath.c_str());
return 1;
}
@@ -512,22 +505,10 @@ static int xdrawer_run_main(int, char**) {
XSetClassHint(dpy, win, &ch);
XStoreName(dpy, win, "XDrawer");
XMapRaised(dpy, win);
XSync(dpy, False);
for (int i = 0; i < 60; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(5000);
}
Pixmap pix = XCreatePixmap(dpy, win, kWinWidth, kWinHeight, DefaultDepth(dpy, screen));
GC gc = XCreateGC(dpy, pix, 0, nullptr);
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
// Use the exact same Pango+Xft font stack as dwm ("Fira Code, DejaVuSansM Nerd Font 15px")
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
@@ -559,6 +540,10 @@ static int xdrawer_run_main(int, char**) {
PhoneController controller;
size_t selected = 0;
bool visible = false;
int64_t openStartMs = 0;
int64_t startupIgnoreUntilMs = 0;
bool initialKeysReleased = true;
auto drawPango = [&](PangoLayout* layout, XftColor* color, int x, int y, int h,
const std::string& text, int maxW = -1) {
@@ -589,7 +574,6 @@ static int xdrawer_run_main(int, char**) {
XSetForeground(dpy, gc, 0x222222);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
// 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<int>((i * kWinHeight) / kRowCount);
@@ -609,7 +593,6 @@ static int xdrawer_run_main(int, char**) {
bool activeState = (it.value > 0);
const char* rowIcon = activeState ? it.iconOn : it.iconOff;
// 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, rowH, rowIcon);
drawPango(layoutMain, isSel ? &colWhite : &colLight, 30, ry, rowH,
@@ -627,7 +610,6 @@ static int xdrawer_run_main(int, char**) {
}
pct = std::clamp(pct, 0, 100);
// Slider track (x = 114, w = 78, h = 12) + thumb + right-aligned pct (x = 196..234)
int barX = 114;
int barW = 78;
int barH = 12;
@@ -644,7 +626,6 @@ static int xdrawer_run_main(int, char**) {
XFillRectangle(dpy, pix, gc, barX + 1, barY + 1, fillW, barH - 2);
}
// Slider thumb knob
int knobX = barX + std::clamp(fillW - 2, 0, barW - 5);
XSetForeground(dpy, gc, 0xffffff);
XFillRectangle(dpy, pix, gc, knobX, barY - 2, 5, barH + 4);
@@ -656,7 +637,6 @@ static int xdrawer_run_main(int, char**) {
drawPango(layoutSmall, isSel ? &colWhite : &colLight, kWinWidth - pw - 6,
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);
@@ -668,11 +648,15 @@ static int xdrawer_run_main(int, char**) {
}
}
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
XFlush(dpy);
if (visible) {
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
XFlush(dpy);
}
};
// Pre-warm Pango/FreeType/Xrender glyph caches into offscreen Pixmap at startup
render();
XSync(dpy, False);
auto writeSoftkeySuppress = [](int64_t untilMs) {
int fd = ::open("/tmp/sxmo_softkey_suppress",
@@ -701,27 +685,108 @@ static int xdrawer_run_main(int, char**) {
return mask;
};
// Drain any stale key events that arrived before initial render finished
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
if (ev.type == Expose && ev.xexpose.count == 0) {
render();
}
}
int64_t startupIgnoreUntilMs = std::max(startMs + 220, nowMs() + 60);
bool initialKeysReleased = (querySoftkeysDown(dpy) == 0);
bool closedByKey = false;
int xfd = ConnectionNumber(dpy);
auto closeOtherPlugin = [](const char* activePidName, const char* fifoPathStr) {
if (readIntFile(getRuntimeFile(activePidName), -1) > 0) {
int ffd = ::open(fifoPathStr, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (ffd >= 0) {
(void)::write(ffd, "CLOSE\n", 6);
::close(ffd);
}
}
};
auto showWindow = [&]() {
if (visible) {
return;
}
closeOtherPlugin("xdialer.pid", "/tmp/dwm_plugin_dialer.fifo");
closeOtherPlugin("xcalllog.pid", "/tmp/dwm_plugin_calllog.fifo");
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialKeysReleased = (querySoftkeysDown(dpy) == 0);
visible = true;
XMapRaised(dpy, win);
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
}
XFlush(dpy);
writeIntFile(pidPath, static_cast<int>(::getpid()));
controller.refreshAll();
render();
runAsyncCmd("pkill -x dmenu >/dev/null 2>&1");
};
auto hideWindow = [&](bool drainSoftkeys) {
if (!visible) {
return;
}
if (drainSoftkeys) {
int64_t drainDeadline = nowMs() + 220;
while (nowMs() < drainDeadline && querySoftkeysDown(dpy) != 0) {
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
}
struct pollfd pfd {};
pfd.fd = xfd;
pfd.events = POLLIN;
::poll(&pfd, 1, 10);
}
writeSoftkeySuppress(nowMs() + 350);
}
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XFlush(dpy);
::unlink(pidPath.c_str());
visible = false;
};
if (!isPreload) {
showWindow();
}
while (gRunning) {
if (!initialKeysReleased && querySoftkeysDown(dpy) == 0) {
if (fifoFd >= 0) {
char fbuf[256];
ssize_t fn = ::read(fifoFd, fbuf, sizeof(fbuf) - 1);
if (fn > 0) {
fbuf[fn] = '\0';
std::stringstream ss(fbuf);
std::string cmd;
while (std::getline(ss, cmd)) {
if (!cmd.empty() && cmd.back() == '\r') cmd.pop_back();
if (cmd == "TOGGLE") {
if (!visible) {
showWindow();
} else if (nowMs() - openStartMs >= 350) {
hideWindow(true);
}
} else if (cmd == "OPEN") {
showWindow();
} else if (cmd == "CLOSE") {
hideWindow(false);
}
}
}
}
if (visible && !initialKeysReleased && querySoftkeysDown(dpy) == 0) {
initialKeysReleased = true;
}
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
if (!visible) {
continue;
}
if (!initialKeysReleased && querySoftkeysDown(dpy) == 0) {
initialKeysReleased = true;
}
@@ -751,9 +816,8 @@ static int xdrawer_run_main(int, char**) {
sym == XF86XK_Back || sym == XF86XK_KbdLcdMenu2 ||
ev.xkey.keycode == 135 || ev.xkey.keycode == 166) {
if (initialKeysReleased && nowMs() >= startupIgnoreUntilMs) {
writeSoftkeySuppress(nowMs() + 500);
closedByKey = true;
gRunning = 0;
writeSoftkeySuppress(nowMs() + 450);
hideWindow(true);
break;
}
continue;
@@ -763,9 +827,8 @@ static int xdrawer_run_main(int, char**) {
case XK_Escape:
case XK_BackSpace:
case XK_q:
writeSoftkeySuppress(nowMs() + 400);
closedByKey = true;
gRunning = 0;
writeSoftkeySuppress(nowMs() + 350);
hideWindow(true);
break;
case XK_Up:
case XK_KP_Up:
@@ -826,38 +889,20 @@ static int xdrawer_run_main(int, char**) {
}
}
if (!gRunning) {
break;
}
struct pollfd pfd {};
pfd.fd = xfd;
pfd.events = POLLIN;
::poll(&pfd, 1, initialKeysReleased ? 250 : 40);
int timeoutMs = visible ? (initialKeysReleased ? 250 : 40) : -1;
struct pollfd pfds[2] {};
pfds[0].fd = xfd;
pfds[0].events = POLLIN;
pfds[1].fd = fifoFd;
pfds[1].events = POLLIN;
::poll(pfds, (fifoFd >= 0) ? 2 : 1, timeoutMs);
}
// Absorb any still-held softkeys (or second softkey of a closing chord)
// before releasing XGrabKeyboard so dwm never sees a trailing softkey press.
if (closedByKey) {
int64_t drainDeadline = nowMs() + 260;
while (nowMs() < drainDeadline && querySoftkeysDown(dpy) != 0) {
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
}
struct pollfd pfd {};
pfd.fd = xfd;
pfd.events = POLLIN;
::poll(&pfd, 1, 15);
}
while (XPending(dpy)) {
XEvent ev;
XNextEvent(dpy, &ev);
}
writeSoftkeySuppress(nowMs() + 400);
}
hideWindow(false);
if (fifoFd >= 0) ::close(fifoFd);
::unlink(fifoPath.c_str());
::unlink(daemonPidPath.c_str());
XUngrabKeyboard(dpy, CurrentTime);
pango_font_description_free(descSmall);
pango_font_description_free(descIcon);
pango_font_description_free(descMain);
@@ -870,11 +915,13 @@ static int xdrawer_run_main(int, char**) {
XFreePixmap(dpy, pix);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
::unlink(pidPath.c_str());
return 0;
}
static int xdrawer_plugin_init(const DwmHostApi*) {
static int xdrawer_plugin_init(const DwmHostApi* host) {
if (host && host->spawn_plugin) {
host->spawn_plugin("xdrawer", "--preload");
}
return 0;
}