Add unified Xresources theme engine, Default.xth export, and xthemes manager plugin

This commit is contained in:
Luxferre
2026-10-21 20:36:27 +03:00
parent f615c6ddbe
commit a31595070c
60 changed files with 3692 additions and 419 deletions
+32 -40
View File
@@ -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);
+48 -53
View File
@@ -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("");
+50 -60
View File
@@ -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);
+27 -42
View File
@@ -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);
+7 -1
View File
@@ -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;
}
+49 -56
View File
@@ -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("");
+30
View File
@@ -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]) {
+64 -66
View File
@@ -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("");
+636
View File
@@ -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(&divider, "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
+194
View File
@@ -107,6 +107,10 @@ static Clr *scheme[SchemeLast];
static char *colors[SchemeLast][2];
static const char *fonts[3] = { NULL, NULL, NULL };
static char xres_font_main[256] = "";
static char xres_font_icon[256] = "";
static time_t last_xres_mtime = 0;
static long last_xres_nsec = 0;
#include "config.h"
@@ -151,11 +155,14 @@ static const char *ICON_ZMI = "\xf3\xb0\x9b\xad";
static const char *ICON_ZMO = "\xf3\xb0\x9b\xac";
static const char *ICON_A2X = "\xe2\x87\x84";
static const char *ICON_KBD = "\xef\x84\x9c";
static const char *ICON_THM = "\xf3\xb0\x8f\x98";
static void drawmenu(void);
static void show_menu(void);
static void hide_menu(void);
static void open_builtin_menu(const char *menu_name, int keep_idx);
static void load_dmenu_xresources(void);
static void reload_dmenu_theme(void);
static char *
cistrstr(const char *s, const char *sub)
@@ -1711,6 +1718,9 @@ open_builtin_menu(const char *menu_name, int keep_idx)
snprintf(label, sizeof(label), "%s Camera", ICON_CAM);
add_menu_item(label, "@plugin:camera", 0);
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
char bl_buf[32] = "";
if (read_first_line("/sys/class/leds/white:torch/brightness", bl_buf, sizeof(bl_buf)) ||
read_first_line("/sys/class/leds/white:flash/brightness", bl_buf, sizeof(bl_buf))) {
@@ -1742,6 +1752,8 @@ open_builtin_menu(const char *menu_name, int keep_idx)
} else if (strcmp(menu_name, "applications") == 0) {
reset_session_defaults(1, "Apps");
append_active_call_entries();
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
load_cache_or_hook("sxmo_apps.cache", "sxmo_hook_apps.sh");
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
add_menu_item(label, "@menu:sys", 1);
@@ -1776,6 +1788,8 @@ open_builtin_menu(const char *menu_name, int keep_idx)
home = "/home/user";
char pbuf[256];
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
snprintf(label, sizeof(label), "%s Brightness", ICON_ARU);
add_menu_item(label, "@toggle:bright_up", 1);
snprintf(label, sizeof(label), "%s Brightness", ICON_ARD);
@@ -1942,6 +1956,7 @@ close_other_active_plugins(void)
{ "xsms.pid", "/tmp/dwm_plugin_sms.fifo" },
{ "xfiles.pid", "/tmp/dwm_plugin_files.fifo" },
{ "xcamera.pid", "/tmp/dwm_plugin_camera.fifo" },
{ "xthemes.pid", "/tmp/dwm_plugin_themes.fifo" },
};
const char *rdir = getenv("XDG_RUNTIME_DIR");
const char *base = (rdir && rdir[0]) ? rdir : "/run/user/10000";
@@ -1960,12 +1975,185 @@ close_other_active_plugins(void)
}
}
static const char *
dmenu_xrm_lookup(XrmDatabase xdb, const char *k1, const char *k2, const char *k3)
{
char *type = NULL;
XrmValue xval;
if (k1 && XrmGetResource(xdb, k1, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k2 && XrmGetResource(xdb, k2, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k3 && XrmGetResource(xdb, k3, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
return NULL;
}
static void
dmenu_set_color(int scm, int col, const char *val)
{
if (!val || !val[0])
return;
free(colors[scm][col]);
colors[scm][col] = strdup(val);
}
static void
load_dmenu_xresources(void)
{
XrmInitialize();
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
struct stat st;
if (stat(path, &st) == 0) {
last_xres_mtime = st.st_mtim.tv_sec;
last_xres_nsec = st.st_mtim.tv_nsec;
}
XrmDatabase xdb = XrmGetFileDatabase(path);
Atom actual_type = None;
int actual_format = 0;
unsigned long nitems = 0, bytes_after = 0;
unsigned char *prop = NULL;
if (!xdb && XGetWindowProperty(dpy, root, XA_RESOURCE_MANAGER, 0L, 65536L, False,
XA_STRING, &actual_type, &actual_format,
&nitems, &bytes_after, &prop) == Success && prop) {
if (actual_type == XA_STRING && actual_format == 8 && nitems > 0)
xdb = XrmGetStringDatabase((const char *)prop);
XFree(prop);
}
if (!xdb) {
char *xrm = XResourceManagerString(dpy);
if (xrm)
xdb = XrmGetStringDatabase(xrm);
}
if (xdb) {
const char *ff = dmenu_xrm_lookup(xdb, "theme.fontFace", NULL, NULL);
const char *icf = dmenu_xrm_lookup(xdb, "theme.iconFontFace", NULL, NULL);
const char *bsz = dmenu_xrm_lookup(xdb, "theme.baseFontSize", NULL, NULL);
const char *dmf = dmenu_xrm_lookup(xdb, "dmenu.font", NULL, NULL);
if (ff || bsz) {
int base_sz = bsz ? atoi(bsz) : 15;
if (base_sz < 8 || base_sz > 32)
base_sz = 15;
int large_sz = (base_sz * 18 + 7) / 15;
snprintf(xres_font_main, sizeof(xres_font_main),
"%s:pixelsize=%d:style=Regular:antialias=true:autohint=true",
ff ? ff : "Fira Code", large_sz);
snprintf(xres_font_icon, sizeof(xres_font_icon),
"%s:pixelsize=%d:style=Regular:antialias=true:autohint=true",
icf ? icf : "DejaVuSansM Nerd Font", large_sz);
fonts[0] = xres_font_main;
fonts[1] = xres_font_icon;
} else if (dmf) {
snprintf(xres_font_main, sizeof(xres_font_main), "%s", dmf);
fonts[0] = xres_font_main;
fonts[1] = defaultfonts[1];
}
dmenu_set_color(SchemeNorm, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgHeader", "theme.bgMain", "dmenu.background"));
dmenu_set_color(SchemeNorm, ColFg,
dmenu_xrm_lookup(xdb, "theme.fgBright", "theme.fgMain", "dmenu.foreground"));
dmenu_set_color(SchemeSel, ColBg,
dmenu_xrm_lookup(xdb, "theme.selBg", "dmenu.selbackground", NULL));
dmenu_set_color(SchemeSel, ColFg,
dmenu_xrm_lookup(xdb, "theme.selFg", "dmenu.selforeground", NULL));
dmenu_set_color(SchemeOut, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgSearch", "dmenu.outbackground", "theme.bgInputFocus"));
dmenu_set_color(SchemeOut, ColFg,
dmenu_xrm_lookup(xdb, "theme.fgBright", "dmenu.outforeground", "theme.fgMain"));
dmenu_set_color(SchemeNormHighlight, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgHeader", "theme.bgMain", "dmenu.background"));
dmenu_set_color(SchemeNormHighlight, ColFg,
dmenu_xrm_lookup(xdb, "theme.highlight", "theme.accent", "dmenu.normhlforeground"));
dmenu_set_color(SchemeSelHighlight, ColBg,
dmenu_xrm_lookup(xdb, "theme.selBg", "dmenu.selbackground", NULL));
dmenu_set_color(SchemeSelHighlight, ColFg,
dmenu_xrm_lookup(xdb, "theme.highlight", "theme.accent", "dmenu.selhlforeground"));
XrmDestroyDatabase(xdb);
}
}
static void
reload_dmenu_theme(void)
{
int i;
load_dmenu_xresources();
for (i = 0; i < SchemeLast; ++i) {
if (!colors[i][ColBg])
colors[i][ColBg] = strdup(defaultcolors[i][ColBg]);
if (!colors[i][ColFg])
colors[i][ColFg] = strdup(defaultcolors[i][ColFg]);
}
if (drw->fonts) {
drw_fontset_free(drw->fonts);
drw->fonts = NULL;
}
if (!fonts[0]) {
fonts[0] = defaultfonts[0];
fonts[1] = defaultfonts[1];
}
if (!drw_fontset_create(drw, fonts, 2))
drw_fontset_create(drw, defaultfonts, 2);
lrpad = drw->fonts->h / 2;
bh = drw->fonts->h + 2;
bh = MAX(bh, (int)lineheight);
default_lines = MAX(1, (avail_h_cached / (int)bh) - 1);
for (i = 0; i < SchemeLast; i++) {
free(scheme[i]);
scheme[i] = drw_scm_create(drw, (const char **)colors[i], 2);
}
for (i = 0; i < SchemeLast; ++i) {
free(colors[i][ColFg]);
free(colors[i][ColBg]);
colors[i][ColFg] = NULL;
colors[i][ColBg] = NULL;
}
if (win) {
XSetWindowBackground(dpy, win, scheme[SchemeNorm][ColBg].pixel);
XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
}
if (menu_active)
drawmenu();
}
static void
check_dmenu_xres_changed(void)
{
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
struct stat st;
if (stat(path, &st) == 0) {
if (st.st_mtim.tv_sec != last_xres_mtime || st.st_mtim.tv_nsec != last_xres_nsec)
reload_dmenu_theme();
}
}
static void
show_menu(void)
{
const int bar_h = 24;
int target_y;
check_dmenu_xres_changed();
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
if (lines > 0) {
lines = default_lines;
@@ -2242,6 +2430,8 @@ handle_fifo_commands(int fifo_fd)
if (strcmp(line, "CLOSE") == 0) {
if (menu_active)
cancel_session();
} else if (strcmp(line, "RELOAD_THEME") == 0) {
reload_dmenu_theme();
} else if (strcmp(line, "OPEN") == 0) {
if (active_client_fd >= 0)
finish_client_session(1, NULL);
@@ -2469,6 +2659,8 @@ setup(int map_immediately)
for (j = 0; j < SchemeLast; ++j) {
free(colors[j][ColFg]);
free(colors[j][ColBg]);
colors[j][ColFg] = NULL;
colors[j][ColBg] = NULL;
}
clip = XInternAtom(dpy, "CLIPBOARD", False);
@@ -2789,6 +2981,8 @@ dmenu_run_main(int argc, char *argv[])
die("could not get embedding window attributes: 0x%lx", parentwin);
drw = drw_create(dpy, screen, root, wa.width, wa.height);
load_dmenu_xresources();
if (!fonts[0]) {
fonts[0] = defaultfonts[0];
fonts[1] = defaultfonts[1];
+153 -35
View File
@@ -272,7 +272,8 @@ static void clienttagpush(const Arg *arg);
static void shiftview(const Arg *arg);
static void pushup(const Arg *arg);
static void pushdown(const Arg *arg);
static void readxresources();
static void readxresources(void);
static void reload_dwm_theme(void);
static void load_plugins(void);
static void unload_plugins(void);
@@ -320,6 +321,7 @@ static Window root, wmcheckwin;
/* Empty arrays to be filled from command line, Xresources, and defaults, in decreasing order of precedence */
static char *colors[4][3]; /* 4 schemes, 3 colors each */
static char active_font[256];
static xcb_connection_t *xcon;
@@ -2258,6 +2260,8 @@ propertynotify(XEvent *e)
if (ev->window == root) {
if (ev->atom == XA_WM_NAME)
updatestatus();
else if (ev->atom == XA_RESOURCE_MANAGER)
reload_dwm_theme();
else
drawbars();
} else if (ev->state == PropertyDelete)
@@ -2657,6 +2661,7 @@ setup(void)
root = RootWindow(dpy, screen);
drw = drw_create(dpy, screen, root, sw, sh);
snprintf(active_font, sizeof(active_font), "%s", font);
readxresources();
/* Apply defaults to font and colors*/
for (i = 0; i <= SchemeTabInactive; ++i){
@@ -2666,7 +2671,7 @@ setup(void)
}
}
if (!drw_font_create(drw, font))
if (!drw_font_create(drw, active_font))
die("no fonts could be loaded.");
lrpad = 6;
bh = MAX(drw->font->h + 4, 24);
@@ -2697,6 +2702,10 @@ setup(void)
for (j = 0; j <= SchemeTabInactive; ++j) {
free(colors[j][ColFg]);
free(colors[j][ColBg]);
free(colors[j][ColBorder]);
colors[j][ColFg] = NULL;
colors[j][ColBg] = NULL;
colors[j][ColBorder] = NULL;
}
/* init bars */
updatebars();
@@ -3429,46 +3438,155 @@ zoom(const Arg *arg)
}
static void
set_color_slot(int scm, int col, const char *val)
{
if (!val || !val[0])
return;
free(colors[scm][col]);
colors[scm][col] = strdup(val);
}
static const char *
xrm_lookup_any(XrmDatabase xdb, const char *k1, const char *k2, const char *k3)
{
char *type = NULL;
XrmValue xval;
if (k1 && XrmGetResource(xdb, k1, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k2 && XrmGetResource(xdb, k2, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k3 && XrmGetResource(xdb, k3, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
return NULL;
}
void
readxresources() {
XrmInitialize();
readxresources(void)
{
XrmInitialize();
char* xrm;
if ((xrm = XResourceManagerString(drw->dpy))) {
char *type;
XrmDatabase xdb = XrmGetStringDatabase(xrm);
XrmValue xval;
XrmDatabase xdb = NULL;
Atom actual_type = None;
int actual_format = 0;
unsigned long nitems = 0, bytes_after = 0;
unsigned char *prop = NULL;
if (XrmGetResource(xdb, "dwm.background", "*", &type, &xval) && !colors[SchemeNorm][ColBg] )
colors[SchemeNorm][ColBg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.foreground", "*", &type, &xval) && !colors[SchemeNorm][ColFg] )
colors[SchemeNorm][ColFg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.border", "*", &type, &xval) && !colors[SchemeNorm][ColBorder] )
colors[SchemeNorm][ColBorder] = strdup(xval.addr);
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
xdb = XrmGetFileDatabase(path);
if (XrmGetResource(xdb, "dwm.selbackground", "*", &type, &xval) && !colors[SchemeSel][ColBg] )
colors[SchemeSel][ColBg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.selforeground", "*", &type, &xval) && !colors[SchemeSel][ColFg] )
colors[SchemeSel][ColFg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.selborder", "*", &type, &xval) && !colors[SchemeSel][ColBorder] )
colors[SchemeSel][ColBorder] = strdup(xval.addr);
if (!xdb && XGetWindowProperty(dpy, root, XA_RESOURCE_MANAGER, 0L, 65536L, False,
XA_STRING, &actual_type, &actual_format,
&nitems, &bytes_after, &prop) == Success && prop) {
if (actual_type == XA_STRING && actual_format == 8 && nitems > 0)
xdb = XrmGetStringDatabase((const char *)prop);
XFree(prop);
}
if (!xdb) {
char *xrm = XResourceManagerString(drw->dpy);
if (xrm)
xdb = XrmGetStringDatabase(xrm);
}
if (XrmGetResource(xdb, "dwm.tabactivebackground", "*", &type, &xval) && !colors[SchemeTabActive][ColBg] )
colors[SchemeTabActive][ColBg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.tabactiveforeground", "*", &type, &xval) && !colors[SchemeTabActive][ColFg] )
colors[SchemeTabActive][ColFg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.tabactiveborder", "*", &type, &xval) && !colors[SchemeTabActive][ColBorder] )
colors[SchemeTabActive][ColBorder] = strdup(xval.addr);
if (xdb) {
const char *ff = xrm_lookup_any(xdb, "theme.fontFace", NULL, NULL);
const char *icf = xrm_lookup_any(xdb, "theme.iconFontFace", NULL, NULL);
const char *bsz = xrm_lookup_any(xdb, "theme.baseFontSize", NULL, NULL);
const char *dwmf = xrm_lookup_any(xdb, "dwm.font", NULL, NULL);
if (XrmGetResource(xdb, "dwm.tabinactivebackground", "*", &type, &xval) && !colors[SchemeTabInactive][ColBg] )
colors[SchemeTabInactive][ColBg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.tabinactiveforeground", "*", &type, &xval) && !colors[SchemeTabInactive][ColFg] )
colors[SchemeTabInactive][ColFg] = strdup(xval.addr);
if (XrmGetResource(xdb, "dwm.tabinactiveborder", "*", &type, &xval) && !colors[SchemeTabInactive][ColBorder] )
colors[SchemeTabInactive][ColBorder] = strdup(xval.addr);
if (ff || bsz) {
int sz = bsz ? atoi(bsz) : 15;
if (sz < 8 || sz > 32)
sz = 15;
snprintf(active_font, sizeof(active_font), "%s, %s %dpx",
ff ? ff : "Fira Code",
icf ? icf : "DejaVuSansM Nerd Font",
sz);
} else if (dwmf) {
snprintf(active_font, sizeof(active_font), "%s", dwmf);
} else {
snprintf(active_font, sizeof(active_font), "%s", font);
}
XrmDestroyDatabase(xdb);
}
set_color_slot(SchemeNorm, ColBg,
xrm_lookup_any(xdb, "theme.bgBar", "dwm.background", "dwm.normbgcolor"));
set_color_slot(SchemeNorm, ColFg,
xrm_lookup_any(xdb, "theme.fgMain", "dwm.foreground", "dwm.normfgcolor"));
set_color_slot(SchemeNorm, ColBorder,
xrm_lookup_any(xdb, "theme.border", "dwm.border", "dwm.normbordercolor"));
set_color_slot(SchemeSel, ColBg,
xrm_lookup_any(xdb, "theme.selBg", "dwm.selbackground", "dwm.selbgcolor"));
set_color_slot(SchemeSel, ColFg,
xrm_lookup_any(xdb, "theme.selFg", "dwm.selforeground", "dwm.selfgcolor"));
set_color_slot(SchemeSel, ColBorder,
xrm_lookup_any(xdb, "theme.accent", "dwm.selborder", "dwm.selbordercolor"));
set_color_slot(SchemeTabActive, ColBg,
xrm_lookup_any(xdb, "theme.selBg", "dwm.tabactivebackground", "dwm.selbgcolor"));
set_color_slot(SchemeTabActive, ColFg,
xrm_lookup_any(xdb, "theme.selFg", "dwm.tabactiveforeground", "dwm.selfgcolor"));
set_color_slot(SchemeTabActive, ColBorder,
xrm_lookup_any(xdb, "theme.accent", "dwm.tabactiveborder", "dwm.selbordercolor"));
set_color_slot(SchemeTabInactive, ColBg,
xrm_lookup_any(xdb, "theme.bgBar", "dwm.tabinactivebackground", "dwm.normbgcolor"));
set_color_slot(SchemeTabInactive, ColFg,
xrm_lookup_any(xdb, "theme.fgMain", "dwm.tabinactiveforeground", "dwm.normfgcolor"));
set_color_slot(SchemeTabInactive, ColBorder,
xrm_lookup_any(xdb, "theme.border", "dwm.tabinactiveborder", "dwm.normbordercolor"));
XrmDestroyDatabase(xdb);
}
}
static void
reload_dwm_theme(void)
{
int i, j;
Monitor *m;
Client *c;
readxresources();
for (i = 0; i <= SchemeTabInactive; ++i) {
for (j = 0; j <= ColBorder; ++j) {
if (!colors[i][j])
colors[i][j] = strdup(defaultcolors[i][j]);
}
}
if (drw->font) {
drw_font_free(drw->font);
drw->font = NULL;
}
if (!drw_font_create(drw, active_font))
drw_font_create(drw, font);
for (i = 0; i < LENGTH(colors); i++) {
free(scheme[i]);
scheme[i] = drw_scm_create(drw, (const char **)colors[i], 3);
}
for (j = 0; j <= SchemeTabInactive; ++j) {
free(colors[j][ColFg]);
free(colors[j][ColBg]);
free(colors[j][ColBorder]);
colors[j][ColFg] = NULL;
colors[j][ColBg] = NULL;
colors[j][ColBorder] = NULL;
}
for (m = mons; m; m = m->next) {
for (c = m->clients; c; c = c->next) {
if (c == m->sel)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
}
}
drawbars();
}
int
+7 -5
View File
@@ -118,7 +118,9 @@ static const char *colorname[] = {
[255] = 0,
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#161616",
"#dddddd",
"#f4d03f",
"#555555",
};
@@ -127,10 +129,10 @@ static const char *colorname[] = {
* Default colors (colorname index)
* foreground, background, cursor, reverse cursor
*/
unsigned int defaultfg = 7;
unsigned int defaultbg = 0;
static unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
unsigned int defaultfg = 257;
unsigned int defaultbg = 256;
static unsigned int defaultcs = 258;
static unsigned int defaultrcs = 259;
/*
* Default shape of cursor
+7 -5
View File
@@ -118,7 +118,9 @@ static const char *colorname[] = {
[255] = 0,
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#161616",
"#dddddd",
"#f4d03f",
"#555555",
};
@@ -127,10 +129,10 @@ static const char *colorname[] = {
* Default colors (colorname index)
* foreground, background, cursor, reverse cursor
*/
unsigned int defaultfg = 7;
unsigned int defaultbg = 0;
static unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
unsigned int defaultfg = 257;
unsigned int defaultbg = 256;
static unsigned int defaultcs = 258;
static unsigned int defaultrcs = 259;
/*
* Default shape of cursor
+84 -5
View File
@@ -1942,6 +1942,21 @@ resize(XEvent *e)
cresize(e->xconfigure.width, e->xconfigure.height);
}
static volatile sig_atomic_t st_reload_requested = 0;
static char st_theme_font[256] = "";
static char st_theme_bg[32] = "";
static char st_theme_fg[32] = "";
static char st_theme_cs[32] = "";
static void
st_sigusr1(int unused)
{
(void)unused;
st_reload_requested = 1;
}
void config_init(void);
void
run(void)
{
@@ -1970,8 +1985,19 @@ run(void)
ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
cresize(w, h);
signal(SIGUSR1, st_sigusr1);
for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
if (st_reload_requested) {
st_reload_requested = 0;
config_init();
xloadcols();
xunloadfonts();
xloadfonts(opt_font ? opt_font : font, 0);
cresize(win.w, win.h);
redraw();
XFlush(xw.dpy);
}
FD_ZERO(&rfd);
FD_SET(ttyfd, &rfd);
FD_SET(xfd, &rfd);
@@ -2084,18 +2110,71 @@ resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst)
void
config_init(void)
{
char *resm;
XrmDatabase db;
XrmDatabase db = NULL;
ResourcePref *p;
Atom actual_type = None;
int actual_format = 0;
unsigned long nitems = 0, bytes_after = 0;
unsigned char *prop = NULL;
char *type = NULL;
XrmValue ret;
XrmInitialize();
resm = XResourceManagerString(xw.dpy);
if (!resm)
{
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
db = XrmGetFileDatabase(path);
}
if (!db && XGetWindowProperty(xw.dpy, DefaultRootWindow(xw.dpy), XA_RESOURCE_MANAGER,
0L, 65536L, False, XA_STRING, &actual_type,
&actual_format, &nitems, &bytes_after, &prop) == Success && prop) {
if (actual_type == XA_STRING && actual_format == 8 && nitems > 0)
db = XrmGetStringDatabase((const char *)prop);
XFree(prop);
}
if (!db) {
char *resm = XResourceManagerString(xw.dpy);
if (resm)
db = XrmGetStringDatabase(resm);
}
if (!db)
return;
db = XrmGetStringDatabase(resm);
for (p = resources; p < resources + LEN(resources); p++)
resource_load(db, p->name, p->type, p->dst);
/* Also honor unified theme.* keys */
const char *ff = NULL, *bsz = NULL;
if (XrmGetResource(db, "theme.fontFace", "*", &type, &ret) && ret.addr && ret.addr[0])
ff = ret.addr;
if (XrmGetResource(db, "theme.baseFontSize", "*", &type, &ret) && ret.addr && ret.addr[0])
bsz = ret.addr;
if (ff || bsz) {
int base_sz = bsz ? atoi(bsz) : 15;
if (base_sz < 8 || base_sz > 32)
base_sz = 15;
int large_sz = (base_sz * 18 + 7) / 15;
snprintf(st_theme_font, sizeof(st_theme_font),
"%s:pixelsize=%d:style=Regular:antialias=true:autohint=true",
ff ? ff : "Fira Code", large_sz);
font = st_theme_font;
}
if (XrmGetResource(db, "theme.bgHeader", "*", &type, &ret) && ret.addr && ret.addr[0]) {
snprintf(st_theme_bg, sizeof(st_theme_bg), "%s", ret.addr);
colorname[256] = st_theme_bg;
} else if (XrmGetResource(db, "theme.bgMain", "*", &type, &ret) && ret.addr && ret.addr[0]) {
snprintf(st_theme_bg, sizeof(st_theme_bg), "%s", ret.addr);
colorname[256] = st_theme_bg;
}
if (XrmGetResource(db, "theme.fgMain", "*", &type, &ret) && ret.addr && ret.addr[0]) {
snprintf(st_theme_fg, sizeof(st_theme_fg), "%s", ret.addr);
colorname[257] = st_theme_fg;
}
if (XrmGetResource(db, "theme.accent", "*", &type, &ret) && ret.addr && ret.addr[0]) {
snprintf(st_theme_cs, sizeof(st_theme_cs), "%s", ret.addr);
colorname[258] = st_theme_cs;
}
}
void
+23 -32
View File
@@ -15,6 +15,7 @@
#include <pango/pangoxft.h>
#include "../sxmo-dwm/dwm_plugin.h"
#include "../dwm-plugins/theme.h"
#include <algorithm>
#include <cmath>
@@ -532,31 +533,17 @@ static int xdrawer_run_main(int argc, char** argv) {
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* 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);
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);
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("#aaaaaa");
XftColor colYellow = allocColor("#f4d03f");
XftColor colGreen = allocColor("#2ecc71");
bananarchy::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;
PhoneController controller;
size_t selected = 0;
@@ -591,7 +578,7 @@ static int xdrawer_run_main(int argc, char** argv) {
};
auto render = [&]() {
XSetForeground(dpy, gc, 0x222222);
XSetForeground(dpy, gc, theme.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
const auto& items = controller.items();
@@ -601,11 +588,11 @@ static int xdrawer_run_main(int argc, char** argv) {
int rowH = nextRy - ry;
bool isSel = (i == selected);
XSetForeground(dpy, gc, isSel ? 0x005577 : ((i % 2 == 0) ? 0x282828 : 0x2e2e2e));
XSetForeground(dpy, gc, 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);
}
@@ -635,19 +622,19 @@ static int xdrawer_run_main(int argc, char** argv) {
int barH = 12;
int barY = ry + (rowH - barH) / 2;
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, theme.spec.bgOverlay);
XFillRectangle(dpy, pix, gc, barX, barY, barW, barH);
XSetForeground(dpy, gc, isSel ? 0xdddddd : 0x555555);
XSetForeground(dpy, gc, isSel ? theme.spec.fgMain : theme.spec.border);
XDrawRectangle(dpy, pix, gc, barX, barY, barW - 1, barH - 1);
int fillW = ((barW - 2) * pct) / 100;
if (fillW > 0) {
XSetForeground(dpy, gc, isSel ? 0xf4d03f : 0x3498db);
XSetForeground(dpy, gc, isSel ? theme.spec.accent : theme.spec.info);
XFillRectangle(dpy, pix, gc, barX + 1, barY + 1, fillW, barH - 2);
}
int knobX = barX + std::clamp(fillW - 2, 0, barW - 5);
XSetForeground(dpy, gc, 0xffffff);
XSetForeground(dpy, gc, theme.spec.fgBright);
XFillRectangle(dpy, pix, gc, knobX, barY - 2, 5, barH + 4);
char pctBuf[16];
@@ -727,6 +714,9 @@ static int xdrawer_run_main(int argc, char** argv) {
closeOtherPlugin("xsms.pid", "/tmp/dwm_plugin_sms.fifo");
closeOtherPlugin("xfiles.pid", "/tmp/dwm_plugin_files.fifo");
closeOtherPlugin("xcamera.pid", "/tmp/dwm_plugin_camera.fifo");
closeOtherPlugin("xthemes.pid", "/tmp/dwm_plugin_themes.fifo");
theme.checkAndReload(dpy, screen, visual, cmap, layoutMain, layoutSmall,
layoutIcon, nullptr, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialKeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -802,6 +792,10 @@ static int xdrawer_run_main(int argc, char** argv) {
showWindow();
} else if (cmd == "CLOSE") {
hideWindow(false);
} else if (cmd == "RELOAD_THEME") {
theme.load(dpy, screen, visual, cmap, layoutMain, layoutSmall,
layoutIcon, nullptr, win);
render();
}
}
}
@@ -928,9 +922,6 @@ static int xdrawer_run_main(int argc, char** argv) {
::unlink(fifoPath.c_str());
::unlink(daemonPidPath.c_str());
pango_font_description_free(descSmall);
pango_font_description_free(descIcon);
pango_font_description_free(descMain);
g_object_unref(layoutSmall);
g_object_unref(layoutIcon);
g_object_unref(layoutMain);