foxygit / sxbar Log in
commit d4ab3d253314eb4443fbcdd8dbfbcfe44ff20ebf
Author:     Abhinav <abhinav.prsai@gmail.com>
AuthorDate: Wed Jul 2 21:03:29 2025 +0100
Commit:     Abhinav <abhinav.prsai@gmail.com>
CommitDate: Wed Jul 2 21:03:29 2025 +0100

    LOTS OF FEATURES. CHECK DESC

    remove config.h - replace with default configs in init_defaults
    add MAX_MONITORS constant (32)
    add missing config options

    too much in sxbar.c just check the code 💀
---
 src/config.h |  17 -----
 src/defs.h   |  12 +++-
 src/sxbar.c  | 212 ++++++++++++++++++++++++++++++++++++++---------------------
 3 files changed, 147 insertions(+), 94 deletions(-)

diff --git a/src/config.h b/src/config.h
deleted file mode 100644
index b7feec1..0000000
--- a/src/config.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#define BOTTOM_BAR      True
-#define BAR_HEIGHT      25
-#define BAR_VERT_PAD    0
-#define BAR_HORI_PAD    0
-#define BAR_BORDER      False
-#define BAR_BORDER_W    2
-
-#define BAR_COLOR_BG        "#000000"
-#define BAR_COLOR_FG        "#7abccd"
-#define BAR_COLOR_BORDER    "#005577"
-#define BAR_FONT            "fixed"
-#define BAR_TEXT_PAD        10
-
-#define BAR_WS_HIGHLIGHT_LEFT   "*"
-#define BAR_WS_HIGHLIGHT_RIGHT  ""
-#define BAR_WS_PADDING          10
-#define BAR_WS_SPACING          10
diff --git a/src/defs.h b/src/defs.h
index 5888108..e2e3a75 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -6,17 +6,23 @@
 #define SXBAR_AUTHOR	"(C) Abhinav Prasai 2025"
 #define SXBAR_LICINFO	"See LICENSE for more info"

+#define MAX_MONITORS 32
+
 typedef struct {
 	Bool bottom_bar;
 	Bool border;
-	long border_colour;
-	long background_colour;
-	long foreground_colour;
+	unsigned long border_colour;
+	unsigned long background_colour;
+	unsigned long foreground_colour;
 	int height;
 	int vertical_padding;
 	int horizontal_padding;
 	int border_width;
+	int text_padding;
+	int workspace_spacing;
 	char *font;
+	char *workspace_highlight_left;
+	char *workspace_highlight_right;
 } Config;

 typedef void (*EventHandler)(XEvent *);
diff --git a/src/sxbar.c b/src/sxbar.c
index 9a17efa..16168cd 100644
--- a/src/sxbar.c
+++ b/src/sxbar.c
@@ -12,16 +12,17 @@
 #include "defs.h"

 void create_bars(void);
+void draw_bar_content(Window win, int monitor_index);
+int find_window_monitor(Window win);
 int get_current_workspace(void);
 char **get_workspace_name(int *count);
 void hdl_dummy(XEvent *xev);
 void hdl_expose(XEvent *xev);
 void hdl_property(XEvent *xev);
+void init_defaults(void);
 unsigned long parse_col(const char *hex);
 void run(void);
 void setup(void);
-int find_monitor_for_window(Window win);
-void draw_bar_content(Window win, int monitor_index);

 EventHandler evtable[LASTEvent];
 XFontStruct *font;
@@ -32,11 +33,7 @@ int nmonitors = 0;
 XineramaScreenInfo *monitors = NULL;
 GC gc;
 int scr;
-unsigned long fg_col;
-unsigned long bg_col;
-unsigned long border_col;
-
-#include "config.h"
+Config config;

 void create_bars(void)
 {
@@ -58,24 +55,40 @@ void create_bars(void)
 		monitors[0].height = DisplayHeight(dpy, scr);
 	}

