Running wmsetbg to set a background from a menu entry (or directly from the command line) would intermittently fail with:
X connection to :0 broken (explicit kill or server shutdown).
The root cause is in setPixmapProperty(). To free the previous background pixmap stored in _XROOTPMAP_ID, the code called XKillClient(dpy, old_pixmap_id). This works when the pixmap belongs to a dead client that had set RetainPermanent — the server just frees those orphanedresources.
The problem is that the X server reuses client IDs. The previous invocation of wmsetbg created the pixmap via a short-lived second connection (tmpDpy, client ID N) and stored that pixmap ID in _XROOTPMAP_ID. After that connection closed, the server eventually recycled client ID N for a new client. On the next invocation of wmsetbg, the new dpy connection might itself receive client ID N, making its first X resource share the same ID as the stored pixmap. When XKillClient(dpy, stored_id) is then called, the server kills wmsetbg's own connection — hence the IO error.
The fix is to switch duplicatePixmap() from RetainPermanent to RetainTemporary. With RetainTemporary, the background pixmap remains alive for the duration of the X session (as long as any client is connected) and is freed automatically at session end. This removes the need tocall XKillClient() to reclaim the previous pixmap entirely.
Secondary cleanups made possible by this change:
- dummyErrorHandler() was only used to suppress errors from XKillClient(); it is now removed.
- The mode variable toggled between PropModeReplace and PropModeAppend depending on whether an old pixmap existed; since we always want to replace, PropModeReplace is used unconditionally.
- A missing XFree(data) after XGetWindowProperty() is added to fix a small memory leak.
Tested on a dual-monitor setup (Xinerama active, 2560×1440 + 1920×1200) where the crash was 100% reproducible before the fix.