A StumpWM module for defining dual-monitor window layouts per group
▲ 37 r/stumpwm+2 crossposts

A StumpWM module for defining dual-monitor window layouts per group

I spent way longer than I'd like to admit getting one defcommand to do "make a group, split both monitors top/bottom, launch emacs + firefox + two terminals into the correct frames every time." The annoying part I eventually realized is that run-shell-command is async, so focus frame, launch app, focus next frame, launch next app just lands everything in whichever frame was focused last.

My fix was to register a *new-window-hook*, match each new window by a substring of its WM_CLASS, and pull-window it into the right frame as it appears. Hook removes itself when all the expected windows have shown up. Once that was working I had a ~50-line blob I didn't want to copy-paste for every project, so I pulled it out into a small module: layout-groups.

It exposes one macro, define-layout-group, which takes a command name, a group title, a layout preset, and a list of (slot class-substring launcher) tuples, and expands into a defcommand that does the group/split/hook/launch sequence for you. So a writing setup becomes one form:

(define-layout-group writing "Writing" :tri-2-head-v-r

(:left "emacs" "emacsclient -c -a emacs ~/notes")

(:right-top "firefox" "firefox --no-remote -P writing")

(:right-bottom "zathura" "zathura ~/refs/handbook.pdf"))

(define-key *root-map* (kbd "W") "writing")

Re-running the command after the group exists just switches to it instead of re-splitting.

Comes with 9 presets covering dual/tri/quad layouts across two monitors in every split direction. README has the WM_CLASS gotchas you only learn the hard way (firefox --no-remote -P is the big one; without it the second firefox invocation just opens a tab and no window event ever fires for the hook to catch).

Repo: https://github.com/licjon/stumpwm-layout-groups

Caveats: presets are hardcoded to exactly 2 heads. Module lives in the :stumpwm package directly because (:use :stumpwm) doesn't import the internals you actually need (find-group, head-frames, vsplit, etc.).

u/licjon — 6 days ago