git.strcat.st

/strcat/wm.git/ - summarytreelogarchivereleases

subject
make bar theme configurable via wmc
commit
28c22dd4a2b3dc107349a713d421472adc9ad4ca
date
2026-05-31T00:40:57Z
message
diff
 extras/bar.c |  64 +++++++++++++++++++++++++++++-------
 extras/bar.h |   2 ++
 wm.c         | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 wmc.c        |  38 +++++++++++++++++++--
 4 files changed, 196 insertions(+), 14 deletions(-)

diff --git a/extras/bar.c b/extras/bar.c
index a966298..e67898c 100644
--- a/extras/bar.c
+++ b/extras/bar.c
@@ -1,4 +1,5 @@
 #include <X11/Xlib.h>
+#include <X11/Xutil.h>
 #ifdef XINERAMA
 #include <X11/extensions/Xinerama.h>
 #endif
@@ -33,12 +34,18 @@ static int bar_monitor_count;
 static int bar_margin_x;
 static int bar_margin_y;
 static int bar_is_bottom;
+static unsigned long bar_fg_color;
+static unsigned long bar_bg_color;
+static unsigned long bar_sel_fg_color;
+static unsigned long bar_sel_bg_color;
+static unsigned long bar_sel_accent_color;
 static int bar_tags[max_tags];
 static char bar_status[max_status_len];
 static char bar_title[max_title_len];
 static struct bar_monitor bar_monitors[max_monitors];
 
 void bar_reconfigure(void);
+static unsigned long bar_alloc_color(const char *color_name, unsigned long fallback);
 
 static int
 bar_text_width(const char *text)
