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
+194
View File
@@ -107,6 +107,10 @@ static Clr *scheme[SchemeLast];
static char *colors[SchemeLast][2];
static const char *fonts[3] = { NULL, NULL, NULL };
static char xres_font_main[256] = "";
static char xres_font_icon[256] = "";
static time_t last_xres_mtime = 0;
static long last_xres_nsec = 0;
#include "config.h"
@@ -151,11 +155,14 @@ static const char *ICON_ZMI = "\xf3\xb0\x9b\xad";
static const char *ICON_ZMO = "\xf3\xb0\x9b\xac";
static const char *ICON_A2X = "\xe2\x87\x84";
static const char *ICON_KBD = "\xef\x84\x9c";
static const char *ICON_THM = "\xf3\xb0\x8f\x98";
static void drawmenu(void);
static void show_menu(void);
static void hide_menu(void);
static void open_builtin_menu(const char *menu_name, int keep_idx);
static void load_dmenu_xresources(void);
static void reload_dmenu_theme(void);
static char *
cistrstr(const char *s, const char *sub)
@@ -1711,6 +1718,9 @@ open_builtin_menu(const char *menu_name, int keep_idx)
snprintf(label, sizeof(label), "%s Camera", ICON_CAM);
add_menu_item(label, "@plugin:camera", 0);
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
char bl_buf[32] = "";
if (read_first_line("/sys/class/leds/white:torch/brightness", bl_buf, sizeof(bl_buf)) ||
read_first_line("/sys/class/leds/white:flash/brightness", bl_buf, sizeof(bl_buf))) {
@@ -1742,6 +1752,8 @@ open_builtin_menu(const char *menu_name, int keep_idx)
} else if (strcmp(menu_name, "applications") == 0) {
reset_session_defaults(1, "Apps");
append_active_call_entries();
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
load_cache_or_hook("sxmo_apps.cache", "sxmo_hook_apps.sh");
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
add_menu_item(label, "@menu:sys", 1);
@@ -1776,6 +1788,8 @@ open_builtin_menu(const char *menu_name, int keep_idx)
home = "/home/user";
char pbuf[256];
snprintf(label, sizeof(label), "%s Themes", ICON_THM);
add_menu_item(label, "@plugin:themes", 0);
snprintf(label, sizeof(label), "%s Brightness", ICON_ARU);
add_menu_item(label, "@toggle:bright_up", 1);
snprintf(label, sizeof(label), "%s Brightness", ICON_ARD);
@@ -1942,6 +1956,7 @@ close_other_active_plugins(void)
{ "xsms.pid", "/tmp/dwm_plugin_sms.fifo" },
{ "xfiles.pid", "/tmp/dwm_plugin_files.fifo" },
{ "xcamera.pid", "/tmp/dwm_plugin_camera.fifo" },
{ "xthemes.pid", "/tmp/dwm_plugin_themes.fifo" },
};
const char *rdir = getenv("XDG_RUNTIME_DIR");
const char *base = (rdir && rdir[0]) ? rdir : "/run/user/10000";
@@ -1960,12 +1975,185 @@ close_other_active_plugins(void)
}
}
static const char *
dmenu_xrm_lookup(XrmDatabase xdb, const char *k1, const char *k2, const char *k3)
{
char *type = NULL;
XrmValue xval;
if (k1 && XrmGetResource(xdb, k1, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k2 && XrmGetResource(xdb, k2, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
if (k3 && XrmGetResource(xdb, k3, "*", &type, &xval) && xval.addr && xval.addr[0])
return xval.addr;
return NULL;
}
static void
dmenu_set_color(int scm, int col, const char *val)
{
if (!val || !val[0])
return;
free(colors[scm][col]);
colors[scm][col] = strdup(val);
}
static void
load_dmenu_xresources(void)
{
XrmInitialize();
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
struct stat st;
if (stat(path, &st) == 0) {
last_xres_mtime = st.st_mtim.tv_sec;
last_xres_nsec = st.st_mtim.tv_nsec;
}
XrmDatabase xdb = XrmGetFileDatabase(path);
Atom actual_type = None;
int actual_format = 0;
unsigned long nitems = 0, bytes_after = 0;
unsigned char *prop = NULL;
if (!xdb && XGetWindowProperty(dpy, root, XA_RESOURCE_MANAGER, 0L, 65536L, False,
XA_STRING, &actual_type, &actual_format,
&nitems, &bytes_after, &prop) == Success && prop) {
if (actual_type == XA_STRING && actual_format == 8 && nitems > 0)
xdb = XrmGetStringDatabase((const char *)prop);
XFree(prop);
}
if (!xdb) {
char *xrm = XResourceManagerString(dpy);
if (xrm)
xdb = XrmGetStringDatabase(xrm);
}
if (xdb) {
const char *ff = dmenu_xrm_lookup(xdb, "theme.fontFace", NULL, NULL);
const char *icf = dmenu_xrm_lookup(xdb, "theme.iconFontFace", NULL, NULL);
const char *bsz = dmenu_xrm_lookup(xdb, "theme.baseFontSize", NULL, NULL);
const char *dmf = dmenu_xrm_lookup(xdb, "dmenu.font", NULL, NULL);
if (ff || bsz) {
int base_sz = bsz ? atoi(bsz) : 15;
if (base_sz < 8 || base_sz > 32)
base_sz = 15;
int large_sz = (base_sz * 18 + 7) / 15;
snprintf(xres_font_main, sizeof(xres_font_main),
"%s:pixelsize=%d:style=Regular:antialias=true:autohint=true",
ff ? ff : "Fira Code", large_sz);
snprintf(xres_font_icon, sizeof(xres_font_icon),
"%s:pixelsize=%d:style=Regular:antialias=true:autohint=true",
icf ? icf : "DejaVuSansM Nerd Font", large_sz);
fonts[0] = xres_font_main;
fonts[1] = xres_font_icon;
} else if (dmf) {
snprintf(xres_font_main, sizeof(xres_font_main), "%s", dmf);
fonts[0] = xres_font_main;
fonts[1] = defaultfonts[1];
}
dmenu_set_color(SchemeNorm, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgHeader", "theme.bgMain", "dmenu.background"));
dmenu_set_color(SchemeNorm, ColFg,
dmenu_xrm_lookup(xdb, "theme.fgBright", "theme.fgMain", "dmenu.foreground"));
dmenu_set_color(SchemeSel, ColBg,
dmenu_xrm_lookup(xdb, "theme.selBg", "dmenu.selbackground", NULL));
dmenu_set_color(SchemeSel, ColFg,
dmenu_xrm_lookup(xdb, "theme.selFg", "dmenu.selforeground", NULL));
dmenu_set_color(SchemeOut, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgSearch", "dmenu.outbackground", "theme.bgInputFocus"));
dmenu_set_color(SchemeOut, ColFg,
dmenu_xrm_lookup(xdb, "theme.fgBright", "dmenu.outforeground", "theme.fgMain"));
dmenu_set_color(SchemeNormHighlight, ColBg,
dmenu_xrm_lookup(xdb, "theme.bgHeader", "theme.bgMain", "dmenu.background"));
dmenu_set_color(SchemeNormHighlight, ColFg,
dmenu_xrm_lookup(xdb, "theme.highlight", "theme.accent", "dmenu.normhlforeground"));
dmenu_set_color(SchemeSelHighlight, ColBg,
dmenu_xrm_lookup(xdb, "theme.selBg", "dmenu.selbackground", NULL));
dmenu_set_color(SchemeSelHighlight, ColFg,
dmenu_xrm_lookup(xdb, "theme.highlight", "theme.accent", "dmenu.selhlforeground"));
XrmDestroyDatabase(xdb);
}
}
static void
reload_dmenu_theme(void)
{
int i;
load_dmenu_xresources();
for (i = 0; i < SchemeLast; ++i) {
if (!colors[i][ColBg])
colors[i][ColBg] = strdup(defaultcolors[i][ColBg]);
if (!colors[i][ColFg])
colors[i][ColFg] = strdup(defaultcolors[i][ColFg]);
}
if (drw->fonts) {
drw_fontset_free(drw->fonts);
drw->fonts = NULL;
}
if (!fonts[0]) {
fonts[0] = defaultfonts[0];
fonts[1] = defaultfonts[1];
}
if (!drw_fontset_create(drw, fonts, 2))
drw_fontset_create(drw, defaultfonts, 2);
lrpad = drw->fonts->h / 2;
bh = drw->fonts->h + 2;
bh = MAX(bh, (int)lineheight);
default_lines = MAX(1, (avail_h_cached / (int)bh) - 1);
for (i = 0; i < SchemeLast; i++) {
free(scheme[i]);
scheme[i] = drw_scm_create(drw, (const char **)colors[i], 2);
}
for (i = 0; i < SchemeLast; ++i) {
free(colors[i][ColFg]);
free(colors[i][ColBg]);
colors[i][ColFg] = NULL;
colors[i][ColBg] = NULL;
}
if (win) {
XSetWindowBackground(dpy, win, scheme[SchemeNorm][ColBg].pixel);
XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
}
if (menu_active)
drawmenu();
}
static void
check_dmenu_xres_changed(void)
{
const char *home = getenv("HOME");
char path[512];
snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user");
struct stat st;
if (stat(path, &st) == 0) {
if (st.st_mtim.tv_sec != last_xres_mtime || st.st_mtim.tv_nsec != last_xres_nsec)
reload_dmenu_theme();
}
}
static void
show_menu(void)
{
const int bar_h = 24;
int target_y;
check_dmenu_xres_changed();
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
if (lines > 0) {
lines = default_lines;
@@ -2242,6 +2430,8 @@ handle_fifo_commands(int fifo_fd)
if (strcmp(line, "CLOSE") == 0) {
if (menu_active)
cancel_session();
} else if (strcmp(line, "RELOAD_THEME") == 0) {
reload_dmenu_theme();
} else if (strcmp(line, "OPEN") == 0) {
if (active_client_fd >= 0)
finish_client_session(1, NULL);
@@ -2469,6 +2659,8 @@ setup(int map_immediately)
for (j = 0; j < SchemeLast; ++j) {
free(colors[j][ColFg]);
free(colors[j][ColBg]);
colors[j][ColFg] = NULL;
colors[j][ColBg] = NULL;
}
clip = XInternAtom(dpy, "CLIPBOARD", False);
@@ -2789,6 +2981,8 @@ dmenu_run_main(int argc, char *argv[])
die("could not get embedding window attributes: 0x%lx", parentwin);
drw = drw_create(dpy, screen, root, wa.width, wa.height);
load_dmenu_xresources();
if (!fonts[0]) {
fonts[0] = defaultfonts[0];
fonts[1] = defaultfonts[1];