+	/* TODO: ADD SUPPORT FOR SELECTING MONITORS */
 	wins = malloc(nmonitors * sizeof(Window));

 	for (int i = 0; i < nmonitors; i++) {
-		int bw = BAR_BORDER ? BAR_BORDER_W : 0;
-		int w = monitors[i].width - (2 * BAR_HORI_PAD);
-		int x = monitors[i].x_org + (monitors[i].width - w) / 2;
-		int h = BAR_HEIGHT;
-		int y = BOTTOM_BAR ? monitors[i].y_org + monitors[i].height - h - BAR_VERT_PAD - bw
-		                   : monitors[i].y_org + BAR_VERT_PAD;
-
-		XSetWindowAttributes wa = {.background_pixel = bg_col,
-		                           .border_pixel = border_col,
+		int bw = config.border ? config.border_width : 0;
+		/* window content width (excluding border) */
+		int w = monitors[i].width - (2 * config.horizontal_padding);
+		/* padding from screen edge */
+		int x = monitors[i].x_org + config.horizontal_padding;
+		int h = config.height;
+		int y;
+
+		if (config.bottom_bar) {
+			y = monitors[i].y_org + monitors[i].height - h - config.vertical_padding - bw;
+		}
+		else {
+			y = monitors[i].y_org + config.vertical_padding;
+		}
+
+		XSetWindowAttributes wa = {.background_pixel = config.background_colour,
+		                           .border_pixel = config.border_colour,
 		                           .event_mask = ExposureMask | ButtonPressMask};

 		wins[i] = XCreateWindow(dpy, root, x, y, w, h, bw, CopyFromParent, InputOutput,
 		                        DefaultVisual(dpy, scr),
 		                        CWBackPixel | CWBorderPixel | CWEventMask, &wa);

+		XStoreName(dpy, wins[i], "sxbar");
+
+		XClassHint class_hint;
+		class_hint.res_name = "sxbar";
+		class_hint.res_class = "sxbar";
+		XSetClassHint(dpy, wins[i], &class_hint);
+
 		Atom A_WM_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
 		Atom A_WM_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
 		XChangeProperty(dpy, wins[i], A_WM_TYPE, XA_ATOM, 32, PropModeReplace,
@@ -84,15 +97,15 @@ void create_bars(void)
 		Atom A_STRUT = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False);
 		long strut[12] = {0};

-		if (BOTTOM_BAR) {
+		if (config.bottom_bar) {
 			strut[3] = h + bw;
 			strut[10] = x;
-			strut[11] = x + w - 1;
+			strut[11] = x + w + (2 * bw) - 1;
 		}
 		else {
 			strut[2] = y + h + bw;
 			strut[8] = x;
-			strut[9] = x + w - 1;
+			strut[9] = x + w + (2 * bw) - 1;
 		}

 		XChangeProperty(dpy, wins[i], A_STRUT, XA_CARDINAL, 32, PropModeReplace,
@@ -102,14 +115,84 @@ void create_bars(void)
 	}

 	gc = XCreateGC(dpy, wins[0], 0, NULL);
-	XSetForeground(dpy, gc, fg_col);
-	font = XLoadQueryFont(dpy, BAR_FONT);
+	XSetForeground(dpy, gc, config.foreground_colour);
+	font = XLoadQueryFont(dpy, config.font);
 	if (!font) {
 		errx(1, "could not load font");
 	}
 	XSetFont(dpy, gc, font->fid);
 }

+void draw_bar_content(Window win, int monitor_index)
+{
+	XClearWindow(dpy, win);
+
+	int current_ws = get_current_workspace();
+	int name_count = 0;
+	char **names = get_workspace_name(&name_count);
+
+	unsigned int text_y = (config.height + font->ascent - font->descent) / 2;
+	const int bg_padding = 5;
+	const int workspace_spacing = 10;
+
+	if (names) {
+		unsigned int *positions = malloc(name_count * sizeof(unsigned int));
+		unsigned int *widths = malloc(name_count * sizeof(unsigned int));
+		unsigned int text_x = config.text_padding + bg_padding;
+
+		/* calculate consistent positions and widths */
+		for (int i = 0; i < name_count; ++i) {
+			char padded_label[64];
+			snprintf(padded_label, sizeof(padded_label), " %s ", names[i]);
+
+			int label_width = XTextWidth(font, padded_label, strlen(padded_label));
+
+			positions[i] = text_x;
+			widths[i] = label_width;
+
+			text_x += label_width + workspace_spacing;
+		}
+
+		/* draw backgrounds and text */
+		for (int i = 0; i < name_count; ++i) {
+			char label[64];
+			/* TODO: ADD ABILITY TO CHANGE WORKSPACE STRING */
+			snprintf(label, sizeof(label), " %s ", names[i]);
+
+			if (i == current_ws) {
+				/* draw highlight background for active workspace */
+				/* TODO: ADD OPTION TO CHANGE SPECIFIC HILIGHT COLOUR */
+				XSetForeground(dpy, gc, config.foreground_colour);
+				XFillRectangle(dpy, win, gc, positions[i] - bg_padding,
+				               text_y - font->ascent - bg_padding, widths[i] + (2 * bg_padding),
+				               font->ascent + font->descent + (2 * bg_padding));
+
+				/* TODO: ADD CUSTOM TEXT COLOURS */
+				XSetForeground(dpy, gc, config.background_colour);
+			}
+			else {
+				XSetForeground(dpy, gc, config.foreground_colour);
+			}
+
+			XDrawString(dpy, win, gc, positions[i], text_y, label, strlen(label));
+			free(names[i]);
+		}
+
+		free(positions);
+		free(widths);
+		free(names);
+	}
+
+	XSetForeground(dpy, gc, config.foreground_colour);
+
+	/* use window content width for version string positioning */
+	int version_width = XTextWidth(font, SXBAR_VERSION, strlen(SXBAR_VERSION));
+	int window_width = monitors[monitor_index].width - (2 * config.horizontal_padding);
+	int version_x = window_width - version_width - config.text_padding - bg_padding;
+
+	XDrawString(dpy, win, gc, version_x, text_y, SXBAR_VERSION, strlen(SXBAR_VERSION));
+}
+
 int get_current_workspace(void)
 {
 	Atom actual_type;
@@ -145,7 +228,7 @@ char **get_workspace_name(int *count)
 		char **names = NULL;
 		int n = 0;
 		char *p = (char *)data;
-		while (n < 32 && p < (char *)data + nitems) {
+		while (n < MAX_MONITORS && p < (char *)data + nitems) {
 			names = realloc(names, (n + 1) * sizeof(char *));
 			names[n++] = strdup(p);
 			p += strlen(p) + 1;
@@ -163,56 +246,10 @@ void hdl_dummy(XEvent *xev)
 	(void)xev;
 }

-int find_monitor_for_window(Window win)
-{
-	for (int i = 0; i < nmonitors; i++) {
-		if (wins[i] == win) {
-			return i;
-		}
-	}
-	return 0; /* fallback to first monitor */
-}
-
-void draw_bar_content(Window win, int monitor_index)
-{
-	XClearWindow(dpy, win);
-
-	int current_ws = get_current_workspace();
-	int name_count = 0;
-	char **names = get_workspace_name(&name_count);
-
-	unsigned int text_y = (BAR_HEIGHT + font->ascent - font->descent) / 2;
-
-	unsigned int text_x = BAR_TEXT_PAD;
-	if (names) {
-		for (int i = 0; i < name_count; ++i) {
-			char label[64];
-			if (i == current_ws) {
-				snprintf(label, sizeof(label), "%s%s%s ", BAR_WS_HIGHLIGHT_LEFT, names[i],
-				         BAR_WS_HIGHLIGHT_RIGHT);
-			}
-			else {
-				snprintf(label, sizeof(label), "%s ", names[i]);
-			}
-
-			XDrawString(dpy, win, gc, text_x, text_y, label, strlen(label));
-			text_x += XTextWidth(font, label, strlen(label)) + BAR_WS_SPACING;
-			free(names[i]);
-		}
-		free(names);
-	}
-
-	int bar_width = monitors[monitor_index].width - (2 * BAR_HORI_PAD);
-	int version_width = XTextWidth(font, SXBAR_VERSION, strlen(SXBAR_VERSION));
-	int version_x = bar_width - version_width - BAR_TEXT_PAD;
-
-	XDrawString(dpy, win, gc, version_x, text_y, SXBAR_VERSION, strlen(SXBAR_VERSION));
-}
-
 void hdl_expose(XEvent *xev)
 {
 	Window win = xev->xexpose.window;
-	int monitor_index = find_monitor_for_window(win);
+	int monitor_index = find_window_monitor(win);
 	draw_bar_content(win, monitor_index);
 }

@@ -226,6 +263,32 @@ void hdl_property(XEvent *xev)
 	}
 }

+
+void init_defaults(void)
+{
+	config.bottom_bar = True;
+	config.height = 19;
+	config.vertical_padding = 0;
+	config.horizontal_padding = 0;
+	config.text_padding = 0;
+	config.border = False;
+	config.border_width = 0;
+	config.background_colour = parse_col("#000000");
+	config.foreground_colour = parse_col("#7abccd");
+	config.border_colour = parse_col("#005577");
+	config.font = strdup("fixed");
+}
+
+int find_window_monitor(Window win)
+{
+	for (int i = 0; i < nmonitors; i++) {
+		if (wins[i] == win) {
+			return i;
+		}
+	}
+	return 0; /* fallback to first monitor */
+}
+
 unsigned long parse_col(const char *hex)
 {
 	XColor col;
@@ -262,6 +325,7 @@ void setup(void)
 	root = XDefaultRootWindow(dpy);
 	scr = DefaultScreen(dpy);

+	/* init all events with a dummy */
 	for (int i = 0; i < LASTEvent; ++i) {
 		evtable[i] = hdl_dummy;
 	}
@@ -270,17 +334,17 @@ void setup(void)
 	evtable[PropertyNotify] = hdl_property;

 	XSelectInput(dpy, root, PropertyChangeMask);
-	fg_col = parse_col(BAR_COLOR_FG);
-	bg_col = parse_col(BAR_COLOR_BG);
-	border_col = parse_col(BAR_COLOR_BORDER);
+
+	init_defaults();
 	create_bars();
 }

 int main(int ac, char **av)
 {
 	if (ac > 1) {
-		if (strcmp(av[1], "-v") == 0 || strcmp(av[1], "--version") == 0)
+		if (strcmp(av[1], "-v") == 0 || strcmp(av[1], "--version") == 0) {
 			errx(0, "%s\n%s\n%s", SXBAR_VERSION, SXBAR_AUTHOR, SXBAR_LICINFO);
+		}
 		else {
 			errx(0, "usage:\n[-v || --version]: See the version of sxbar");
 		}
@@ -289,4 +353,4 @@ int main(int ac, char **av)
 	setup();
 	run();
 	return 0;
-}
\ No newline at end of file
+}