@@ -118,13 +125,11 @@ bar_load_monitors(void)
 static void
 bar_create_windows(void)
 {
-	unsigned long black;
 	XSetWindowAttributes attrs;
 	int i, window_x, window_y, window_width;
 
-	black = BlackPixel(bar_display, bar_screen);
 	attrs.override_redirect = True;
-	attrs.background_pixel = black;
+	attrs.background_pixel = bar_bg_color;
 	attrs.event_mask = ExposureMask;
 	for (i = 0; i < bar_monitor_count; i++) {
 	    window_x = bar_monitors[i].x + bar_margin_x;
@@ -157,6 +162,11 @@ bar_init(Display *display, Window root_window)
 	    return 0;
 	bar_gc = XCreateGC(display, root_window, 0, NULL);
 	XSetFont(display, bar_gc, bar_font->fid);
+	bar_fg_color = bar_alloc_color("#222222", BlackPixel(display, bar_screen));
+	bar_bg_color = bar_alloc_color("#ffffff", WhitePixel(display, bar_screen));
+	bar_sel_fg_color = bar_alloc_color("#111111", BlackPixel(display, bar_screen));
+	bar_sel_bg_color = bar_alloc_color("#dfe8ff", WhitePixel(display, bar_screen));
+	bar_sel_accent_color = bar_alloc_color("#005fcc", BlackPixel(display, bar_screen));
 	if (!bar_load_monitors())
 	    return 0;
 	bar_create_windows();
@@ -194,40 +204,37 @@ bar_set_title(const char *title)
 void
 bar_draw(void)
 {
-	unsigned long white, black;
 	int m, x, i, tw, tagw, title_x, status_x, bar_width;
 	char tag_text[8];
 
 	if (bar_display == NULL || bar_monitor_count < 1)
 	    return;
-	white = WhitePixel(bar_display, bar_screen);
-	black = BlackPixel(bar_display, bar_screen);
 	for (m = 0; m < bar_monitor_count; m++) {
 	    if (bar_monitors[m].window == None)
 	        continue;
 	    bar_width = bar_monitors[m].bar_width;
-	    bar_draw_rect(bar_monitors[m].window, black, 0, 0, bar_width, bar_height);
+	    bar_draw_rect(bar_monitors[m].window, bar_bg_color, 0, 0, bar_width, bar_height);
 	    x = 0;
 	    for (i = 0; i < bar_tag_count; i++) {
 	        snprintf(tag_text, sizeof(tag_text), "%d", bar_tags[i]);
 	        tw = bar_text_width(tag_text);
 	        tagw = tw + (bar_padding * 2);
 	        if (i == bar_selected_tag) {
-	            bar_draw_rect(bar_monitors[m].window, white, x, 2, tagw, bar_height - 4);
-	            bar_draw_text(bar_monitors[m].window, black, x + bar_padding, bar_height - 5, tag_text);
+	            bar_draw_rect(bar_monitors[m].window, bar_sel_bg_color, x, 0, tagw, bar_height);
+	            bar_draw_text(bar_monitors[m].window, bar_sel_accent_color, x + bar_padding, bar_height - 5, tag_text);
 	        } else {
-	            bar_draw_text(bar_monitors[m].window, white, x + bar_padding, bar_height - 5, tag_text);
+	            bar_draw_text(bar_monitors[m].window, bar_fg_color, x + bar_padding, bar_height - 5, tag_text);
 	        }
 	        x += tagw + bar_padding;
 	    }
 	    status_x = bar_width - bar_padding - bar_text_width(bar_status);
 	    if (status_x < x + bar_padding)
 	        status_x = x + bar_padding;
-	    bar_draw_text(bar_monitors[m].window, white, status_x, bar_height - 5, bar_status);
+	    bar_draw_text(bar_monitors[m].window, bar_fg_color, status_x, bar_height - 5, bar_status);
 	    title_x = x + bar_padding;
 	    tw = bar_text_width(bar_title);
 	    if (title_x + tw < status_x - bar_padding)
-	        bar_draw_text(bar_monitors[m].window, white, title_x, bar_height - 5, bar_title);
+	        bar_draw_text(bar_monitors[m].window, bar_sel_fg_color, title_x, bar_height - 5, bar_title);
 	}
 	XFlush(bar_display);
 }
@@ -255,6 +262,39 @@ bar_set_bottom(int is_bottom)
 	bar_reconfigure();
 }
 
+static unsigned long
+bar_alloc_color(const char *color_name, unsigned long fallback)
+{
+	Colormap cmap;
+	XColor color;
+
+	if (bar_display == NULL || color_name == NULL || color_name[0] == '\0')
+	    return fallback;
+	cmap = DefaultColormap(bar_display, bar_screen);
+	if (!XParseColor(bar_display, cmap, color_name, &color))
+	    return fallback;
+	if (!XAllocColor(bar_display, cmap, &color))
+	    return fallback;
+	return color.pixel;
+}
+
+void
+bar_set_colors(const char *fg, const char *bg, const char *sel_fg,
+    const char *sel_bg, const char *sel_accent)
+{
+	if (fg != NULL)
+	    bar_fg_color = bar_alloc_color(fg, bar_fg_color);
+	if (bg != NULL)
+	    bar_bg_color = bar_alloc_color(bg, bar_bg_color);
+	if (sel_fg != NULL)
+	    bar_sel_fg_color = bar_alloc_color(sel_fg, bar_sel_fg_color);
+	if (sel_bg != NULL)
+	    bar_sel_bg_color = bar_alloc_color(sel_bg, bar_sel_bg_color);
+	if (sel_accent != NULL)
+	    bar_sel_accent_color = bar_alloc_color(sel_accent, bar_sel_accent_color);
+	bar_draw();
+}
+
 void
 bar_reconfigure(void)
 {
diff --git a/extras/bar.h b/extras/bar.h
index 08f4392..f12da4b 100644
--- a/extras/bar.h
+++ b/extras/bar.h
@@ -9,6 +9,8 @@ void bar_set_tags(const int *tags, int count, int selected_tag);
 void bar_set_title(const char *title);
 void bar_set_margins(int margin_x, int margin_y);
 void bar_set_bottom(int is_bottom);
+void bar_set_colors(const char *fg, const char *bg, const char *sel_fg,
+    const char *sel_bg, const char *sel_accent);
 void bar_reconfigure(void);
 void bar_draw(void);
 void bar_cleanup(void);
diff --git a/wm.c b/wm.c
index 87647cd..4b98a9f 100644
--- a/wm.c
+++ b/wm.c
@@ -1,5 +1,7 @@
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include "extras/ewmh/ewmh.h"
 #ifdef XINERAMA
@@ -19,6 +21,11 @@
 #define cmd_bar_margin_x 5
 #define cmd_bar_margin_y 6
 #define cmd_reload 7
+#define cmd_bar_nf 8
+#define cmd_bar_nb 9
+#define cmd_bar_sf 10
+#define cmd_bar_sb 11
+#define cmd_bar_sa 12
 #define num_workspaces 9
 #define modifier_variants 4
 
@@ -27,6 +34,7 @@ static int window_workspace[max_windows], window_count, current_workspace;
 static Atom net_wm_desktop_atom;
 #ifdef BAR
 static Atom wm_bar_margin_x_atom, wm_bar_margin_y_atom, wm_bar_bottom_atom;
+static Atom wm_bar_nf_atom, wm_bar_nb_atom, wm_bar_sf_atom, wm_bar_sb_atom, wm_bar_sa_atom;
 static Atom net_wm_name_atom, utf8_string_atom;
 #endif
 static int is_root_child(Display *display, Window root_window, Window window);
@@ -231,6 +239,47 @@ write_root_int_property(Display *display, Window root_window, Atom atom, int val
 	XChangeProperty(display, root_window, atom, XA_CARDINAL, 32, PropModeReplace,
 	    (unsigned char *)&card, 1);
 }
+
+static char *
+read_root_string_property(Display *display, Window root_window, Atom atom)
+{
+	Atom actual_type;
+	int actual_format;
+	unsigned long nitems, bytes_after;
+	unsigned char *prop_data;
+	char *out;
+
+	prop_data = NULL;
+	if (XGetWindowProperty(display, root_window, atom, 0, 256, False, XA_STRING,
+	    &actual_type, &actual_format, &nitems, &bytes_after, &prop_data) != Success)
+	    return NULL;
+	if (prop_data == NULL)
+	    return NULL;
+	if (actual_type != XA_STRING || actual_format != 8 || nitems < 1) {
+	    XFree(prop_data);
+	    return NULL;
+	}
+	out = (char *)malloc(nitems + 1);
+	if (out == NULL) {
+	    XFree(prop_data);
+	    return NULL;
+	}
+	memcpy(out, prop_data, nitems);
+	out[nitems] = '\0';
+	XFree(prop_data);
+	return out;
+}
+
+static void
+write_root_string_property(Display *display, Window root_window, Atom atom, const char *value)
+{
+	if (value == NULL || value[0] == '\0') {
+	    XDeleteProperty(display, root_window, atom);
+	    return;
+	}
+	XChangeProperty(display, root_window, atom, XA_STRING, 8, PropModeReplace,
+	    (unsigned char *)value, (int)strlen(value));
+}
 #endif
 
 static void
@@ -379,6 +428,11 @@ main(int argc, char **argv)
 	wm_bar_margin_x_atom = XInternAtom(display, "_WM_BAR_MARGIN_X", False);
 	wm_bar_margin_y_atom = XInternAtom(display, "_WM_BAR_MARGIN_Y", False);
 	wm_bar_bottom_atom = XInternAtom(display, "_WM_BAR_BOTTOM", False);
+	wm_bar_nf_atom = XInternAtom(display, "_WM_BAR_NF", False);
+	wm_bar_nb_atom = XInternAtom(display, "_WM_BAR_NB", False);
+	wm_bar_sf_atom = XInternAtom(display, "_WM_BAR_SF", False);
+	wm_bar_sb_atom = XInternAtom(display, "_WM_BAR_SB", False);
+	wm_bar_sa_atom = XInternAtom(display, "_WM_BAR_SA", False);
 	net_wm_name_atom = XInternAtom(display, "_NET_WM_NAME", False);
 	utf8_string_atom = XInternAtom(display, "UTF8_STRING", False);
 #endif
@@ -391,6 +445,21 @@ main(int argc, char **argv)
 	ewmh_sync(display, root_window, windows, window_workspace, window_count);
 #ifdef BAR
 	bar_init(display, root_window);
+	{
+	    char *nf, *nb, *sf, *sb, *sa;
+
+	    nf = read_root_string_property(display, root_window, wm_bar_nf_atom);
+	    nb = read_root_string_property(display, root_window, wm_bar_nb_atom);
+	    sf = read_root_string_property(display, root_window, wm_bar_sf_atom);
+	    sb = read_root_string_property(display, root_window, wm_bar_sb_atom);
+	    sa = read_root_string_property(display, root_window, wm_bar_sa_atom);
+	    bar_set_colors(nf, nb, sf, sb, sa);
+	    free(nf);
+	    free(nb);
+	    free(sf);
+	    free(sb);
+	    free(sa);
+	}
 	bar_set_margins(read_root_int_property(display, root_window, wm_bar_margin_x_atom, 0),
 	    read_root_int_property(display, root_window, wm_bar_margin_y_atom, 0));
 	bar_set_bottom(read_root_int_property(display, root_window, wm_bar_bottom_atom, 0) ? 1 : 0);
@@ -429,6 +498,43 @@ main(int argc, char **argv)
 	            write_root_int_property(display, root_window, wm_bar_margin_y_atom, value);
 	            refresh_bar(display);
 	            continue;
+	        } else if (command == cmd_bar_nf || command == cmd_bar_nb ||
+	            command == cmd_bar_sf || command == cmd_bar_sb || command == cmd_bar_sa) {
+	            Atom source_atom;
+	            Atom actual_type;
+	            int actual_format;
+	            unsigned long nitems, bytes_after;
+	            unsigned char *prop_data;
+
+	            source_atom = (Atom)event.xclient.data.l[1];
+	            prop_data = NULL;
+	            if (XGetWindowProperty(display, root_window, source_atom, 0, 256, False,
+	                XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after,
+	                &prop_data) == Success &&
+	                prop_data != NULL && actual_type == XA_STRING &&
+	                actual_format == 8 && nitems > 0) {
+	                if (command == cmd_bar_nf) {
+	                    bar_set_colors((char *)prop_data, NULL, NULL, NULL, NULL);
+	                    write_root_string_property(display, root_window, wm_bar_nf_atom, (char *)prop_data);
+	                } else if (command == cmd_bar_nb) {
+	                    bar_set_colors(NULL, (char *)prop_data, NULL, NULL, NULL);
+	                    write_root_string_property(display, root_window, wm_bar_nb_atom, (char *)prop_data);
+	                } else if (command == cmd_bar_sf) {
+	                    bar_set_colors(NULL, NULL, (char *)prop_data, NULL, NULL);
+	                    write_root_string_property(display, root_window, wm_bar_sf_atom, (char *)prop_data);
+	                } else if (command == cmd_bar_sb) {
+	                    bar_set_colors(NULL, NULL, NULL, (char *)prop_data, NULL);
+	                    write_root_string_property(display, root_window, wm_bar_sb_atom, (char *)prop_data);
+	                } else if (command == cmd_bar_sa) {
+	                    bar_set_colors(NULL, NULL, NULL, NULL, (char *)prop_data);
+	                    write_root_string_property(display, root_window, wm_bar_sa_atom, (char *)prop_data);
+	                }
+	                refresh_bar(display);
+	            }
+	            if (prop_data != NULL)
+	                XFree(prop_data);
+	            XDeleteProperty(display, root_window, source_atom);
+	            continue;
 	        }
 #endif
 	        if (command == cmd_kill) {
diff --git a/wmc.c b/wmc.c
index ab18a9d..415def1 100644
--- a/wmc.c
+++ b/wmc.c
@@ -1,4 +1,5 @@
 #include <X11/Xlib.h>
+#include <X11/Xatom.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -11,6 +12,11 @@
 #define cmd_bar_margin_x 5
 #define cmd_bar_margin_y 6
 #define cmd_reload 7
+#define cmd_bar_nf 8
+#define cmd_bar_nb 9
+#define cmd_bar_sf 10
+#define cmd_bar_sb 11
+#define cmd_bar_sa 12
 
 int
 main(int argc, char **argv)
@@ -41,6 +47,19 @@ main(int argc, char **argv)
 	        return 1;
 	    }
 	} else if (argc == 4 && !strcmp(argv[1], "bar")) {
+	    if (!strcmp(argv[2], "nf"))
+	        command = cmd_bar_nf;
+	    else if (!strcmp(argv[2], "nb"))
+	        command = cmd_bar_nb;
+	    else if (!strcmp(argv[2], "sf"))
+	        command = cmd_bar_sf;
+	    else if (!strcmp(argv[2], "sb"))
+	        command = cmd_bar_sb;
+	    else if (!strcmp(argv[2], "sa"))
+	        command = cmd_bar_sa;
+	    if (command >= cmd_bar_nf && command <= cmd_bar_sa) {
+	        value = 0;
+	    } else {
 	    value = (int)strtol(argv[3], &end_ptr, 10);
 	    if (*end_ptr != '\0' || value < 0)
 	        return 1;
@@ -49,9 +68,11 @@ main(int argc, char **argv)
 	    else if (!strcmp(argv[2], "my"))
 	        command = cmd_bar_margin_y;
 	    else {
-	        fprintf(stderr, "usage: wmc bar mx <px> | wmc bar my <px>\n");
+	        fprintf(stderr, "usage: wmc bar mx <px> | wmc bar my <px> | "
+	            "wmc bar nf|nb|sf|sb|sa <color>\n");
 	        return 1;
 	    }
+	    }
 	}
 	else if (argc == 2)
 	    workspace = (int)strtol(argv[1], &end_ptr, 10);
