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