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

This commit is contained in:
Luxferre
2026-10-21 20:36:27 +03:00
parent f615c6ddbe
commit a31595070c
60 changed files with 3692 additions and 419 deletions
+64 -66
View File
@@ -130,10 +130,12 @@ int runSmsApp(int argc, char** argv) {
Visual* visual = DefaultVisual(dpy, screen);
Colormap cmap = DefaultColormap(dpy, screen);
ThemeSpec initSpec = loadThemeSpec(dpy, screen);
XSetWindowAttributes swa {};
swa.override_redirect = True;
swa.background_pixel = 0x222222;
swa.border_pixel = 0x005577;
swa.background_pixel = initSpec.bgMain;
swa.border_pixel = initSpec.selBg;
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
VisibilityChangeMask | StructureNotifyMask;
@@ -155,37 +157,18 @@ int runSmsApp(int argc, char** argv) {
GC gc = XCreateGC(dpy, pix, 0, nullptr);
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
PangoFontDescription* descMain =
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
pango_layout_set_font_description(layoutMain, descMain);
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
PangoFontDescription* descSmall =
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
pango_layout_set_font_description(layoutSmall, descSmall);
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
PangoFontDescription* descIcon =
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
pango_layout_set_font_description(layoutIcon, descIcon);
auto allocColor = [&](const char* hex) {
XftColor col {};
XftColorAllocName(dpy, visual, cmap, hex, &col);
return col;
};
XftColor colWhite = allocColor("#ffffff");
XftColor colLight = allocColor("#dddddd");
XftColor colMuted = allocColor("#999999");
XftColor colYellow = allocColor("#f4d03f");
XftColor colGreen = allocColor("#2ecc71");
XftColor colBlue = allocColor("#5dade2");
XftColor colRed = allocColor("#e74c3c");
PluginTheme pt;
pt.init(dpy, screen, visual, cmap, win);
auto& layoutMain = pt.layoutMain;
auto& layoutSmall = pt.layoutSmall;
auto& layoutIcon = pt.layoutIcon;
auto& colWhite = pt.colWhite;
auto& colLight = pt.colLight;
auto& colMuted = pt.colMuted;
auto& colYellow = pt.colYellow;
auto& colGreen = pt.colGreen;
auto& colBlue = pt.colBlue;
auto& colRed = pt.colRed;
ContactBook book;
std::vector<SmsThread> allThreads;
@@ -442,9 +425,9 @@ int runSmsApp(int argc, char** argv) {
const std::string& centerLabel,
const std::string& rightLabel) {
const int barY = kWinHeight - 26;
XSetForeground(dpy, gc, 0x141414);
XSetForeground(dpy, gc, pt.spec.bgFooter);
XFillRectangle(dpy, pix, gc, 0, barY, kWinWidth, 26);
XSetForeground(dpy, gc, 0x2d2d2d);
XSetForeground(dpy, gc, pt.spec.divider);
XDrawLine(dpy, pix, gc, 0, barY, kWinWidth, barY);
drawPango(layoutMain, &colBlue, 6, barY, 26, leftLabel);
@@ -467,14 +450,15 @@ int runSmsApp(int argc, char** argv) {
auto drawUI = [&]() {
if (!visible) return;
pt.checkAndReload();
XSetForeground(dpy, gc, 0x222222);
XSetForeground(dpy, gc, pt.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
if (mode == ViewMode::Threads) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 38);
XSetForeground(dpy, gc, 0x333333);
XSetForeground(dpy, gc, pt.spec.border);
XDrawLine(dpy, pix, gc, 0, 37, kWinWidth, 37);
if (searchQuery.empty()) {
@@ -521,12 +505,13 @@ int runSmsApp(int argc, char** argv) {
bool isSel = (idx == selectedThread);
if (isSel) {
XSetForeground(dpy, gc, 0x005577);
XSetForeground(dpy, gc, pt.spec.selBg);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
XSetForeground(dpy, gc, 0x0088bb);
XSetForeground(dpy, gc, pt.spec.accentAlt);
XDrawLine(dpy, pix, gc, 0, ry, kWinWidth, ry);
} else {
XSetForeground(dpy, gc, (i % 2 == 0) ? 0x222222 : 0x1d1d1d);
XSetForeground(dpy, gc,
(i % 2 == 0) ? pt.spec.bgAltEven : pt.spec.bgAltOdd);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
}
@@ -562,9 +547,9 @@ int runSmsApp(int argc, char** argv) {
searchQuery.empty() ? "Close" : "Clear");
} else if (mode == ViewMode::ThreadView) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
XSetForeground(dpy, gc, 0x333333);
XSetForeground(dpy, gc, pt.spec.border);
XDrawLine(dpy, pix, gc, 0, 35, kWinWidth, 35);
std::string cname = book.resolveName(activeNumber);
@@ -640,13 +625,15 @@ int runSmsApp(int argc, char** argv) {
int cardH = std::min(cg.h, areaH - 4);
unsigned long bg = isSel
? 0x004466
: (cg.incoming ? 0x262d32 : 0x1f2d38);
? pt.spec.bgInputFocus
: (cg.incoming ? pt.spec.bgBubbleIn
: pt.spec.bgBubbleOut);
XSetForeground(dpy, gc, bg);
XFillRectangle(dpy, pix, gc, cardX, curY, cardW, cardH);
unsigned long accent = cg.incoming ? 0x2ecc71 : 0x5dade2;
XSetForeground(dpy, gc, isSel ? 0xf4d03f : accent);
unsigned long accent =
cg.incoming ? pt.spec.success : pt.spec.info;
XSetForeground(dpy, gc, isSel ? pt.spec.accent : accent);
if (cg.incoming) {
XFillRectangle(dpy, pix, gc, cardX, curY, 3, cardH);
} else {
@@ -672,9 +659,9 @@ int runSmsApp(int argc, char** argv) {
drawSoftkeyBar("Options", "Reply", "Back");
} else if (mode == ViewMode::Compose) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 32);
XSetForeground(dpy, gc, 0x333333);
XSetForeground(dpy, gc, pt.spec.border);
XDrawLine(dpy, pix, gc, 0, 31, kWinWidth, 31);
drawPango(layoutIcon, &colBlue, 6, 0, 30, kIconSend);
@@ -703,9 +690,10 @@ int runSmsApp(int argc, char** argv) {
// Field 0: Recipient
bool toSel = (composeField == 0);
XSetForeground(dpy, gc, toSel ? 0x004466 : 0x1a1a1a);
XSetForeground(dpy, gc,
toSel ? pt.spec.bgInputFocus : pt.spec.bgInput);
XFillRectangle(dpy, pix, gc, 6, 36, kWinWidth - 12, 32);
XSetForeground(dpy, gc, toSel ? 0x0088bb : 0x383838);
XSetForeground(dpy, gc, toSel ? pt.spec.accentAlt : pt.spec.border);
XDrawRectangle(dpy, pix, gc, 6, 36, kWinWidth - 13, 31);
std::string cname = book.resolveName(composeRecipient);
@@ -719,9 +707,10 @@ int runSmsApp(int argc, char** argv) {
// Field 1: Message Body
bool msgSel = (composeField == 1);
XSetForeground(dpy, gc, msgSel ? 0x182630 : 0x181818);
XSetForeground(dpy, gc,
msgSel ? pt.spec.bgInputFocus : pt.spec.bgInput);
XFillRectangle(dpy, pix, gc, 6, 72, kWinWidth - 12, 156);
XSetForeground(dpy, gc, msgSel ? 0x0088bb : 0x383838);
XSetForeground(dpy, gc, msgSel ? pt.spec.accentAlt : pt.spec.border);
XDrawRectangle(dpy, pix, gc, 6, 72, kWinWidth - 13, 155);
std::string bodyDisp = composeText;
@@ -752,15 +741,15 @@ int runSmsApp(int argc, char** argv) {
// Field 2 & 3: Send & Save Draft buttons
bool sendSel = (composeField == 2);
bool draftSel = (composeField == 3);
XSetForeground(dpy, gc, sendSel ? 0x005577 : 0x262626);
XSetForeground(dpy, gc, sendSel ? pt.spec.selBg : pt.spec.bgButton);
XFillRectangle(dpy, pix, gc, 6, 234, 118, 30);
XSetForeground(dpy, gc, sendSel ? 0x0088bb : 0x444444);
XSetForeground(dpy, gc, sendSel ? pt.spec.accentAlt : pt.spec.border);
XDrawRectangle(dpy, pix, gc, 6, 234, 117, 29);
drawPango(layoutMain, &colGreen, 18, 234, 30, "Send SMS", 100);
XSetForeground(dpy, gc, draftSel ? 0x005577 : 0x262626);
XSetForeground(dpy, gc, draftSel ? pt.spec.selBg : pt.spec.bgButton);
XFillRectangle(dpy, pix, gc, 130, 234, 104, 30);
XSetForeground(dpy, gc, draftSel ? 0x0088bb : 0x444444);
XSetForeground(dpy, gc, draftSel ? pt.spec.accentAlt : pt.spec.border);
XDrawRectangle(dpy, pix, gc, 130, 234, 103, 29);
drawPango(layoutMain, &colYellow, 146, 234, 30, "Draft", 84);
@@ -778,7 +767,7 @@ int runSmsApp(int argc, char** argv) {
}
} else if (mode == ViewMode::PickContact) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
drawPango(layoutIcon, &colBlue, 8, 0, 34, kIconBook);
drawPango(layoutMain, &colWhite, 30, 0, 34, "Select Recipient", 200);
@@ -799,7 +788,7 @@ int runSmsApp(int argc, char** argv) {
const auto& c = pickerContacts[idx];
int ry = listY + i * rowH;
bool isSel = (idx == selectedPicker);
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
drawPango(layoutIcon, c.isRecent ? &colYellow : &colBlue, 6, ry + 1,
20, c.isRecent ? kIconClock : kIconUser);
@@ -811,7 +800,7 @@ int runSmsApp(int argc, char** argv) {
drawSoftkeyBar("Select", "Select", "Back");
} else if (mode == ViewMode::Options) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
drawPango(layoutIcon, &colBlue, 8, 0, 34, kIconList);
drawPango(layoutMain, &colWhite, 30, 0, 34, "Message Options", 200);
@@ -821,7 +810,7 @@ int runSmsApp(int argc, char** argv) {
for (size_t i = 0; i < optionsList.size(); ++i) {
int ry = listY + static_cast<int>(i) * rowH;
bool isSel = (static_cast<int>(i) == selectedOption);
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
drawPango(layoutIcon, isSel ? &colYellow : &colBlue, 10, ry, rowH,
optionsList[i].icon);
@@ -831,7 +820,7 @@ int runSmsApp(int argc, char** argv) {
drawSoftkeyBar("Select", "Select", "Back");
} else if (mode == ViewMode::ConfirmDelete) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
drawPango(layoutIcon, &colRed, 8, 0, 36, kIconTrash);
drawPango(layoutMain, &colWhite, 30, 0, 36, "Delete Thread?", 200);
@@ -844,7 +833,10 @@ int runSmsApp(int argc, char** argv) {
for (int i = 0; i < 2; ++i) {
int ry = 164 + i * 40;
bool isSel = (confirmSelected == i);
XSetForeground(dpy, gc, isSel ? (i == 1 ? 0x922b21 : 0x005577) : 0x2a2a2a);
XSetForeground(
dpy, gc,
isSel ? (i == 1 ? pt.spec.dangerBg : pt.spec.selBg)
: pt.spec.bgButton);
XFillRectangle(dpy, pix, gc, 16, ry, kWinWidth - 32, 34);
drawPango(layoutMain, &colWhite, 28, ry, 34,
i == 0 ? "No, Keep Thread" : "Yes, Delete Thread", 184);
@@ -856,9 +848,9 @@ int runSmsApp(int argc, char** argv) {
{
std::lock_guard<std::mutex> lk(sendMutex);
if (!statusBanner.empty() && tNow < statusBannerUntilMs) {
XSetForeground(dpy, gc, 0x102535);
XSetForeground(dpy, gc, pt.spec.bgToast);
XFillRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 16, 28);
XSetForeground(dpy, gc, 0x5dade2);
XSetForeground(dpy, gc, pt.spec.info);
XDrawRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 17, 27);
drawPango(layoutSmall, &colWhite, 14, kWinHeight - 58, 28, statusBanner,
kWinWidth - 28);
@@ -1106,6 +1098,9 @@ int runSmsApp(int argc, char** argv) {
for (const auto& cmd : readFifoLines(fifoFd)) {
if (cmd == "NOP") {
continue;
} else if (cmd == "RELOAD_THEME") {
pt.reload();
if (visible) drawUI();
} else if (cmd == "CLOSE") {
hideWindow();
} else if (cmd == "OPEN") {
@@ -1416,7 +1411,10 @@ int runSmsApp(int argc, char** argv) {
if (pr > 0) {
if (fifoIdx >= 0 && (pfds[fifoIdx].revents & POLLIN)) {
for (const auto& cmd : readFifoLines(fifoFd)) {
if (cmd == "CLOSE") {
if (cmd == "RELOAD_THEME") {
pt.reload();
if (visible) drawUI();
} else if (cmd == "CLOSE") {
hideWindow();
} else if (cmd == "OPEN") {
showWindow("");