> Nathan's notes_ Search Rice Projects Reading Root

I3-like multihead in bspwm

28 April 2015

i3 has fantastic focus and window handling for multiple monitors out of the box in my opinion. I didn’t realize that I missed this until I was on campus and used i3 on a multihead setup there, so I set out to make my bspwm setup act the same.

Initial

In the example sxhkd in the bspwm repo, the main window focus keybind is set up as such:

super + {_,shift + }{h,j,k,l}
    bspc node -{f,s} {west,south,north,east}

I’m going to separate this into 2 parts, a focus part and a movement part.

Focus

So taking the focus part out of the above we get:

super + {h,j,k,l}
    bspc node -f {west,south,north,east}

So, This will focus on a window in a direction determined by keypress, which are vi-like here. However, lets say you have 2 monitors, and one is empty:

img

the command bspc node -f east will fail, as there is no window to the right, and no action occurs as a result.

Desired behavior

In the above, on a focus right, I want the cursor to appear in the middle of the right monitor as well as focus on that monitor.

super + {h,j,k,l}
    bspc config pointer_follows_monitor true; \
    bspc config pointer_follows_focus true; \
    dir={west,south,north,east}; \
        if ! bspc node -f $dir; then \
    bspc monitor -f $dir; \
    fi; \
    bspc config pointer_follows_monitor false; \
    bspc config pointer_follows_focus false

When the node right command fails in the above scenario, the monitor to the right will be focused, and the pointer will be there.

Why is half of this function setting pointer_follows_* values?

So let’s say you are moving a floating window with your mouse across the 2 screens - with either of the two pointer options above enabled, when you cross the border the cursor will move, and the window with it. This window snapping out from under you can be quite annoying.

Movement

Movement part of the example sxhkd:

super + shift + {h,j,k,l}
    bspc node -s {west,south,north,east}

Alright, so what this does is switch the currently focused window with another window by direction, although the focus remains on the original window spot, which I’ve found to be quite annoying. It feels more natural to keep focus on the same window and ‘carry’ it around your workspaces/monitors.

Desired behavior

When a window is switched, retain focus on the original window that was moved. If switching to an empty monitor, move the window to that monitor. If switching to an occupied monitor, move the window to that monitor instead of switching windows.

Under this behavior, the keypress super + shift + l would yield the following results:

img img

Here is an implementation:

super + shift + {h,j,k,l}
    bspc config pointer_follows_focus true; \
    cur_win=$(bspc query -W -w) \
    cur_mon=$(bspc query -M -m); \
    dir={west,south,north,east}; \
    if ! bspc node -f $dir; then \
        bspc node -m $dir; \
        bspc monitor -f $dir; \
    else \
        new_mon=$(bspc query -M -m); \
        if [ $new_mon -eq $cur_mon ]; then \
            bspc node -s $cur_win; \
        else \
            bspc node $cur_win -m ^$new_mon; \
        fi; \
    bspc node -f $cur_win; \
    fi; \
    bspc config pointer_follows_focus false

You can see from this I used the same strategy as above - check if the window focus command failed and act accordingly - if it does not fail, then check if we are on a new monitor - if so, move the original window to the new monitor.

wooo.