Making i3 windows float without for_window

Published on July 13, 2019 under Blog

i3 configs often include bindings to launch applications. For example, to run Konsole terminal when Super+Enter is pressed, one would add this line to their config:

bindsym --release Mod4+Return exec "konsole"

The standard approach to make windows flaot is to find out the window's class and (optionally) instance name. Running xprop and clicking on the window would output:

$ euql1n@dominator ~> xprop | grep CLASS
WM_CLASS(STRING) = "konsole", "konsole"

Then, one could force the window to float using for_window in their config:

for_window [class="konsole"] floating enable

Problem statement

Consider the situation where you don't want all instances of the application to float. For example, you want a floating terminal window to appear on Super+Shift+Enter key press, and a non-floating on Super+Enter.

Some programs, like urxvt and termite, make this task easy by letting you explicitly specify the instance name. For example, the following lines can be added to the config to get the desired behaviour with termite:

# Setup key bindings
set $mod=Mod4
bindsym --release $mod+Return exec "termite"
bindsym --release $mod+Shift+Return exec "termite --name=floating"

# Make only the `floating` version of termite float
for_window [class="Termite" instance="floating"] floating enable

Unfortunately, this isn't always possible - konsole, for example, doesn't take any command line arguments that define the instance name. What do we do in this case?

Solution

The solution is based on this answer. We create a special wrapper script that will force our apps to float. The script will take our desired command as an argument, and will execute it making sure the resultant window is set to float.

You can download the float-runner.sh from this gist, or simply create file called float-runner.sh and put this inside:

#!/usr/bin/env bash

# Get the command from arguments
arg_str="$*"
$arg_str &
pid="$!"

# Wait for the window to open and grab its window ID
winid=''
while : ; do
    winid="`wmctrl -lp | awk -vpid=$pid '$3==pid {print $1; exit}'`"
    [[ -z "${winid}" ]] || break
done

# Focus the window we found
wmctrl -ia "${winid}"

# Make it float
i3-msg floating enable > /dev/null;

# Move it to the center for good measure
i3-msg move position center > /dev/null;

# Wait for the application to quit
wait "${pid}";

I put this script into ~/.config/i3/ directory, but you can put it wherever you want. You might also want to make sure wmctrl is installed before running it.

Finally, wrap relevant command in your i3 config with this script. For example, to get the behaviour described above with konsole, one would write:

set $mod=Mod4
bindsym --release $mod+Return exec "konsole"
bindsym --release $mod+Shift+Return exec "bash ~/.config/i3/float-runner.sh konsole"

And voila, konsole will float whenever you start it using Super+Shift+Enter.


End of Article

Timur Kuzhagaliyev Author

I'm a computer science graduate from UCL & Caltech, working as a systems engineer at Jump Trading. Before Jump, I was doing computer vision for Video Quality Analysis team at Amazon.