@@ -62,7 +83,8 @@ main(int argc, char **argv)
 	    workspace = (int)strtol(argv[2], &end_ptr, 10);
 	else {
 	    fprintf(stderr, "usage: wmc [ws] 1-9 | wmc move 1-9 | wmc kill | wmc reload | "
-	        "wmc bar top|bottom | wmc bar mx <px> | wmc bar my <px>\n");
+	        "wmc bar top|bottom | wmc bar mx <px> | wmc bar my <px> | "
+	        "wmc bar nf|nb|sf|sb|sa <color>\n");
 	    return 1;
 	}
 	if ((command == cmd_workspace || command == cmd_move) &&
@@ -83,6 +105,18 @@ main(int argc, char **argv)
 	event.xclient.data.l[0] = command;
 	if (command == cmd_workspace || command == cmd_move)
 	    event.xclient.data.l[1] = workspace > 0 ? workspace - 1 : 0;
+	else if (command >= cmd_bar_nf && command <= cmd_bar_sa) {
+	    Atom payload_atom;
+	    char atom_name[64];
+	    static unsigned long seq;
+
+	    seq++;
+	    snprintf(atom_name, sizeof(atom_name), "_WM_CTL_PAYLOAD_%lu", seq);
+	    payload_atom = XInternAtom(display, atom_name, False);
+	    XChangeProperty(display, root_window, payload_atom, XA_STRING, 8, PropModeReplace,
+	        (unsigned char *)argv[3], (int)strlen(argv[3]));
+	    event.xclient.data.l[1] = (long)payload_atom;
+	}
 	else
 	    event.xclient.data.l[1] = workspace;
 	XSendEvent(display, root_window, False, SubstructureNotifyMask, &event);