Add unified Xresources theme engine, Default.xth export, and xthemes manager plugin
This commit is contained in:
@@ -101,33 +101,19 @@ int calllog_run_main(int argc, char** argv) {
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col{};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
XftColor colRed = allocColor("#e74c3c");
|
||||
PluginTheme theme;
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
nullptr, win);
|
||||
XftColor& colWhite = theme.colWhite;
|
||||
XftColor& colLight = theme.colLight;
|
||||
XftColor& colMuted = theme.colMuted;
|
||||
XftColor& colYellow = theme.colYellow;
|
||||
XftColor& colGreen = theme.colGreen;
|
||||
XftColor& colBlue = theme.colBlue;
|
||||
XftColor& colRed = theme.colRed;
|
||||
|
||||
ContactBook book;
|
||||
std::vector<CallLogItem> allLogs;
|
||||
@@ -208,10 +194,10 @@ int calllog_run_main(int argc, char** argv) {
|
||||
const char* kTabLabels[4] = {"All", "Missed", "In", "Out"};
|
||||
|
||||
auto render = [&]() {
|
||||
XSetForeground(dpy, gc, 0x222222);
|
||||
XSetForeground(dpy, gc, theme.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
XSetForeground(dpy, gc, 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
|
||||
for (int t = 0; t < 4; ++t) {
|
||||
@@ -219,9 +205,9 @@ int calllog_run_main(int argc, char** argv) {
|
||||
int tw = ((t + 1) * kWinWidth) / 4 - tx;
|
||||
bool active = (t == filterTab);
|
||||
if (active) {
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, tx, 0, tw, kHeaderH - 2);
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, tx, kHeaderH - 3, tw, 3);
|
||||
}
|
||||
std::string lbl(kTabLabels[t]);
|
||||
@@ -230,7 +216,7 @@ int calllog_run_main(int argc, char** argv) {
|
||||
tx + (tw - lw) / 2, 0, kHeaderH - 2, lbl);
|
||||
}
|
||||
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 1, kWinWidth, 1);
|
||||
|
||||
if (filtered.empty()) {
|
||||
@@ -259,11 +245,11 @@ int calllog_run_main(int argc, char** argv) {
|
||||
bool isSel = (idx == selected);
|
||||
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
|
||||
isSel ? theme.spec.selBg : ((i % 2 == 0) ? theme.spec.bgAltEven : theme.spec.bgAltOdd));
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH);
|
||||
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
|
||||
@@ -313,15 +299,15 @@ int calllog_run_main(int argc, char** argv) {
|
||||
int sbH = std::max(16, (kListH * kVisibleRows) / total);
|
||||
int sbY = kListY +
|
||||
((kListH - sbH) * scrollOffset) / (total - kVisibleRows);
|
||||
XSetForeground(dpy, gc, 0x666666);
|
||||
XSetForeground(dpy, gc, theme.spec.divider);
|
||||
XFillRectangle(dpy, pix, gc, kWinWidth - 3, sbY, 3, sbH);
|
||||
}
|
||||
}
|
||||
|
||||
int sbY = kWinHeight - kSoftbarH;
|
||||
XSetForeground(dpy, gc, 0x181818);
|
||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, kSoftbarH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, 1);
|
||||
|
||||
drawPango(layoutMain, &colLight, 6, sbY + 1, kSoftbarH - 1, "Dialer");
|
||||
@@ -348,6 +334,9 @@ int calllog_run_main(int argc, char** argv) {
|
||||
|
||||
auto showWindow = [&]() {
|
||||
closeOtherPlugins("calllog");
|
||||
bool themeChanged = theme.checkAndReload(
|
||||
dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
nullptr, win);
|
||||
if (!visible) {
|
||||
openStartMs = nowMs();
|
||||
startupIgnoreUntilMs = openStartMs + 90;
|
||||
@@ -356,7 +345,9 @@ int calllog_run_main(int argc, char** argv) {
|
||||
softkeyDeadlineMs = 0;
|
||||
visible = true;
|
||||
XMapRaised(dpy, win);
|
||||
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
|
||||
if (!themeChanged) {
|
||||
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) {
|
||||
@@ -364,7 +355,7 @@ int calllog_run_main(int argc, char** argv) {
|
||||
}
|
||||
usleep(2000);
|
||||
}
|
||||
if (reloadLogsIfNeeded() || filterTab != 0 || selected != 0) {
|
||||
if (reloadLogsIfNeeded() || themeChanged || filterTab != 0 || selected != 0) {
|
||||
filterTab = 0;
|
||||
selected = 0;
|
||||
applyFilter();
|
||||
@@ -373,7 +364,7 @@ int calllog_run_main(int argc, char** argv) {
|
||||
XFlush(dpy);
|
||||
writeIntFile(activePidPath, static_cast<int>(::getpid()));
|
||||
} else {
|
||||
if (reloadLogsIfNeeded()) {
|
||||
if (reloadLogsIfNeeded() || themeChanged) {
|
||||
render();
|
||||
}
|
||||
}
|
||||
@@ -428,6 +419,10 @@ int calllog_run_main(int argc, char** argv) {
|
||||
hideWindow(false);
|
||||
} else if (cmd.rfind("OPEN", 0) == 0) {
|
||||
showWindow();
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall,
|
||||
layoutIcon, nullptr, win);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,9 +609,6 @@ int calllog_run_main(int argc, char** argv) {
|
||||
::unlink(getPluginFifoPath("calllog").c_str());
|
||||
::unlink(daemonPidPath.c_str());
|
||||
|
||||
pango_font_description_free(descIcon);
|
||||
pango_font_description_free(descSmall);
|
||||
pango_font_description_free(descMain);
|
||||
g_object_unref(layoutIcon);
|
||||
g_object_unref(layoutSmall);
|
||||
g_object_unref(layoutMain);
|
||||
|
||||
@@ -198,10 +198,12 @@ int runCameraApp(int argc, char** argv) {
|
||||
Visual* visual = DefaultVisual(dpy, screen);
|
||||
Colormap cmap = DefaultColormap(dpy, screen);
|
||||
|
||||
ThemeSpec initSpec = loadThemeSpec(dpy, screen);
|
||||
|
||||
XSetWindowAttributes swa {};
|
||||
swa.override_redirect = True;
|
||||
swa.background_pixel = 0x181818;
|
||||
swa.border_pixel = 0x005577;
|
||||
swa.background_pixel = initSpec.bgMain;
|
||||
swa.border_pixel = initSpec.selBg;
|
||||
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
VisibilityChangeMask | StructureNotifyMask;
|
||||
|
||||
@@ -223,37 +225,18 @@ int runCameraApp(int argc, char** argv) {
|
||||
GC gc = XCreateGC(dpy, pix, 0, nullptr);
|
||||
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
|
||||
|
||||
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col {};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
XftColor colRed = allocColor("#e74c3c");
|
||||
PluginTheme pt;
|
||||
pt.init(dpy, screen, visual, cmap, win);
|
||||
auto& layoutMain = pt.layoutMain;
|
||||
auto& layoutSmall = pt.layoutSmall;
|
||||
auto& layoutIcon = pt.layoutIcon;
|
||||
auto& colWhite = pt.colWhite;
|
||||
auto& colLight = pt.colLight;
|
||||
auto& colMuted = pt.colMuted;
|
||||
auto& colYellow = pt.colYellow;
|
||||
auto& colGreen = pt.colGreen;
|
||||
auto& colBlue = pt.colBlue;
|
||||
auto& colRed = pt.colRed;
|
||||
|
||||
std::vector<uint32_t> vfFront(kVfW * kVfH, 0x101010);
|
||||
std::vector<uint32_t> vfBack(kVfW * kVfH, 0x101010);
|
||||
@@ -524,9 +507,9 @@ int runCameraApp(int argc, char** argv) {
|
||||
const std::string& centerLabel,
|
||||
const std::string& rightLabel) {
|
||||
const int barY = kWinHeight - 26;
|
||||
XSetForeground(dpy, gc, 0x141414);
|
||||
XSetForeground(dpy, gc, pt.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, barY, kWinWidth, 26);
|
||||
XSetForeground(dpy, gc, 0x2d2d2d);
|
||||
XSetForeground(dpy, gc, pt.spec.divider);
|
||||
XDrawLine(dpy, pix, gc, 0, barY, kWinWidth, barY);
|
||||
|
||||
drawPango(layoutMain, &colBlue, 6, barY, 26, leftLabel);
|
||||
@@ -549,14 +532,15 @@ int runCameraApp(int argc, char** argv) {
|
||||
|
||||
auto drawUI = [&]() {
|
||||
if (!visible) return;
|
||||
pt.checkAndReload();
|
||||
int64_t tNow = nowMs();
|
||||
|
||||
XSetForeground(dpy, gc, 0x181818);
|
||||
XSetForeground(dpy, gc, pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
if (mode == ViewMode::Viewfinder) {
|
||||
// Top status bar (28px)
|
||||
XSetForeground(dpy, gc, 0x141414);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 28);
|
||||
drawPango(layoutIcon, &colGreen, 6, 0, 28, kIconCamera);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 28, "800x600", 85);
|
||||
@@ -590,7 +574,7 @@ int runCameraApp(int argc, char** argv) {
|
||||
drawPango(layoutSmall, &colLight, 16, vfY + 78, 24, errMsg, 208);
|
||||
} else {
|
||||
// Subtle framing reticle in center of viewfinder
|
||||
XSetForeground(dpy, gc, 0x0088bb);
|
||||
XSetForeground(dpy, gc, pt.spec.accentAlt);
|
||||
int cx = kWinWidth / 2;
|
||||
int cy = vfY + kVfH / 2;
|
||||
XDrawLine(dpy, pix, gc, cx - 10, cy, cx - 3, cy);
|
||||
@@ -600,14 +584,14 @@ int runCameraApp(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (tNow < shutterFlashUntilMs) {
|
||||
XSetForeground(dpy, gc, 0xffffff);
|
||||
XSetForeground(dpy, gc, pt.spec.fgBright);
|
||||
XDrawRectangle(dpy, pix, gc, 0, vfY, kVfW - 1, kVfH - 1);
|
||||
XDrawRectangle(dpy, pix, gc, 1, vfY + 1, kVfW - 3, kVfH - 3);
|
||||
}
|
||||
|
||||
if (countdownActive) {
|
||||
int remSec = std::max(1, static_cast<int>((countdownTargetMs - tNow + 999) / 1000));
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, pt.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 78, vfY + 66, 84, 44);
|
||||
drawPango(layoutMain, &colWhite, 92, vfY + 66, 44,
|
||||
"Timer: " + std::to_string(remSec));
|
||||
@@ -615,31 +599,34 @@ int runCameraApp(int argc, char** argv) {
|
||||
|
||||
// Camera control strip (y = 208..270, 62px)
|
||||
const int ctlY = 208;
|
||||
XSetForeground(dpy, gc, 0x1e1e1e);
|
||||
XSetForeground(dpy, gc, pt.spec.bgAltOdd);
|
||||
XFillRectangle(dpy, pix, gc, 0, ctlY, kWinWidth, 62);
|
||||
XSetForeground(dpy, gc, 0x333333);
|
||||
XSetForeground(dpy, gc, pt.spec.border);
|
||||
XDrawLine(dpy, pix, gc, 0, ctlY, kWinWidth, ctlY);
|
||||
|
||||
// Pill 1: Flash (* or 1)
|
||||
XSetForeground(dpy, gc, fl ? 0x004466 : 0x282828);
|
||||
XSetForeground(dpy, gc, fl ? pt.spec.bgInputFocus : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 6, ctlY + 5, 110, 24);
|
||||
drawPango(layoutSmall, fl ? &colYellow : &colLight, 10, ctlY + 5, 24,
|
||||
std::string("[*] Flash:") + (fl ? "ON" : "OFF"), 102);
|
||||
|
||||
// Pill 2: Torch (0 or 2)
|
||||
XSetForeground(dpy, gc, torchOn ? 0x004466 : 0x282828);
|
||||
XSetForeground(dpy, gc,
|
||||
torchOn ? pt.spec.bgInputFocus : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 124, ctlY + 5, 110, 24);
|
||||
drawPango(layoutSmall, torchOn ? &colYellow : &colLight, 128, ctlY + 5,
|
||||
24, std::string("[0] Torch:") + (torchOn ? "ON" : "OFF"), 102);
|
||||
|
||||
// Pill 3: Zoom (Up/Dn)
|
||||
XSetForeground(dpy, gc, zf > 1 ? 0x004466 : 0x282828);
|
||||
XSetForeground(dpy, gc,
|
||||
zf > 1 ? pt.spec.bgInputFocus : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 6, ctlY + 33, 110, 24);
|
||||
drawPango(layoutSmall, &colBlue, 10, ctlY + 33, 24,
|
||||
"[U/D] Zoom:" + std::to_string(zf) + "x", 102);
|
||||
|
||||
// Pill 4: Timer (# or 3)
|
||||
XSetForeground(dpy, gc, timerEnabled ? 0x004466 : 0x282828);
|
||||
XSetForeground(dpy, gc,
|
||||
timerEnabled ? pt.spec.bgInputFocus : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 124, ctlY + 33, 110, 24);
|
||||
drawPango(layoutSmall, timerEnabled ? &colYellow : &colLight, 128,
|
||||
ctlY + 33, 24,
|
||||
@@ -648,7 +635,7 @@ int runCameraApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Gallery", "SNAP", "Close");
|
||||
|
||||
} else if (mode == ViewMode::Gallery) {
|
||||
XSetForeground(dpy, gc, 0x141414);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 28);
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 28, kIconImage);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 28, "Gallery", 110);
|
||||
@@ -684,7 +671,7 @@ int runCameraApp(int argc, char** argv) {
|
||||
|
||||
} else if (mode == ViewMode::ConfirmDelete && !photos.empty()) {
|
||||
const auto& pe = photos[selectedPhoto];
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colRed, 8, 0, 36, kIconTrash);
|
||||
drawPango(layoutMain, &colWhite, 30, 0, 36, "Delete Photo?", 200);
|
||||
@@ -696,8 +683,10 @@ int runCameraApp(int argc, char** argv) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int ry = 156 + i * 40;
|
||||
bool isSel = (confirmSelected == i);
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? (i == 1 ? 0x922b21 : 0x005577) : 0x2a2a2a);
|
||||
XSetForeground(
|
||||
dpy, gc,
|
||||
isSel ? (i == 1 ? pt.spec.dangerBg : pt.spec.selBg)
|
||||
: pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 16, ry, kWinWidth - 32, 34);
|
||||
drawPango(layoutMain, &colWhite, 28, ry, 34,
|
||||
i == 0 ? "No, Keep Photo" : "Yes, Delete Photo", 184);
|
||||
@@ -708,9 +697,9 @@ int runCameraApp(int argc, char** argv) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(frameMutex);
|
||||
if (!statusBanner.empty() && tNow < statusBannerUntilMs) {
|
||||
XSetForeground(dpy, gc, 0x102535);
|
||||
XSetForeground(dpy, gc, pt.spec.bgToast);
|
||||
XFillRectangle(dpy, pix, gc, 8, 174, kWinWidth - 16, 28);
|
||||
XSetForeground(dpy, gc, 0x2ecc71);
|
||||
XSetForeground(dpy, gc, pt.spec.success);
|
||||
XDrawRectangle(dpy, pix, gc, 8, 174, kWinWidth - 17, 27);
|
||||
drawPango(layoutSmall, &colWhite, 14, 174, 28, statusBanner,
|
||||
kWinWidth - 28);
|
||||
@@ -814,6 +803,9 @@ int runCameraApp(int argc, char** argv) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "NOP") {
|
||||
continue;
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
@@ -993,7 +985,10 @@ int runCameraApp(int argc, char** argv) {
|
||||
if (pr > 0) {
|
||||
if (fifoIdx >= 0 && (pfds[fifoIdx].revents & POLLIN)) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "CLOSE") {
|
||||
if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
showWindow("");
|
||||
|
||||
@@ -130,33 +130,19 @@ int runContactsApp(int argc, char** argv) {
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col{};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
XftColor colRed = allocColor("#e74c3c");
|
||||
PluginTheme theme;
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
nullptr, win);
|
||||
XftColor& colWhite = theme.colWhite;
|
||||
XftColor& colLight = theme.colLight;
|
||||
XftColor& colMuted = theme.colMuted;
|
||||
XftColor& colYellow = theme.colYellow;
|
||||
XftColor& colGreen = theme.colGreen;
|
||||
XftColor& colBlue = theme.colBlue;
|
||||
XftColor& colRed = theme.colRed;
|
||||
|
||||
ContactBook book;
|
||||
std::vector<ContactEntry> filtered;
|
||||
@@ -277,9 +263,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
const std::string& rightLabel,
|
||||
XftColor* centerCol = nullptr) {
|
||||
int sbY = kWinHeight - kSoftbarH;
|
||||
XSetForeground(dpy, gc, 0x181818);
|
||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, kSoftbarH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, 1);
|
||||
|
||||
if (!leftLabel.empty()) {
|
||||
@@ -298,14 +284,14 @@ int runContactsApp(int argc, char** argv) {
|
||||
};
|
||||
|
||||
auto render = [&]() {
|
||||
XSetForeground(dpy, gc, 0x222222);
|
||||
XSetForeground(dpy, gc, theme.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
if (mode == ViewMode::List || mode == ViewMode::Options) {
|
||||
// Header bar with live search / count
|
||||
XSetForeground(dpy, gc, 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 1, kWinWidth, 1);
|
||||
|
||||
if (searchQuery.empty()) {
|
||||
@@ -347,10 +333,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
bool isSel = (idx == selected);
|
||||
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
|
||||
isSel ? theme.spec.selBg : ((i % 2 == 0) ? theme.spec.bgAltEven : theme.spec.bgAltOdd));
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
|
||||
@@ -367,7 +353,7 @@ int runContactsApp(int argc, char** argv) {
|
||||
int sbH = std::max(16, (kListH * kVisibleRows) / total);
|
||||
int sbY = kListY +
|
||||
((kListH - sbH) * scrollOffset) / (total - kVisibleRows);
|
||||
XSetForeground(dpy, gc, 0x666666);
|
||||
XSetForeground(dpy, gc, theme.spec.divider);
|
||||
XFillRectangle(dpy, pix, gc, kWinWidth - 3, sbY, 3, sbH);
|
||||
}
|
||||
}
|
||||
@@ -394,18 +380,18 @@ int runContactsApp(int argc, char** argv) {
|
||||
int menuRowH = 34;
|
||||
int menuH = optCount * menuRowH + 4;
|
||||
int menuY = kWinHeight - kSoftbarH - menuH;
|
||||
XSetForeground(dpy, gc, 0x181818);
|
||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 16, menuH);
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
XDrawRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 17, menuH - 1);
|
||||
|
||||
for (int i = 0; i < optCount; ++i) {
|
||||
int ry = menuY + 2 + i * menuRowH;
|
||||
bool isSel = (i == optionSelected);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 10, ry, kWinWidth - 20, menuRowH);
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 10, ry, 4, menuRowH);
|
||||
}
|
||||
XftColor* icCol = (i == 5) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||
@@ -424,11 +410,11 @@ int runContactsApp(int argc, char** argv) {
|
||||
c = filtered[selected];
|
||||
}
|
||||
constexpr int kCardH = 56;
|
||||
XSetForeground(dpy, gc, 0x1b3b4b);
|
||||
XSetForeground(dpy, gc, theme.spec.bgInputFocus);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kCardH);
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, 4, kCardH);
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 0, kCardH - 2, kWinWidth, 2);
|
||||
|
||||
drawPango(layoutIcon, &colYellow, 10, 6, 24, kIconUser);
|
||||
@@ -469,10 +455,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
bool isSel = (idx == detailSelected);
|
||||
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
|
||||
isSel ? theme.spec.selBg : ((i % 2 == 0) ? theme.spec.bgAltEven : theme.spec.bgAltOdd));
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
XftColor* icCol =
|
||||
@@ -485,9 +471,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
|
||||
drawSoftbar("Edit", "Select", "Back", &colGreen);
|
||||
} else if (mode == ViewMode::Edit) {
|
||||
XSetForeground(dpy, gc, 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 1, kWinWidth, 1);
|
||||
|
||||
drawPango(layoutIcon, &colGreen, 8, 0, kHeaderH,
|
||||
@@ -505,9 +491,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
// Field 0: Name box (y = 44..108)
|
||||
auto drawFieldBox = [&](int y, int h, bool active, const char* label,
|
||||
const std::string& val, const char* placeholder) {
|
||||
XSetForeground(dpy, gc, active ? 0x1b3b4b : 0x2a2a2a);
|
||||
XSetForeground(dpy, gc, active ? theme.spec.bgInputFocus : theme.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 6, y, kWinWidth - 12, h);
|
||||
XSetForeground(dpy, gc, active ? 0xf4d03f : 0x444444);
|
||||
XSetForeground(dpy, gc, active ? theme.spec.accent : theme.spec.border);
|
||||
XDrawRectangle(dpy, pix, gc, 6, y, kWinWidth - 13, h - 1);
|
||||
drawPango(layoutSmall, active ? &colYellow : &colMuted, 12, y + 3, 18,
|
||||
label);
|
||||
@@ -533,10 +519,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
// Button Row 2: Pick from Recent Numbers (y = 168..208)
|
||||
auto drawBtnRow = [&](int y, int h, bool active, const char* icon,
|
||||
const char* label, XftColor* icCol) {
|
||||
XSetForeground(dpy, gc, active ? 0x005577 : 0x2c2c2c);
|
||||
XSetForeground(dpy, gc, active ? theme.spec.selBg : theme.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 6, y, kWinWidth - 12, h);
|
||||
if (active) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 6, y, 4, h);
|
||||
}
|
||||
drawPango(layoutIcon, active ? &colYellow : icCol, 14, y, h, icon);
|
||||
@@ -555,9 +541,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
drawSoftbar("Save", (editField >= 2) ? "Select" : "Next",
|
||||
canClearChar ? "Clear" : "Cancel", &colGreen);
|
||||
} else if (mode == ViewMode::PickRecent) {
|
||||
XSetForeground(dpy, gc, 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 1, kWinWidth, 1);
|
||||
drawPango(layoutIcon, &colBlue, 8, 0, kHeaderH, kIconClock);
|
||||
drawPango(layoutMain, &colWhite, 32, 0, kHeaderH, "Recent Numbers", 200);
|
||||
@@ -579,10 +565,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
int rowH = nextRy - ry;
|
||||
bool isSel = (idx == recentSelected);
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
|
||||
isSel ? theme.spec.selBg : ((i % 2 == 0) ? theme.spec.bgAltEven : theme.spec.bgAltOdd));
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
drawPango(layoutIcon, isSel ? &colYellow : &colGreen, 10, ry, rowH,
|
||||
@@ -593,7 +579,7 @@ int runContactsApp(int argc, char** argv) {
|
||||
}
|
||||
drawSoftbar("Use", "Select", "Back", &colGreen);
|
||||
} else if (mode == ViewMode::ConfirmDelete) {
|
||||
XSetForeground(dpy, gc, 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
drawPango(layoutIcon, &colRed, 8, 0, kHeaderH, kIconTrash);
|
||||
drawPango(layoutMain, &colWhite, 32, 0, kHeaderH, "Delete Contact?", 200);
|
||||
@@ -609,10 +595,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int ry = 130 + i * 50;
|
||||
bool isSel = (i == confirmSelected);
|
||||
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x2c2c2c);
|
||||
XSetForeground(dpy, gc, isSel ? theme.spec.selBg : theme.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 8, ry, kWinWidth - 16, 44);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 8, ry, 4, 44);
|
||||
}
|
||||
drawPango(layoutIcon, (i == 1) ? &colRed : &colGreen, 16, ry, 44,
|
||||
@@ -691,6 +677,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
|
||||
auto showWindow = [&](const std::string& arg) {
|
||||
closeOtherPlugins("contacts");
|
||||
bool themeChanged = theme.checkAndReload(
|
||||
dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
nullptr, win);
|
||||
bool openNewMode = (arg.rfind("new:", 0) == 0);
|
||||
std::string prefill = openNewMode ? arg.substr(4) : "";
|
||||
|
||||
@@ -703,7 +692,7 @@ int runContactsApp(int argc, char** argv) {
|
||||
zeroHeld = false;
|
||||
visible = true;
|
||||
XMapRaised(dpy, win);
|
||||
if (!openNewMode) {
|
||||
if (!openNewMode && !themeChanged) {
|
||||
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
|
||||
}
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
@@ -717,8 +706,8 @@ int runContactsApp(int argc, char** argv) {
|
||||
if (openNewMode) {
|
||||
beginNewContact(prefill);
|
||||
render();
|
||||
} else if (reloaded || mode != ViewMode::List || !searchQuery.empty() ||
|
||||
selected != 0) {
|
||||
} else if (reloaded || themeChanged || mode != ViewMode::List ||
|
||||
!searchQuery.empty() || selected != 0) {
|
||||
mode = ViewMode::List;
|
||||
searchQuery.clear();
|
||||
selected = 0;
|
||||
@@ -918,6 +907,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
} else {
|
||||
showWindow("");
|
||||
}
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall,
|
||||
layoutIcon, nullptr, win);
|
||||
render();
|
||||
} else if (cmd == "OPEN") {
|
||||
showWindow("");
|
||||
} else if (cmd.rfind("OPEN ", 0) == 0) {
|
||||
@@ -1443,9 +1436,6 @@ int runContactsApp(int argc, char** argv) {
|
||||
::unlink(getPluginFifoPath("contacts").c_str());
|
||||
::unlink(daemonPidPath.c_str());
|
||||
|
||||
pango_font_description_free(descIcon);
|
||||
pango_font_description_free(descSmall);
|
||||
pango_font_description_free(descMain);
|
||||
g_object_unref(layoutIcon);
|
||||
g_object_unref(layoutSmall);
|
||||
g_object_unref(layoutMain);
|
||||
|
||||
@@ -106,37 +106,19 @@ int dialer_run_main(int argc, char** argv) {
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutNum = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descNum =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font Bold 18px");
|
||||
pango_layout_set_font_description(layoutNum, descNum);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col{};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
PluginTheme theme;
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
layoutNum, win);
|
||||
XftColor& colWhite = theme.colWhite;
|
||||
XftColor& colLight = theme.colLight;
|
||||
XftColor& colMuted = theme.colMuted;
|
||||
XftColor& colYellow = theme.colYellow;
|
||||
XftColor& colGreen = theme.colGreen;
|
||||
XftColor& colBlue = theme.colBlue;
|
||||
|
||||
ContactBook book;
|
||||
std::vector<ContactEntry> allContacts;
|
||||
@@ -241,17 +223,17 @@ int dialer_run_main(int argc, char** argv) {
|
||||
constexpr int kVisibleRows = 5;
|
||||
|
||||
auto render = [&]() {
|
||||
XSetForeground(dpy, gc, 0x222222);
|
||||
XSetForeground(dpy, gc, theme.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
bool topSel = (selected < 0);
|
||||
XSetForeground(dpy, gc, topSel ? 0x1b3b4b : 0x1c1c1c);
|
||||
XSetForeground(dpy, gc, topSel ? theme.spec.bgInputFocus : theme.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||
if (topSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, 4, kHeaderH);
|
||||
}
|
||||
XSetForeground(dpy, gc, topSel ? 0x005577 : 0x444444);
|
||||
XSetForeground(dpy, gc, topSel ? theme.spec.selBg : theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 2, kWinWidth, 2);
|
||||
|
||||
drawPango(layoutIcon, &colGreen, 10, 4, 30, kIconPhone);
|
||||
@@ -315,11 +297,11 @@ int dialer_run_main(int argc, char** argv) {
|
||||
bool isSel = (idx == selected);
|
||||
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
|
||||
isSel ? theme.spec.selBg : ((i % 2 == 0) ? theme.spec.bgAltEven : theme.spec.bgAltOdd));
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH);
|
||||
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0xf4d03f);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
|
||||
@@ -345,15 +327,15 @@ int dialer_run_main(int argc, char** argv) {
|
||||
int sbH = std::max(16, (kListH * kVisibleRows) / total);
|
||||
int sbY = kListY +
|
||||
((kListH - sbH) * scrollOffset) / (total - kVisibleRows);
|
||||
XSetForeground(dpy, gc, 0x666666);
|
||||
XSetForeground(dpy, gc, theme.spec.divider);
|
||||
XFillRectangle(dpy, pix, gc, kWinWidth - 3, sbY, 3, sbH);
|
||||
}
|
||||
}
|
||||
|
||||
int sbY = kWinHeight - kSoftbarH;
|
||||
XSetForeground(dpy, gc, 0x181818);
|
||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, kSoftbarH);
|
||||
XSetForeground(dpy, gc, 0x444444);
|
||||
XSetForeground(dpy, gc, theme.spec.border);
|
||||
XFillRectangle(dpy, pix, gc, 0, sbY, kWinWidth, 1);
|
||||
|
||||
std::string leftLabel = number.empty() ? "Call Log" : "+Sign";
|
||||
@@ -387,6 +369,9 @@ int dialer_run_main(int argc, char** argv) {
|
||||
|
||||
auto showWindow = [&](const std::string& arg) {
|
||||
closeOtherPlugins("dialer");
|
||||
bool themeChanged = theme.checkAndReload(
|
||||
dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
|
||||
layoutNum, win);
|
||||
std::string cleanArg = normalizePhone(arg);
|
||||
if (!visible) {
|
||||
openStartMs = nowMs();
|
||||
@@ -398,7 +383,7 @@ int dialer_run_main(int argc, char** argv) {
|
||||
zeroPressMs = zeroHeld ? openStartMs : 0;
|
||||
visible = true;
|
||||
XMapRaised(dpy, win);
|
||||
if (cleanArg.empty()) {
|
||||
if (cleanArg.empty() && !themeChanged) {
|
||||
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
|
||||
}
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
@@ -409,7 +394,7 @@ int dialer_run_main(int argc, char** argv) {
|
||||
usleep(2000);
|
||||
}
|
||||
bool reloaded = reloadContactsIfNeeded();
|
||||
if (!cleanArg.empty() || reloaded || number != cleanArg) {
|
||||
if (!cleanArg.empty() || reloaded || themeChanged || number != cleanArg) {
|
||||
number = cleanArg;
|
||||
selected = number.empty() ? (allContacts.empty() ? -1 : 0) : -1;
|
||||
updateFilter();
|
||||
@@ -494,6 +479,10 @@ int dialer_run_main(int argc, char** argv) {
|
||||
showWindow("");
|
||||
} else if (cmd.rfind("OPEN ", 0) == 0) {
|
||||
showWindow(cmd.substr(5));
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall,
|
||||
layoutIcon, layoutNum, win);
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,10 +712,6 @@ int dialer_run_main(int argc, char** argv) {
|
||||
::unlink(getPluginFifoPath("dialer").c_str());
|
||||
::unlink(daemonPidPath.c_str());
|
||||
|
||||
pango_font_description_free(descIcon);
|
||||
pango_font_description_free(descSmall);
|
||||
pango_font_description_free(descMain);
|
||||
pango_font_description_free(descNum);
|
||||
g_object_unref(layoutIcon);
|
||||
g_object_unref(layoutSmall);
|
||||
g_object_unref(layoutMain);
|
||||
|
||||
@@ -127,7 +127,10 @@ static int try_dmenu_socket_client(int sub_argc, char **sub_argv, int *exit_code
|
||||
}
|
||||
|
||||
static int try_send_plugin_fifo(const char *plugin_name, int sub_argc, char **sub_argv) {
|
||||
if (sub_argc >= 2 && sub_argv[1] && strcmp(sub_argv[1], "--preload") == 0) {
|
||||
if (sub_argc >= 2 && sub_argv[1] &&
|
||||
(strcmp(sub_argv[1], "--preload") == 0 ||
|
||||
strcmp(sub_argv[1], "--apply") == 0 ||
|
||||
strcmp(sub_argv[1], "--export-default") == 0)) {
|
||||
return 0;
|
||||
}
|
||||
char pid_path[128];
|
||||
@@ -212,6 +215,9 @@ static const char *resolve_plugin_name_from_argv0(const char *argv0) {
|
||||
if (strcasecmp(base, "xcamera") == 0 || strcasecmp(base, "camera") == 0) {
|
||||
return "camera";
|
||||
}
|
||||
if (strcasecmp(base, "xthemes") == 0 || strcasecmp(base, "themes") == 0) {
|
||||
return "themes";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -236,10 +236,12 @@ int runFilesApp(int argc, char** argv) {
|
||||
Visual* visual = DefaultVisual(dpy, screen);
|
||||
Colormap cmap = DefaultColormap(dpy, screen);
|
||||
|
||||
ThemeSpec initSpec = loadThemeSpec(dpy, screen);
|
||||
|
||||
XSetWindowAttributes swa {};
|
||||
swa.override_redirect = True;
|
||||
swa.background_pixel = 0x222222;
|
||||
swa.border_pixel = 0x005577;
|
||||
swa.background_pixel = initSpec.bgMain;
|
||||
swa.border_pixel = initSpec.selBg;
|
||||
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
VisibilityChangeMask | StructureNotifyMask;
|
||||
|
||||
@@ -261,38 +263,19 @@ int runFilesApp(int argc, char** argv) {
|
||||
GC gc = XCreateGC(dpy, pix, 0, nullptr);
|
||||
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
|
||||
|
||||
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col {};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
XftColor colOrange = allocColor("#e67e22");
|
||||
XftColor colRed = allocColor("#e74c3c");
|
||||
PluginTheme pt;
|
||||
pt.init(dpy, screen, visual, cmap, win);
|
||||
auto& layoutMain = pt.layoutMain;
|
||||
auto& layoutSmall = pt.layoutSmall;
|
||||
auto& layoutIcon = pt.layoutIcon;
|
||||
auto& colWhite = pt.colWhite;
|
||||
auto& colLight = pt.colLight;
|
||||
auto& colMuted = pt.colMuted;
|
||||
auto& colYellow = pt.colYellow;
|
||||
auto& colGreen = pt.colGreen;
|
||||
auto& colBlue = pt.colBlue;
|
||||
auto& colOrange = pt.colOrange;
|
||||
auto& colRed = pt.colRed;
|
||||
|
||||
const char* homeEnv = std::getenv("HOME");
|
||||
std::string homeDir = (homeEnv && homeEnv[0]) ? homeEnv : "/home/user";
|
||||
@@ -599,9 +582,9 @@ int runFilesApp(int argc, char** argv) {
|
||||
const std::string& centerLabel,
|
||||
const std::string& rightLabel) {
|
||||
const int barY = kWinHeight - 26;
|
||||
XSetForeground(dpy, gc, 0x141414);
|
||||
XSetForeground(dpy, gc, pt.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, barY, kWinWidth, 26);
|
||||
XSetForeground(dpy, gc, 0x2d2d2d);
|
||||
XSetForeground(dpy, gc, pt.spec.divider);
|
||||
XDrawLine(dpy, pix, gc, 0, barY, kWinWidth, barY);
|
||||
|
||||
drawPango(layoutMain, &colBlue, 6, barY, 26, leftLabel);
|
||||
@@ -624,14 +607,15 @@ int runFilesApp(int argc, char** argv) {
|
||||
|
||||
auto drawUI = [&]() {
|
||||
if (!visible) return;
|
||||
pt.checkAndReload();
|
||||
|
||||
XSetForeground(dpy, gc, 0x222222);
|
||||
XSetForeground(dpy, gc, pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
if (mode == ViewMode::Browser) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 38);
|
||||
XSetForeground(dpy, gc, 0x333333);
|
||||
XSetForeground(dpy, gc, pt.spec.border);
|
||||
XDrawLine(dpy, pix, gc, 0, 37, kWinWidth, 37);
|
||||
|
||||
if (searchQuery.empty()) {
|
||||
@@ -681,12 +665,13 @@ int runFilesApp(int argc, char** argv) {
|
||||
bool isSel = (idx == selected);
|
||||
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, pt.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
XSetForeground(dpy, gc, 0x0088bb);
|
||||
XSetForeground(dpy, gc, pt.spec.accentAlt);
|
||||
XDrawLine(dpy, pix, gc, 0, ry, kWinWidth, ry);
|
||||
} else {
|
||||
XSetForeground(dpy, gc, (i % 2 == 0) ? 0x222222 : 0x1d1d1d);
|
||||
XSetForeground(dpy, gc,
|
||||
(i % 2 == 0) ? pt.spec.bgAltEven : pt.spec.bgAltOdd);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
}
|
||||
|
||||
@@ -738,7 +723,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
searchQuery.empty() ? "Close" : "Clear");
|
||||
|
||||
} else if (mode == ViewMode::Actions) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 34, kIconList);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 34, "File Actions", 200);
|
||||
@@ -755,7 +740,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
if (idx >= static_cast<int>(actionsList.size())) break;
|
||||
int ry = listY + i * rowH;
|
||||
bool isSel = (idx == actionSelected);
|
||||
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
|
||||
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
drawPango(layoutIcon, isSel ? &colYellow : &colBlue, 8, ry, rowH,
|
||||
actionsList[idx].icon);
|
||||
@@ -765,7 +750,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Select", "Select", "Back");
|
||||
|
||||
} else if (mode == ViewMode::TextView) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 32);
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 32, kIconFileText);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 32, viewingFileName, 150);
|
||||
@@ -791,7 +776,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Edit", "Down", "Back");
|
||||
|
||||
} else if (mode == ViewMode::ImageView) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colGreen, 6, 1, 18, kIconImage);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 20, viewingFileName, 145);
|
||||
@@ -807,7 +792,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
|
||||
} else if (mode == ViewMode::PropsView && !filteredItems.empty()) {
|
||||
const auto& it = filteredItems[selected];
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 34, kIconInfo);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 34, "Properties", 200);
|
||||
@@ -838,7 +823,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Back", "Back", "Back");
|
||||
|
||||
} else if (mode == ViewMode::InputModal) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 36, kIconEdit);
|
||||
drawPango(layoutMain, &colWhite, 28, 0, 36, inputTitle, 145);
|
||||
@@ -849,9 +834,9 @@ int runFilesApp(int argc, char** argv) {
|
||||
else if (caseMode == TextCaseMode::Num) mBadge = "[123]";
|
||||
drawPango(layoutSmall, &colYellow, kWinWidth - 46, 0, 36, mBadge);
|
||||
|
||||
XSetForeground(dpy, gc, 0x182630);
|
||||
XSetForeground(dpy, gc, pt.spec.bgInputFocus);
|
||||
XFillRectangle(dpy, pix, gc, 10, 74, kWinWidth - 20, 38);
|
||||
XSetForeground(dpy, gc, 0x0088bb);
|
||||
XSetForeground(dpy, gc, pt.spec.accentAlt);
|
||||
XDrawRectangle(dpy, pix, gc, 10, 74, kWinWidth - 21, 37);
|
||||
drawPango(layoutMain, &colWhite, 16, 74, 38, inputValue + "|",
|
||||
kWinWidth - 32);
|
||||
@@ -863,7 +848,7 @@ int runFilesApp(int argc, char** argv) {
|
||||
|
||||
} else if (mode == ViewMode::ConfirmDelete && !filteredItems.empty()) {
|
||||
const auto& it = filteredItems[selected];
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colRed, 8, 0, 36, kIconTrash);
|
||||
drawPango(layoutMain, &colWhite, 30, 0, 36, "Delete Item?", 200);
|
||||
@@ -878,8 +863,10 @@ int runFilesApp(int argc, char** argv) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int ry = 164 + i * 40;
|
||||
bool isSel = (confirmSelected == i);
|
||||
XSetForeground(dpy, gc,
|
||||
isSel ? (i == 1 ? 0x922b21 : 0x005577) : 0x2a2a2a);
|
||||
XSetForeground(
|
||||
dpy, gc,
|
||||
isSel ? (i == 1 ? pt.spec.dangerBg : pt.spec.selBg)
|
||||
: pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 16, ry, kWinWidth - 32, 34);
|
||||
drawPango(layoutMain, &colWhite, 28, ry, 34,
|
||||
i == 0 ? "No, Keep Item" : "Yes, Delete", 184);
|
||||
@@ -889,9 +876,9 @@ int runFilesApp(int argc, char** argv) {
|
||||
|
||||
int64_t tNow = nowMs();
|
||||
if (!statusBanner.empty() && tNow < statusBannerUntilMs) {
|
||||
XSetForeground(dpy, gc, 0x102535);
|
||||
XSetForeground(dpy, gc, pt.spec.bgToast);
|
||||
XFillRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 16, 28);
|
||||
XSetForeground(dpy, gc, 0x5dade2);
|
||||
XSetForeground(dpy, gc, pt.spec.info);
|
||||
XDrawRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 17, 27);
|
||||
drawPango(layoutSmall, &colWhite, 14, kWinHeight - 58, 28, statusBanner,
|
||||
kWinWidth - 28);
|
||||
@@ -1151,6 +1138,9 @@ int runFilesApp(int argc, char** argv) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "NOP") {
|
||||
continue;
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
@@ -1384,7 +1374,10 @@ int runFilesApp(int argc, char** argv) {
|
||||
int pr = ::poll(pfds, nfds, timeoutMs);
|
||||
if (pr > 0 && fifoIdx >= 0 && (pfds[fifoIdx].revents & POLLIN)) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "CLOSE") {
|
||||
if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
showWindow("");
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#define _EVDEVK(_v) (0x10081000 + (_v))
|
||||
#endif
|
||||
|
||||
#include "theme.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <csignal>
|
||||
@@ -69,6 +71,7 @@ constexpr const char* kIconInfo = "\xef\x81\x9a"; // U+F05A info-circle
|
||||
constexpr const char* kIconRefresh = "\xef\x80\xa1"; // U+F021 refresh
|
||||
constexpr const char* kIconSort = "\xef\x83\x9c"; // U+F0DC sort
|
||||
constexpr const char* kIconEye = "\xef\x81\xae"; // U+F06E eye/view
|
||||
constexpr const char* kIconPalette = "\xef\x87\xbc"; // U+F1FC paint-brush / theme
|
||||
|
||||
inline int64_t nowMs() {
|
||||
struct timespec ts {};
|
||||
@@ -962,6 +965,7 @@ inline void closeOtherPlugins(const char* selfPluginName) {
|
||||
{"xsms.pid", "sms"},
|
||||
{"xfiles.pid", "files"},
|
||||
{"xcamera.pid", "camera"},
|
||||
{"xthemes.pid", "themes"},
|
||||
};
|
||||
for (const auto& p : kAllPlugins) {
|
||||
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
|
||||
@@ -976,6 +980,32 @@ inline void closeOtherPlugins(const char* selfPluginName) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void broadcastThemeReload(const char* selfPluginName = nullptr) {
|
||||
static const char* kPlugins[] = {
|
||||
"dmenu",
|
||||
"xdrawer",
|
||||
"dialer",
|
||||
"calllog",
|
||||
"contacts",
|
||||
"sms",
|
||||
"files",
|
||||
"camera",
|
||||
"themes",
|
||||
};
|
||||
for (const char* name : kPlugins) {
|
||||
if (selfPluginName && std::strcmp(name, selfPluginName) == 0) {
|
||||
continue;
|
||||
}
|
||||
std::string fifoPath = getPluginFifoPath(name);
|
||||
int fd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
(void)::write(fd, "RELOAD_THEME\n", 13);
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
runAsyncCmd("pkill -USR1 -x st >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
inline std::string getModemLogDirPath() {
|
||||
const char* env = std::getenv("SXMO_LOGDIR");
|
||||
if (env && env[0]) {
|
||||
|
||||
@@ -130,10 +130,12 @@ int runSmsApp(int argc, char** argv) {
|
||||
Visual* visual = DefaultVisual(dpy, screen);
|
||||
Colormap cmap = DefaultColormap(dpy, screen);
|
||||
|
||||
ThemeSpec initSpec = loadThemeSpec(dpy, screen);
|
||||
|
||||
XSetWindowAttributes swa {};
|
||||
swa.override_redirect = True;
|
||||
swa.background_pixel = 0x222222;
|
||||
swa.border_pixel = 0x005577;
|
||||
swa.background_pixel = initSpec.bgMain;
|
||||
swa.border_pixel = initSpec.selBg;
|
||||
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
VisibilityChangeMask | StructureNotifyMask;
|
||||
|
||||
@@ -155,37 +157,18 @@ int runSmsApp(int argc, char** argv) {
|
||||
GC gc = XCreateGC(dpy, pix, 0, nullptr);
|
||||
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
|
||||
|
||||
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
|
||||
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
|
||||
|
||||
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descMain =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
|
||||
pango_layout_set_font_description(layoutMain, descMain);
|
||||
|
||||
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descSmall =
|
||||
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
|
||||
pango_layout_set_font_description(layoutSmall, descSmall);
|
||||
|
||||
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
|
||||
PangoFontDescription* descIcon =
|
||||
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
|
||||
pango_layout_set_font_description(layoutIcon, descIcon);
|
||||
|
||||
auto allocColor = [&](const char* hex) {
|
||||
XftColor col {};
|
||||
XftColorAllocName(dpy, visual, cmap, hex, &col);
|
||||
return col;
|
||||
};
|
||||
|
||||
XftColor colWhite = allocColor("#ffffff");
|
||||
XftColor colLight = allocColor("#dddddd");
|
||||
XftColor colMuted = allocColor("#999999");
|
||||
XftColor colYellow = allocColor("#f4d03f");
|
||||
XftColor colGreen = allocColor("#2ecc71");
|
||||
XftColor colBlue = allocColor("#5dade2");
|
||||
XftColor colRed = allocColor("#e74c3c");
|
||||
PluginTheme pt;
|
||||
pt.init(dpy, screen, visual, cmap, win);
|
||||
auto& layoutMain = pt.layoutMain;
|
||||
auto& layoutSmall = pt.layoutSmall;
|
||||
auto& layoutIcon = pt.layoutIcon;
|
||||
auto& colWhite = pt.colWhite;
|
||||
auto& colLight = pt.colLight;
|
||||
auto& colMuted = pt.colMuted;
|
||||
auto& colYellow = pt.colYellow;
|
||||
auto& colGreen = pt.colGreen;
|
||||
auto& colBlue = pt.colBlue;
|
||||
auto& colRed = pt.colRed;
|
||||
|
||||
ContactBook book;
|
||||
std::vector<SmsThread> allThreads;
|
||||
@@ -442,9 +425,9 @@ int runSmsApp(int argc, char** argv) {
|
||||
const std::string& centerLabel,
|
||||
const std::string& rightLabel) {
|
||||
const int barY = kWinHeight - 26;
|
||||
XSetForeground(dpy, gc, 0x141414);
|
||||
XSetForeground(dpy, gc, pt.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 0, barY, kWinWidth, 26);
|
||||
XSetForeground(dpy, gc, 0x2d2d2d);
|
||||
XSetForeground(dpy, gc, pt.spec.divider);
|
||||
XDrawLine(dpy, pix, gc, 0, barY, kWinWidth, barY);
|
||||
|
||||
drawPango(layoutMain, &colBlue, 6, barY, 26, leftLabel);
|
||||
@@ -467,14 +450,15 @@ int runSmsApp(int argc, char** argv) {
|
||||
|
||||
auto drawUI = [&]() {
|
||||
if (!visible) return;
|
||||
pt.checkAndReload();
|
||||
|
||||
XSetForeground(dpy, gc, 0x222222);
|
||||
XSetForeground(dpy, gc, pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
|
||||
|
||||
if (mode == ViewMode::Threads) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 38);
|
||||
XSetForeground(dpy, gc, 0x333333);
|
||||
XSetForeground(dpy, gc, pt.spec.border);
|
||||
XDrawLine(dpy, pix, gc, 0, 37, kWinWidth, 37);
|
||||
|
||||
if (searchQuery.empty()) {
|
||||
@@ -521,12 +505,13 @@ int runSmsApp(int argc, char** argv) {
|
||||
bool isSel = (idx == selectedThread);
|
||||
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, 0x005577);
|
||||
XSetForeground(dpy, gc, pt.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
XSetForeground(dpy, gc, 0x0088bb);
|
||||
XSetForeground(dpy, gc, pt.spec.accentAlt);
|
||||
XDrawLine(dpy, pix, gc, 0, ry, kWinWidth, ry);
|
||||
} else {
|
||||
XSetForeground(dpy, gc, (i % 2 == 0) ? 0x222222 : 0x1d1d1d);
|
||||
XSetForeground(dpy, gc,
|
||||
(i % 2 == 0) ? pt.spec.bgAltEven : pt.spec.bgAltOdd);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
}
|
||||
|
||||
@@ -562,9 +547,9 @@ int runSmsApp(int argc, char** argv) {
|
||||
searchQuery.empty() ? "Close" : "Clear");
|
||||
|
||||
} else if (mode == ViewMode::ThreadView) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
XSetForeground(dpy, gc, 0x333333);
|
||||
XSetForeground(dpy, gc, pt.spec.border);
|
||||
XDrawLine(dpy, pix, gc, 0, 35, kWinWidth, 35);
|
||||
|
||||
std::string cname = book.resolveName(activeNumber);
|
||||
@@ -640,13 +625,15 @@ int runSmsApp(int argc, char** argv) {
|
||||
int cardH = std::min(cg.h, areaH - 4);
|
||||
|
||||
unsigned long bg = isSel
|
||||
? 0x004466
|
||||
: (cg.incoming ? 0x262d32 : 0x1f2d38);
|
||||
? pt.spec.bgInputFocus
|
||||
: (cg.incoming ? pt.spec.bgBubbleIn
|
||||
: pt.spec.bgBubbleOut);
|
||||
XSetForeground(dpy, gc, bg);
|
||||
XFillRectangle(dpy, pix, gc, cardX, curY, cardW, cardH);
|
||||
|
||||
unsigned long accent = cg.incoming ? 0x2ecc71 : 0x5dade2;
|
||||
XSetForeground(dpy, gc, isSel ? 0xf4d03f : accent);
|
||||
unsigned long accent =
|
||||
cg.incoming ? pt.spec.success : pt.spec.info;
|
||||
XSetForeground(dpy, gc, isSel ? pt.spec.accent : accent);
|
||||
if (cg.incoming) {
|
||||
XFillRectangle(dpy, pix, gc, cardX, curY, 3, cardH);
|
||||
} else {
|
||||
@@ -672,9 +659,9 @@ int runSmsApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Options", "Reply", "Back");
|
||||
|
||||
} else if (mode == ViewMode::Compose) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 32);
|
||||
XSetForeground(dpy, gc, 0x333333);
|
||||
XSetForeground(dpy, gc, pt.spec.border);
|
||||
XDrawLine(dpy, pix, gc, 0, 31, kWinWidth, 31);
|
||||
|
||||
drawPango(layoutIcon, &colBlue, 6, 0, 30, kIconSend);
|
||||
@@ -703,9 +690,10 @@ int runSmsApp(int argc, char** argv) {
|
||||
|
||||
// Field 0: Recipient
|
||||
bool toSel = (composeField == 0);
|
||||
XSetForeground(dpy, gc, toSel ? 0x004466 : 0x1a1a1a);
|
||||
XSetForeground(dpy, gc,
|
||||
toSel ? pt.spec.bgInputFocus : pt.spec.bgInput);
|
||||
XFillRectangle(dpy, pix, gc, 6, 36, kWinWidth - 12, 32);
|
||||
XSetForeground(dpy, gc, toSel ? 0x0088bb : 0x383838);
|
||||
XSetForeground(dpy, gc, toSel ? pt.spec.accentAlt : pt.spec.border);
|
||||
XDrawRectangle(dpy, pix, gc, 6, 36, kWinWidth - 13, 31);
|
||||
|
||||
std::string cname = book.resolveName(composeRecipient);
|
||||
@@ -719,9 +707,10 @@ int runSmsApp(int argc, char** argv) {
|
||||
|
||||
// Field 1: Message Body
|
||||
bool msgSel = (composeField == 1);
|
||||
XSetForeground(dpy, gc, msgSel ? 0x182630 : 0x181818);
|
||||
XSetForeground(dpy, gc,
|
||||
msgSel ? pt.spec.bgInputFocus : pt.spec.bgInput);
|
||||
XFillRectangle(dpy, pix, gc, 6, 72, kWinWidth - 12, 156);
|
||||
XSetForeground(dpy, gc, msgSel ? 0x0088bb : 0x383838);
|
||||
XSetForeground(dpy, gc, msgSel ? pt.spec.accentAlt : pt.spec.border);
|
||||
XDrawRectangle(dpy, pix, gc, 6, 72, kWinWidth - 13, 155);
|
||||
|
||||
std::string bodyDisp = composeText;
|
||||
@@ -752,15 +741,15 @@ int runSmsApp(int argc, char** argv) {
|
||||
// Field 2 & 3: Send & Save Draft buttons
|
||||
bool sendSel = (composeField == 2);
|
||||
bool draftSel = (composeField == 3);
|
||||
XSetForeground(dpy, gc, sendSel ? 0x005577 : 0x262626);
|
||||
XSetForeground(dpy, gc, sendSel ? pt.spec.selBg : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 6, 234, 118, 30);
|
||||
XSetForeground(dpy, gc, sendSel ? 0x0088bb : 0x444444);
|
||||
XSetForeground(dpy, gc, sendSel ? pt.spec.accentAlt : pt.spec.border);
|
||||
XDrawRectangle(dpy, pix, gc, 6, 234, 117, 29);
|
||||
drawPango(layoutMain, &colGreen, 18, 234, 30, "Send SMS", 100);
|
||||
|
||||
XSetForeground(dpy, gc, draftSel ? 0x005577 : 0x262626);
|
||||
XSetForeground(dpy, gc, draftSel ? pt.spec.selBg : pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 130, 234, 104, 30);
|
||||
XSetForeground(dpy, gc, draftSel ? 0x0088bb : 0x444444);
|
||||
XSetForeground(dpy, gc, draftSel ? pt.spec.accentAlt : pt.spec.border);
|
||||
XDrawRectangle(dpy, pix, gc, 130, 234, 103, 29);
|
||||
drawPango(layoutMain, &colYellow, 146, 234, 30, "Draft", 84);
|
||||
|
||||
@@ -778,7 +767,7 @@ int runSmsApp(int argc, char** argv) {
|
||||
}
|
||||
|
||||
} else if (mode == ViewMode::PickContact) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
|
||||
drawPango(layoutIcon, &colBlue, 8, 0, 34, kIconBook);
|
||||
drawPango(layoutMain, &colWhite, 30, 0, 34, "Select Recipient", 200);
|
||||
@@ -799,7 +788,7 @@ int runSmsApp(int argc, char** argv) {
|
||||
const auto& c = pickerContacts[idx];
|
||||
int ry = listY + i * rowH;
|
||||
bool isSel = (idx == selectedPicker);
|
||||
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
|
||||
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
drawPango(layoutIcon, c.isRecent ? &colYellow : &colBlue, 6, ry + 1,
|
||||
20, c.isRecent ? kIconClock : kIconUser);
|
||||
@@ -811,7 +800,7 @@ int runSmsApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Select", "Select", "Back");
|
||||
|
||||
} else if (mode == ViewMode::Options) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
|
||||
drawPango(layoutIcon, &colBlue, 8, 0, 34, kIconList);
|
||||
drawPango(layoutMain, &colWhite, 30, 0, 34, "Message Options", 200);
|
||||
@@ -821,7 +810,7 @@ int runSmsApp(int argc, char** argv) {
|
||||
for (size_t i = 0; i < optionsList.size(); ++i) {
|
||||
int ry = listY + static_cast<int>(i) * rowH;
|
||||
bool isSel = (static_cast<int>(i) == selectedOption);
|
||||
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
|
||||
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
|
||||
drawPango(layoutIcon, isSel ? &colYellow : &colBlue, 10, ry, rowH,
|
||||
optionsList[i].icon);
|
||||
@@ -831,7 +820,7 @@ int runSmsApp(int argc, char** argv) {
|
||||
drawSoftkeyBar("Select", "Select", "Back");
|
||||
|
||||
} else if (mode == ViewMode::ConfirmDelete) {
|
||||
XSetForeground(dpy, gc, 0x161616);
|
||||
XSetForeground(dpy, gc, pt.spec.bgHeader);
|
||||
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
|
||||
drawPango(layoutIcon, &colRed, 8, 0, 36, kIconTrash);
|
||||
drawPango(layoutMain, &colWhite, 30, 0, 36, "Delete Thread?", 200);
|
||||
@@ -844,7 +833,10 @@ int runSmsApp(int argc, char** argv) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int ry = 164 + i * 40;
|
||||
bool isSel = (confirmSelected == i);
|
||||
XSetForeground(dpy, gc, isSel ? (i == 1 ? 0x922b21 : 0x005577) : 0x2a2a2a);
|
||||
XSetForeground(
|
||||
dpy, gc,
|
||||
isSel ? (i == 1 ? pt.spec.dangerBg : pt.spec.selBg)
|
||||
: pt.spec.bgButton);
|
||||
XFillRectangle(dpy, pix, gc, 16, ry, kWinWidth - 32, 34);
|
||||
drawPango(layoutMain, &colWhite, 28, ry, 34,
|
||||
i == 0 ? "No, Keep Thread" : "Yes, Delete Thread", 184);
|
||||
@@ -856,9 +848,9 @@ int runSmsApp(int argc, char** argv) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(sendMutex);
|
||||
if (!statusBanner.empty() && tNow < statusBannerUntilMs) {
|
||||
XSetForeground(dpy, gc, 0x102535);
|
||||
XSetForeground(dpy, gc, pt.spec.bgToast);
|
||||
XFillRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 16, 28);
|
||||
XSetForeground(dpy, gc, 0x5dade2);
|
||||
XSetForeground(dpy, gc, pt.spec.info);
|
||||
XDrawRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 17, 27);
|
||||
drawPango(layoutSmall, &colWhite, 14, kWinHeight - 58, 28, statusBanner,
|
||||
kWinWidth - 28);
|
||||
@@ -1106,6 +1098,9 @@ int runSmsApp(int argc, char** argv) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "NOP") {
|
||||
continue;
|
||||
} else if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
@@ -1416,7 +1411,10 @@ int runSmsApp(int argc, char** argv) {
|
||||
if (pr > 0) {
|
||||
if (fifoIdx >= 0 && (pfds[fifoIdx].revents & POLLIN)) {
|
||||
for (const auto& cmd : readFifoLines(fifoFd)) {
|
||||
if (cmd == "CLOSE") {
|
||||
if (cmd == "RELOAD_THEME") {
|
||||
pt.reload();
|
||||
if (visible) drawUI();
|
||||
} else if (cmd == "CLOSE") {
|
||||
hideWindow();
|
||||
} else if (cmd == "OPEN") {
|
||||
showWindow("");
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
#ifndef DWM_PLUGINS_THEME_H
|
||||
#define DWM_PLUGINS_THEME_H
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xft/Xft.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <pango/pango.h>
|
||||
#include <pango/pangoxft.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace bananarchy {
|
||||
|
||||
inline std::string themeTrim(const std::string& s) {
|
||||
size_t b = 0;
|
||||
while (b < s.size() && std::isspace(static_cast<unsigned char>(s[b]))) {
|
||||
++b;
|
||||
}
|
||||
size_t e = s.size();
|
||||
while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1]))) {
|
||||
--e;
|
||||
}
|
||||
if (e >= b + 2 &&
|
||||
((s[b] == '"' && s[e - 1] == '"') || (s[b] == '\'' && s[e - 1] == '\''))) {
|
||||
return s.substr(b + 1, e - b - 2);
|
||||
}
|
||||
return s.substr(b, e - b);
|
||||
}
|
||||
|
||||
inline int themeHexNibble(char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
|
||||
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline bool parseHexRgb(const std::string& raw, uint32_t* outRgb) {
|
||||
std::string s = themeTrim(raw);
|
||||
if (s.rfind("0x", 0) == 0 || s.rfind("0X", 0) == 0) {
|
||||
s = "#" + s.substr(2);
|
||||
}
|
||||
if (s.size() == 7 && s[0] == '#') {
|
||||
uint32_t v = 0;
|
||||
for (int i = 1; i < 7; ++i) {
|
||||
int n = themeHexNibble(s[i]);
|
||||
if (n < 0) return false;
|
||||
v = (v << 4) | static_cast<uint32_t>(n);
|
||||
}
|
||||
*outRgb = v;
|
||||
return true;
|
||||
}
|
||||
if (s.size() == 4 && s[0] == '#') {
|
||||
int r = themeHexNibble(s[1]);
|
||||
int g = themeHexNibble(s[2]);
|
||||
int b = themeHexNibble(s[3]);
|
||||
if (r < 0 || g < 0 || b < 0) return false;
|
||||
*outRgb = static_cast<uint32_t>(((r * 17) << 16) | ((g * 17) << 8) | (b * 17));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string formatHexRgb(uint32_t rgb) {
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "#%06x", static_cast<unsigned int>(rgb & 0xffffffu));
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline uint32_t adjustRgb(uint32_t rgb, int delta) {
|
||||
int r = std::clamp(static_cast<int>((rgb >> 16) & 0xff) + delta, 0, 255);
|
||||
int g = std::clamp(static_cast<int>((rgb >> 8) & 0xff) + delta, 0, 255);
|
||||
int b = std::clamp(static_cast<int>(rgb & 0xff) + delta, 0, 255);
|
||||
return static_cast<uint32_t>((r << 16) | (g << 8) | b);
|
||||
}
|
||||
|
||||
inline uint32_t blendRgb(uint32_t c1, uint32_t c2, int weight2Pct) {
|
||||
int w2 = std::clamp(weight2Pct, 0, 100);
|
||||
int w1 = 100 - w2;
|
||||
int r = (((c1 >> 16) & 0xff) * w1 + ((c2 >> 16) & 0xff) * w2) / 100;
|
||||
int g = (((c1 >> 8) & 0xff) * w1 + ((c2 >> 8) & 0xff) * w2) / 100;
|
||||
int b = ((c1 & 0xff) * w1 + (c2 & 0xff) * w2) / 100;
|
||||
return static_cast<uint32_t>((std::clamp(r, 0, 255) << 16) |
|
||||
(std::clamp(g, 0, 255) << 8) |
|
||||
std::clamp(b, 0, 255));
|
||||
}
|
||||
|
||||
inline std::string getHomeDirPath() {
|
||||
const char* h = std::getenv("HOME");
|
||||
return (h && h[0]) ? h : "/home/user";
|
||||
}
|
||||
|
||||
inline std::string getXresourcesPath() {
|
||||
return getHomeDirPath() + "/.Xresources";
|
||||
}
|
||||
|
||||
inline std::string getThemesDirPath() {
|
||||
return getHomeDirPath() + "/.xthemes";
|
||||
}
|
||||
|
||||
struct ThemeSpec {
|
||||
std::string name = "Default";
|
||||
std::string fontFace = "Fira Code";
|
||||
std::string iconFontFace = "DejaVuSansM Nerd Font";
|
||||
int baseFontSize = 15;
|
||||
|
||||
// Core colors (24-bit RGB)
|
||||
uint32_t bgMain = 0x222222;
|
||||
uint32_t bgBar = 0x323232;
|
||||
uint32_t bgHeader = 0x1c1c1c;
|
||||
uint32_t bgFooter = 0x181818;
|
||||
uint32_t bgOverlay = 0x161616;
|
||||
uint32_t bgAltEven = 0x282828;
|
||||
uint32_t bgAltOdd = 0x2e2e2e;
|
||||
uint32_t bgInput = 0x1a1a1a;
|
||||
uint32_t bgInputFocus = 0x1b3b4b;
|
||||
uint32_t bgButton = 0x2a2a2a;
|
||||
uint32_t bgSearch = 0x383838;
|
||||
uint32_t bgBubbleIn = 0x262d32;
|
||||
uint32_t bgBubbleOut = 0x1f2d38;
|
||||
uint32_t bgToast = 0x102535;
|
||||
|
||||
uint32_t fgMain = 0xdddddd;
|
||||
uint32_t fgBright = 0xffffff;
|
||||
uint32_t fgMuted = 0x999999;
|
||||
|
||||
uint32_t selBg = 0x005577;
|
||||
uint32_t selFg = 0xffffff;
|
||||
uint32_t accent = 0xf4d03f;
|
||||
uint32_t accentAlt = 0x0088bb;
|
||||
uint32_t highlight = 0xffc978;
|
||||
uint32_t border = 0x444444;
|
||||
uint32_t divider = 0x666666;
|
||||
|
||||
uint32_t success = 0x2ecc71;
|
||||
uint32_t info = 0x5dade2;
|
||||
uint32_t warning = 0xe67e22;
|
||||
uint32_t danger = 0xe74c3c;
|
||||
uint32_t dangerBg = 0x922b21;
|
||||
|
||||
int sizeSmall() const {
|
||||
return std::max(8, static_cast<int>(std::lround(baseFontSize * 12.0 / 15.0)));
|
||||
}
|
||||
int sizeMain() const {
|
||||
return std::clamp(baseFontSize, 10, 24);
|
||||
}
|
||||
int sizeIcon() const {
|
||||
return std::max(10, static_cast<int>(std::lround(baseFontSize * 16.0 / 15.0)));
|
||||
}
|
||||
int sizeLarge() const {
|
||||
return std::max(11, static_cast<int>(std::lround(baseFontSize * 18.0 / 15.0)));
|
||||
}
|
||||
|
||||
std::string pangoMainFontStr() const {
|
||||
return fontFace + ", " + iconFontFace + " " + std::to_string(sizeMain()) + "px";
|
||||
}
|
||||
std::string pangoSmallFontStr() const {
|
||||
return fontFace + ", " + iconFontFace + " " + std::to_string(sizeSmall()) + "px";
|
||||
}
|
||||
std::string pangoIconFontStr() const {
|
||||
return iconFontFace + ", " + fontFace + " " + std::to_string(sizeIcon()) + "px";
|
||||
}
|
||||
std::string pangoLargeBoldFontStr() const {
|
||||
return fontFace + ", " + iconFontFace + " Bold " + std::to_string(sizeLarge()) + "px";
|
||||
}
|
||||
std::string xftFontStr(int pxSize) const {
|
||||
return fontFace + ":pixelsize=" + std::to_string(pxSize) +
|
||||
":style=Regular:antialias=true:autohint=true";
|
||||
}
|
||||
std::string xftIconFontStr(int pxSize) const {
|
||||
return iconFontFace + ":pixelsize=" + std::to_string(pxSize) +
|
||||
":style=Regular:antialias=true:autohint=true";
|
||||
}
|
||||
|
||||
void recomputeDerivedFromCore() {
|
||||
int luma = (((bgMain >> 16) & 0xff) * 299 +
|
||||
((bgMain >> 8) & 0xff) * 587 +
|
||||
(bgMain & 0xff) * 114) / 1000;
|
||||
int dir = (luma > 140) ? -1 : 1;
|
||||
|
||||
bgBar = adjustRgb(bgMain, dir * 16);
|
||||
bgHeader = adjustRgb(bgMain, -dir * 6);
|
||||
bgFooter = adjustRgb(bgMain, -dir * 10);
|
||||
bgOverlay = adjustRgb(bgMain, -dir * 12);
|
||||
bgAltEven = adjustRgb(bgMain, dir * 6);
|
||||
bgAltOdd = adjustRgb(bgMain, dir * 12);
|
||||
bgInput = adjustRgb(bgMain, -dir * 8);
|
||||
bgInputFocus = blendRgb(bgMain, selBg, 40);
|
||||
bgButton = adjustRgb(bgMain, dir * 8);
|
||||
bgSearch = adjustRgb(bgMain, dir * 22);
|
||||
bgBubbleIn = blendRgb(bgMain, success, 14);
|
||||
bgBubbleOut = blendRgb(bgMain, selBg, 26);
|
||||
bgToast = blendRgb(bgOverlay, selBg, 35);
|
||||
|
||||
int fgLuma = (((fgMain >> 16) & 0xff) * 299 +
|
||||
((fgMain >> 8) & 0xff) * 587 +
|
||||
(fgMain & 0xff) * 114) / 1000;
|
||||
fgBright = (fgLuma > 128) ? 0xffffff : 0x000000;
|
||||
fgMuted = blendRgb(fgMain, bgMain, 45);
|
||||
|
||||
accentAlt = adjustRgb(selBg, 48);
|
||||
highlight = blendRgb(accent, 0xffffff, 30);
|
||||
border = adjustRgb(bgMain, dir * 34);
|
||||
divider = adjustRgb(bgMain, dir * 68);
|
||||
}
|
||||
|
||||
void parseFromMap(const std::unordered_map<std::string, std::string>& kv) {
|
||||
auto getStr = [&](const char* k1, const char* k2 = nullptr,
|
||||
const char* k3 = nullptr) -> std::string {
|
||||
auto it = kv.find(k1);
|
||||
if (it != kv.end() && !it->second.empty()) return it->second;
|
||||
if (k2) {
|
||||
it = kv.find(k2);
|
||||
if (it != kv.end() && !it->second.empty()) return it->second;
|
||||
}
|
||||
if (k3) {
|
||||
it = kv.find(k3);
|
||||
if (it != kv.end() && !it->second.empty()) return it->second;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
std::string nm = getStr("theme.name", "*themeName");
|
||||
if (!nm.empty()) name = nm;
|
||||
|
||||
std::string ff = getStr("theme.fontFace", "*fontFace");
|
||||
if (!ff.empty()) {
|
||||
fontFace = ff;
|
||||
} else {
|
||||
std::string dwmFont = getStr("dwm.font");
|
||||
if (!dwmFont.empty()) {
|
||||
size_t comma = dwmFont.find(',');
|
||||
if (comma != std::string::npos) {
|
||||
fontFace = themeTrim(dwmFont.substr(0, comma));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string iff = getStr("theme.iconFontFace", "*iconFontFace");
|
||||
if (!iff.empty()) iconFontFace = iff;
|
||||
|
||||
std::string bfs = getStr("theme.baseFontSize", "*baseFontSize");
|
||||
if (!bfs.empty()) {
|
||||
int sz = std::atoi(bfs.c_str());
|
||||
if (sz >= 10 && sz <= 24) baseFontSize = sz;
|
||||
} else {
|
||||
std::string dwmFont = getStr("dwm.font");
|
||||
size_t pxPos = dwmFont.rfind("px");
|
||||
if (pxPos != std::string::npos && pxPos > 0) {
|
||||
size_t sp = pxPos;
|
||||
while (sp > 0 && std::isdigit(static_cast<unsigned char>(dwmFont[sp - 1]))) {
|
||||
--sp;
|
||||
}
|
||||
if (sp < pxPos) {
|
||||
int sz = std::atoi(dwmFont.substr(sp, pxPos - sp).c_str());
|
||||
if (sz >= 10 && sz <= 24) baseFontSize = sz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto getCol = [&](uint32_t* dst, const char* k1, const char* k2 = nullptr,
|
||||
const char* k3 = nullptr) -> bool {
|
||||
std::string s = getStr(k1, k2, k3);
|
||||
if (s.empty()) return false;
|
||||
uint32_t v = 0;
|
||||
if (parseHexRgb(s, &v)) {
|
||||
*dst = v;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool hasCoreBg = getCol(&bgMain, "theme.bgMain", "*background", "dmenu.background");
|
||||
bool hasCoreFg = getCol(&fgMain, "theme.fgMain", "*foreground", "dwm.normfgcolor");
|
||||
bool hasSelBg = getCol(&selBg, "theme.selBg", "dmenu.selbackground", "dwm.selbgcolor");
|
||||
bool hasSelFg = getCol(&selFg, "theme.selFg", "dmenu.selforeground", "dwm.selfgcolor");
|
||||
bool hasAccent = getCol(&accent, "theme.accent", "*accentColor", "st.cursorColor");
|
||||
(void)hasCoreBg;
|
||||
(void)hasCoreFg;
|
||||
(void)hasSelBg;
|
||||
(void)hasSelFg;
|
||||
(void)hasAccent;
|
||||
|
||||
// Derive defaults from core colors first, then override with any explicit tokens
|
||||
recomputeDerivedFromCore();
|
||||
|
||||
getCol(&bgBar, "theme.bgBar", "dwm.background", "dwm.normbgcolor");
|
||||
getCol(&bgHeader, "theme.bgHeader", "dmenu.background", "st.background");
|
||||
getCol(&bgFooter, "theme.bgFooter");
|
||||
getCol(&bgOverlay, "theme.bgOverlay");
|
||||
getCol(&bgAltEven, "theme.bgAltEven");
|
||||
getCol(&bgAltOdd, "theme.bgAltOdd");
|
||||
getCol(&bgInput, "theme.bgInput");
|
||||
getCol(&bgInputFocus, "theme.bgInputFocus");
|
||||
getCol(&bgButton, "theme.bgButton");
|
||||
getCol(&bgSearch, "theme.bgSearch", "dmenu.outbackground");
|
||||
getCol(&bgBubbleIn, "theme.bgBubbleIn");
|
||||
getCol(&bgBubbleOut, "theme.bgBubbleOut");
|
||||
getCol(&bgToast, "theme.bgToast");
|
||||
|
||||
getCol(&fgBright, "theme.fgBright", "dmenu.selforeground");
|
||||
getCol(&fgMuted, "theme.fgMuted");
|
||||
|
||||
getCol(&accentAlt, "theme.accentAlt");
|
||||
getCol(&highlight, "theme.highlight", "dmenu.highlight");
|
||||
getCol(&border, "theme.border", "dwm.border", "dwm.normbordercolor");
|
||||
getCol(÷r, "theme.divider");
|
||||
|
||||
getCol(&success, "theme.success");
|
||||
getCol(&info, "theme.info");
|
||||
getCol(&warning, "theme.warning");
|
||||
getCol(&danger, "theme.danger");
|
||||
getCol(&dangerBg, "theme.dangerBg");
|
||||
}
|
||||
|
||||
static void parseLinesIntoMap(const std::string& content,
|
||||
std::unordered_map<std::string, std::string>& kv) {
|
||||
std::istringstream ss(content);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
std::string t = themeTrim(line);
|
||||
if (t.empty() || t[0] == '!' || t[0] == '#') continue;
|
||||
size_t colon = t.find(':');
|
||||
if (colon == std::string::npos) continue;
|
||||
std::string key = themeTrim(t.substr(0, colon));
|
||||
std::string val = themeTrim(t.substr(colon + 1));
|
||||
if (!key.empty() && !val.empty()) {
|
||||
kv[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool loadFromFile(const std::string& path) {
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) return false;
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
std::unordered_map<std::string, std::string> kv;
|
||||
parseLinesIntoMap(ss.str(), kv);
|
||||
parseFromMap(kv);
|
||||
return true;
|
||||
}
|
||||
|
||||
void loadActive(Display* dpy, int screen) {
|
||||
std::unordered_map<std::string, std::string> kv;
|
||||
if (dpy) {
|
||||
Atom actualType = None;
|
||||
int actualFormat = 0;
|
||||
unsigned long nitems = 0, bytesAfter = 0;
|
||||
unsigned char* prop = nullptr;
|
||||
if (XGetWindowProperty(dpy, RootWindow(dpy, screen), XA_RESOURCE_MANAGER,
|
||||
0L, 65536L, False, XA_STRING, &actualType,
|
||||
&actualFormat, &nitems, &bytesAfter,
|
||||
&prop) == Success &&
|
||||
prop) {
|
||||
if (actualType == XA_STRING && actualFormat == 8 && nitems > 0) {
|
||||
std::string resStr(reinterpret_cast<char*>(prop), nitems);
|
||||
parseLinesIntoMap(resStr, kv);
|
||||
}
|
||||
XFree(prop);
|
||||
}
|
||||
}
|
||||
// Also read ~/.Xresources so direct file edits always take effect
|
||||
std::ifstream in(getXresourcesPath());
|
||||
if (in.is_open()) {
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
parseLinesIntoMap(ss.str(), kv);
|
||||
}
|
||||
if (!kv.empty()) {
|
||||
parseFromMap(kv);
|
||||
}
|
||||
}
|
||||
|
||||
std::string serializeXresources() const {
|
||||
std::ostringstream out;
|
||||
out << "! Space to setup configversion\n";
|
||||
out << "! configversion: 7bff124b2de16292fe6e7a7e5d3322ae\n";
|
||||
out << "! Bananarchy Unified Theme: " << name << "\n\n";
|
||||
|
||||
out << "theme.name: " << name << "\n";
|
||||
out << "theme.fontFace: " << fontFace << "\n";
|
||||
out << "theme.iconFontFace: " << iconFontFace << "\n";
|
||||
out << "theme.baseFontSize: " << sizeMain() << "\n\n";
|
||||
|
||||
out << "theme.bgMain: " << formatHexRgb(bgMain) << "\n";
|
||||
out << "theme.bgBar: " << formatHexRgb(bgBar) << "\n";
|
||||
out << "theme.bgHeader: " << formatHexRgb(bgHeader) << "\n";
|
||||
out << "theme.bgFooter: " << formatHexRgb(bgFooter) << "\n";
|
||||
out << "theme.bgOverlay: " << formatHexRgb(bgOverlay) << "\n";
|
||||
out << "theme.bgAltEven: " << formatHexRgb(bgAltEven) << "\n";
|
||||
out << "theme.bgAltOdd: " << formatHexRgb(bgAltOdd) << "\n";
|
||||
out << "theme.bgInput: " << formatHexRgb(bgInput) << "\n";
|
||||
out << "theme.bgInputFocus: " << formatHexRgb(bgInputFocus) << "\n";
|
||||
out << "theme.bgButton: " << formatHexRgb(bgButton) << "\n";
|
||||
out << "theme.bgSearch: " << formatHexRgb(bgSearch) << "\n";
|
||||
out << "theme.bgBubbleIn: " << formatHexRgb(bgBubbleIn) << "\n";
|
||||
out << "theme.bgBubbleOut: " << formatHexRgb(bgBubbleOut) << "\n";
|
||||
out << "theme.bgToast: " << formatHexRgb(bgToast) << "\n\n";
|
||||
|
||||
out << "theme.fgMain: " << formatHexRgb(fgMain) << "\n";
|
||||
out << "theme.fgBright: " << formatHexRgb(fgBright) << "\n";
|
||||
out << "theme.fgMuted: " << formatHexRgb(fgMuted) << "\n\n";
|
||||
|
||||
out << "theme.selBg: " << formatHexRgb(selBg) << "\n";
|
||||
out << "theme.selFg: " << formatHexRgb(selFg) << "\n";
|
||||
out << "theme.accent: " << formatHexRgb(accent) << "\n";
|
||||
out << "theme.accentAlt: " << formatHexRgb(accentAlt) << "\n";
|
||||
out << "theme.highlight: " << formatHexRgb(highlight) << "\n";
|
||||
out << "theme.border: " << formatHexRgb(border) << "\n";
|
||||
out << "theme.divider: " << formatHexRgb(divider) << "\n\n";
|
||||
|
||||
out << "theme.success: " << formatHexRgb(success) << "\n";
|
||||
out << "theme.info: " << formatHexRgb(info) << "\n";
|
||||
out << "theme.warning: " << formatHexRgb(warning) << "\n";
|
||||
out << "theme.danger: " << formatHexRgb(danger) << "\n";
|
||||
out << "theme.dangerBg: " << formatHexRgb(dangerBg) << "\n\n";
|
||||
|
||||
out << "dmenu.font: " << xftFontStr(sizeLarge()) << "\n";
|
||||
out << "dmenu.background: " << formatHexRgb(bgHeader) << "\n";
|
||||
out << "dmenu.foreground: " << formatHexRgb(fgBright) << "\n";
|
||||
out << "dmenu.selbackground: " << formatHexRgb(selBg) << "\n";
|
||||
out << "dmenu.selforeground: " << formatHexRgb(selFg) << "\n";
|
||||
out << "dmenu.outbackground: " << formatHexRgb(bgSearch) << "\n";
|
||||
out << "dmenu.outforeground: " << formatHexRgb(fgBright) << "\n";
|
||||
out << "dmenu.highlight: " << formatHexRgb(highlight) << "\n\n";
|
||||
|
||||
out << "dwm.font: " << pangoMainFontStr() << "\n";
|
||||
out << "dwm.background: " << formatHexRgb(bgBar) << "\n";
|
||||
out << "dwm.foreground: " << formatHexRgb(fgMain) << "\n";
|
||||
out << "dwm.border: " << formatHexRgb(border) << "\n";
|
||||
out << "dwm.selbackground: " << formatHexRgb(selBg) << "\n";
|
||||
out << "dwm.selforeground: " << formatHexRgb(selFg) << "\n";
|
||||
out << "dwm.selborder: " << formatHexRgb(selBg) << "\n";
|
||||
out << "dwm.normbgcolor: " << formatHexRgb(bgBar) << "\n";
|
||||
out << "dwm.normbordercolor: " << formatHexRgb(border) << "\n";
|
||||
out << "dwm.normfgcolor: " << formatHexRgb(fgMain) << "\n";
|
||||
out << "dwm.selbgcolor: " << formatHexRgb(selBg) << "\n";
|
||||
out << "dwm.selbordercolor: " << formatHexRgb(selBg) << "\n";
|
||||
out << "dwm.selfgcolor: " << formatHexRgb(selFg) << "\n\n";
|
||||
|
||||
out << "st.font: " << xftFontStr(sizeLarge()) << "\n";
|
||||
out << "st.termname: xterm-256color\n";
|
||||
out << "st.background: " << formatHexRgb(bgHeader) << "\n";
|
||||
out << "st.foreground: " << formatHexRgb(fgMain) << "\n";
|
||||
out << "st.cursorColor: " << formatHexRgb(accent) << "\n\n";
|
||||
|
||||
out << "svkbd.font: " << fontFace << ":pixelsize=" << sizeSmall() << "\n";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bool saveToFile(const std::string& path) const {
|
||||
std::ofstream out(path, std::ios::trunc);
|
||||
if (!out.is_open()) return false;
|
||||
out << serializeXresources();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
inline ThemeSpec loadThemeSpec(Display* dpy = nullptr, int screen = 0) {
|
||||
ThemeSpec spec;
|
||||
spec.loadActive(dpy, screen);
|
||||
return spec;
|
||||
}
|
||||
|
||||
struct PluginTheme {
|
||||
ThemeSpec spec;
|
||||
time_t lastXresMtime = -1;
|
||||
off_t lastXresSize = -1;
|
||||
bool colorsAllocated = false;
|
||||
|
||||
Display* dpy_ = nullptr;
|
||||
int screen_ = 0;
|
||||
Visual* visual_ = nullptr;
|
||||
Colormap cmap_ = 0;
|
||||
Window win_ = None;
|
||||
PangoContext* pangoCtx = nullptr;
|
||||
PangoLayout* layoutMain = nullptr;
|
||||
PangoLayout* layoutSmall = nullptr;
|
||||
PangoLayout* layoutIcon = nullptr;
|
||||
PangoLayout* layoutNum = nullptr;
|
||||
|
||||
XftColor colWhite{};
|
||||
XftColor colLight{};
|
||||
XftColor colMuted{};
|
||||
XftColor colYellow{};
|
||||
XftColor colGreen{};
|
||||
XftColor colBlue{};
|
||||
XftColor colOrange{};
|
||||
XftColor colRed{};
|
||||
|
||||
void allocXft(Display* dpy, Visual* visual, Colormap cmap, uint32_t rgb,
|
||||
XftColor* dst) {
|
||||
std::string hex = formatHexRgb(rgb);
|
||||
XftColorAllocName(dpy, visual, cmap, hex.c_str(), dst);
|
||||
}
|
||||
|
||||
void init(Display* dpy, int screen, Visual* visual, Colormap cmap,
|
||||
Window win = None) {
|
||||
dpy_ = dpy;
|
||||
screen_ = screen;
|
||||
visual_ = visual;
|
||||
cmap_ = cmap;
|
||||
win_ = win;
|
||||
if (!pangoCtx && dpy) {
|
||||
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
|
||||
pangoCtx = pango_font_map_create_context(fontMap);
|
||||
layoutMain = pango_layout_new(pangoCtx);
|
||||
layoutSmall = pango_layout_new(pangoCtx);
|
||||
layoutIcon = pango_layout_new(pangoCtx);
|
||||
layoutNum = pango_layout_new(pangoCtx);
|
||||
}
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (dpy_ && visual_) {
|
||||
load(dpy_, screen_, visual_, cmap_, layoutMain, layoutSmall, layoutIcon,
|
||||
layoutNum, win_);
|
||||
}
|
||||
}
|
||||
|
||||
void load(Display* dpy, int screen, Visual* visual, Colormap cmap,
|
||||
PangoLayout* lMain = nullptr,
|
||||
PangoLayout* lSmall = nullptr,
|
||||
PangoLayout* lIcon = nullptr,
|
||||
PangoLayout* lNum = nullptr,
|
||||
Window win = None) {
|
||||
dpy_ = dpy;
|
||||
screen_ = screen;
|
||||
visual_ = visual;
|
||||
cmap_ = cmap;
|
||||
if (win != None) win_ = win;
|
||||
if (lMain) layoutMain = lMain;
|
||||
if (lSmall) layoutSmall = lSmall;
|
||||
if (lIcon) layoutIcon = lIcon;
|
||||
if (lNum) layoutNum = lNum;
|
||||
|
||||
struct stat st {};
|
||||
if (::stat(getXresourcesPath().c_str(), &st) == 0) {
|
||||
lastXresMtime = st.st_mtim.tv_sec;
|
||||
lastXresSize = st.st_size;
|
||||
}
|
||||
|
||||
spec = ThemeSpec{};
|
||||
spec.loadActive(dpy, screen);
|
||||
|
||||
if (colorsAllocated) {
|
||||
XftColorFree(dpy, visual, cmap, &colWhite);
|
||||
XftColorFree(dpy, visual, cmap, &colLight);
|
||||
XftColorFree(dpy, visual, cmap, &colMuted);
|
||||
XftColorFree(dpy, visual, cmap, &colYellow);
|
||||
XftColorFree(dpy, visual, cmap, &colGreen);
|
||||
XftColorFree(dpy, visual, cmap, &colBlue);
|
||||
XftColorFree(dpy, visual, cmap, &colOrange);
|
||||
XftColorFree(dpy, visual, cmap, &colRed);
|
||||
}
|
||||
|
||||
allocXft(dpy, visual, cmap, spec.fgBright, &colWhite);
|
||||
allocXft(dpy, visual, cmap, spec.fgMain, &colLight);
|
||||
allocXft(dpy, visual, cmap, spec.fgMuted, &colMuted);
|
||||
allocXft(dpy, visual, cmap, spec.accent, &colYellow);
|
||||
allocXft(dpy, visual, cmap, spec.success, &colGreen);
|
||||
allocXft(dpy, visual, cmap, spec.info, &colBlue);
|
||||
allocXft(dpy, visual, cmap, spec.warning, &colOrange);
|
||||
allocXft(dpy, visual, cmap, spec.danger, &colRed);
|
||||
colorsAllocated = true;
|
||||
|
||||
if (layoutMain) {
|
||||
PangoFontDescription* d =
|
||||
pango_font_description_from_string(spec.pangoMainFontStr().c_str());
|
||||
pango_layout_set_font_description(layoutMain, d);
|
||||
pango_font_description_free(d);
|
||||
}
|
||||
if (layoutSmall) {
|
||||
PangoFontDescription* d =
|
||||
pango_font_description_from_string(spec.pangoSmallFontStr().c_str());
|
||||
pango_layout_set_font_description(layoutSmall, d);
|
||||
pango_font_description_free(d);
|
||||
}
|
||||
if (layoutIcon) {
|
||||
PangoFontDescription* d =
|
||||
pango_font_description_from_string(spec.pangoIconFontStr().c_str());
|
||||
pango_layout_set_font_description(layoutIcon, d);
|
||||
pango_font_description_free(d);
|
||||
}
|
||||
if (layoutNum) {
|
||||
PangoFontDescription* d = pango_font_description_from_string(
|
||||
spec.pangoLargeBoldFontStr().c_str());
|
||||
pango_layout_set_font_description(layoutNum, d);
|
||||
pango_font_description_free(d);
|
||||
}
|
||||
if (win_ != None) {
|
||||
XSetWindowBackground(dpy, win_, spec.bgMain);
|
||||
XSetWindowBorder(dpy, win_, spec.selBg);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkAndReload() {
|
||||
if (!dpy_ || !visual_) return false;
|
||||
return checkAndReload(dpy_, screen_, visual_, cmap_, layoutMain,
|
||||
layoutSmall, layoutIcon, layoutNum, win_);
|
||||
}
|
||||
|
||||
bool checkAndReload(Display* dpy, int screen, Visual* visual, Colormap cmap,
|
||||
PangoLayout* lMain = nullptr,
|
||||
PangoLayout* lSmall = nullptr,
|
||||
PangoLayout* lIcon = nullptr,
|
||||
PangoLayout* lNum = nullptr,
|
||||
Window win = None) {
|
||||
struct stat st {};
|
||||
if (::stat(getXresourcesPath().c_str(), &st) == 0) {
|
||||
if (st.st_mtim.tv_sec != lastXresMtime || st.st_size != lastXresSize) {
|
||||
load(dpy, screen, visual, cmap, lMain, lSmall, lIcon, lNum, win);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bananarchy
|
||||
|
||||
#endif // DWM_PLUGINS_THEME_H
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user