bspwm
has a useful sticky
flag, which keeps the window on whatever the focused desktop
is, basically like using node -d
whenever you change desktops.
for floating windows this works very well, but the situation is more complicated if
you set the sticky
flag for a tiled window.
It works, of course, but since the sticky window is moved away from a desktop when it stops being focused, it loses its location in the desktop's layout, and switching back to that desktop places the sticky tiled window in the location any window would be. This is not ideal if you don't want to constantly reconfigure your layout after switching to a browser or something similar.
There's actually a draft pull request from 2019 on bspwm
's Github that addresses this,
#1032, but patching bspwm to solve this
problem is quite bothersome if you use a binary package. Plus, bspwm
's great advantage
is how extensible and scriptable it is; there must be a way to solve this problem that way!
There absolutely is, but it took a while for me to figure it out. I spent a few hours on this,
getting it to almost work, but be flawed in a way that made it not worth using.
But I finally got it working after emanuele6 helped me
on IRC (thanks!), and now I use it. So, here it is, bsp_movedesk
:
#!/bin/sh
# deal with sticky windows when moving workspaces.
test -z "$1" && exit
# path format: desktop/stickywindow: parent
bspc query -N -n '.local.leaf.!hidden.sticky' | while read -r wid; do
[ "$(bsp_countwindows)" = 1 ] && break
dir="$HOME/run/bspsticky/$(bspc query -D -d focused)"
mkdir -p "$dir"
bspc node "$wid" -i
bspc query -N "$wid" -n '@brother.leaf.!window' > "${dir}/${wid}"
done
bspc desktop -f "$1"
bspc query -N -n '.local.leaf.!hidden.sticky' | while read -r wid; do
dir="$HOME/run/bspsticky/$(bspc query -D -d focused)"
[ "$(bsp_countwindows)" = 1 ] && break
stdir="${dir}/${wid}"
if [ -s "$stdir" ]; then
rece="$(cat "$stdir")"
bspc node "$wid" -n "$rece"
rm "$stdir"
fi
done
# remove hidden window's receptacles. sucks but better than the alternative
while bspc node 'any.local.leaf.!window' -k; do :; done
The script is a wrapper around desktop -f
, and can be used by replacing instances
of bspc desktop -f
with bsp_movedesk
in your sxhkd
config.
It works by reading and writing to a special directory
(on my machine $HOME/run/bspsticky
, kept inside a ramdisk),
which contains directories named with a desktop's ID.
These directories contain files named for a sticky node's ID,
and contain the ID of a receptacle which the window should be
moved to if that desktop is focused.
These receptacles are placed before the focused desktop changes if there
are multiple windows open (because doing this when the sticky window
is the only open window is unnecessary.)
This is the bsp_countwindows
script:
#!/bin/sh
bspc query -N -n '.window.!hidden' -d | wc -l
There are definitely improvements that could be made here, but It Works. the end.
---
here's a completely unrelated script, which swaps a window's parent to its sibling, in case you want that:
#!/bin/sh
# move focused window to sibling
set -e
# possibly causes a race condition if focused changes during script?
focused=$(bspc query -N -n)
if [ "$1" = "2" ]; then
bspc node -f @parent/parent/brother
else
bspc node -f @parent/brother
fi
bspc node -i
bspc node $focused -n $(bspc query -N -n .local.leaf.!window)
bspc node $focused -f