foxygit / sxbar Log in
commit c5bf9d703fb01bc8105edfa28a1c420a14d17e3c
Author:     MrJensK <jens.se@icloud.com>
AuthorDate: Mon Aug 10 18:01:52 2026 +0200
Commit:     MrJensK <jens.se@icloud.com>
CommitDate: Mon Aug 10 18:01:52 2026 +0200

    crash fix
---
 src/sxbar.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/src/sxbar.c b/src/sxbar.c
index e3af39f..9995ad8 100644
--- a/src/sxbar.c
+++ b/src/sxbar.c
@@ -65,6 +65,22 @@ int nmonitors = 0;
 int scr;
 static Popup popup;

+/* Windows pulled from _NET_CLIENT_LIST (taskbar, workspace dots, ...) are
+ * routinely destroyed between that snapshot and a follow-up property/attr
+ * query on them -- a normal race, not a bug. Xlib's default error handler
+ * would exit() the whole bar over it; swallow just that race instead and
+ * let callers' existing "did this call fail" checks handle it. */
+static int xerror(Display *d, XErrorEvent *ee)
+{
+	(void)d;
+	if (ee->error_code == BadWindow || ee->error_code == BadDrawable ||
+	    ee->error_code == BadMatch)
+		return 0;
+	warnx("X error: request %d.%d, error code %d",
+	      ee->request_code, ee->minor_code, ee->error_code);
+	return 0;
+}
+
 int window_on_monitor(Window win, int monitor_index) {
     XWindowAttributes attr;
     if (!XGetWindowAttributes(dpy, win, &attr))
@@ -552,6 +568,18 @@ void create_bars(void)
 		}
 	}

+	/* sxwm only re-tiles/recalculates reserved screen space in reaction
+	 * to a _NET_WM_STRUT_PARTIAL PropertyNotify on the root window; it
+	 * doesn't do that on its own when a dock window first maps. Without
+	 * this, windows only reflow around a (re)started bar the next time
+	 * something unrelated happens to trigger a retile. Touch that same
+	 * atom on root ourselves so it happens immediately -- the value is
+	 * irrelevant, sxwm rescans all dock windows itself when it sees this. */
+	Atom A_ROOT_RETILE_KICK = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False);
+	long retile_kick = 0;
+	XChangeProperty(dpy, root, A_ROOT_RETILE_KICK, XA_CARDINAL, 32, PropModeReplace,
+	                (unsigned char *)&retile_kick, 1);
+
 	gc   = XCreateGC(dpy, bars[0].win, 0, NULL);
 	font = XftFontOpenName(dpy, scr, config.font);
 	if (!font)
@@ -1904,6 +1932,7 @@ void setup(void)
 	if (!(dpy = XOpenDisplay(NULL))) {
 		errx(1, "can't open display");
 	}
+	XSetErrorHandler(xerror);
 	root = XDefaultRootWindow(dpy);
 	scr = DefaultScreen(dpy);

@@ -1935,6 +1964,15 @@ int main(int ac, char **av)
 		}
 		errx(1, "usage: sxbar [-v|--version]");
 	}
+
+	/* sxwm spawns us without setsid(), so we start out in its process
+	 * group -- along with every other program it launches. Some apps
+	 * (Electron ones observed in practice) signal their own process
+	 * group on shutdown to clean up child processes, which then takes
+	 * sxbar down as collateral. Detach into our own session so we're
+	 * immune regardless of what else shares that group. */
+	setsid();
+
 	setup();
 	run();