I've often had encounters starting like "Whats that windowmanager you're using?" (that actually happens, yes) in the past that developed into a discussion on how productivity can be increased and how stuff can be configured. I'd like to point out here that this all depends on a lot of things and that (almost) no one will have the same setup, so all blogposts showing how to setup things should be seen as a guideline or more a reference to get an impression on whats possible.

So I want to give you an impression on how my windowmanager is setup so you might get an impression what might fit your usecase and what might not.

The actual windowmanager

My current setup consists of a plain i3wm setup configured to fit my needs, so below, you'll get a list of changes I've make to the i3 config file, so that I'm comfortable.

$mod is my modifiers key, set to the Windows key on my keyboard by default.


I like to be quickly be able to connect my bluetooth headphones, so the combination $mod + b execs the alias bt that connects to my headphones using bluetoothctl:

bindsym $mod+b exec --no-startup-id "bt"
$ alias bt
bt='echo "power on\nagent on\n connect XX:XX:XX:XX:XX:XX\n" | bluetoothctl'

Changing Focus

I've changed the default layout for changing focus from j, k, l, semicolon to h, j, k, l.
The reason the layout is set to such a, one might call it "weird", default, is that the $mod + h modifier is set to change the titling to horizontal. This collides with the what whould have been intuitive combination for changing the focus to the left.
I've solved this for me by setting the change focus key-combinations to the following and remapping the combination for changing the tiling (horizontal/vertical):

bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right

Changing split direction

As described in the paragraph above, changing the split direction collided with changing the focus, so I've changed the combination from $mod+h to $mod+Shift+bar:

# split in horizontal orientation
bindsym $mod+Shift+bar split h

# split in vertical orientation
bindsym $mod+v split v

Workspaces on multiple monitors

I've kept the defaults for using workspaces on one monitor, but made some changes that fit my needs for multiple monitors.
The basic concept for using a single monitor (the internal one on my laptop) is, that workspace 1 can be accessed through $mod+1, the workspace 2 can be accessed through $mod+2 and so on.
When using a second monitor, I've mapped the F-keys to workspaces, these are continously numbered, so the workspace mapped to $mod+F1 is called workspace 11 internally, and displayed as 1F (This is done so that the workspaces are displayed in order).

To make this clearer, heres a bit of config:

# Workspaces on the internal screen
set $ws1 "1"
set $ws2 "2"
set $ws9 "9"
set $ws10 "10"

# Workspaces on external screens
set $ws11 "1F"
set $ws12 "2F"
set $ws19 "9F"
set $ws20 "10F"

Moving focused containers onto the extra F-Key workspaces is done as with the "normal" workspaces using $mod+Shift+F<1-10>:

bindsym $mod+Shift+1 move container to workspace $ws1
bindsym $mod+Shift+2 move container to workspace $ws2
bindsym $mod+Shift+9 move container to workspace $ws9
bindsym $mod+Shift+0 move container to workspace $ws10

bindsym $mod+Shift+F1 move container to workspace $ws11
bindsym $mod+Shift+F2 move container to workspace $ws12
bindsym $mod+Shift+F9 move container to workspace $ws19
bindsym $mod+Shift+F10 move container to workspace $ws20

This works really well with up to three (or 4, I haved tried that yet though) screens when combining this with the "open workspaces on predefined screens" option:

# define on what monitor what workspace gets opened
workspace $ws1  output eDP-1
workspace $ws10  output eDP-1

This defines that the workspaces 1-10 allways get opened on my laptop screen.

Handing external screens is done like this:

workspace $ws11 output HDMI-2 DP-1-1
workspace $ws12 output HDMI-2 DP-1-1
workspace $ws13 output HDMI-2 DP-1-1
workspace $ws14 output HDMI-2 DP-1-1
workspace $ws15 output HDMI-2 DP-1-2
workspace $ws16 output HDMI-2 DP-1-2
workspace $ws17 output HDMI-2 DP-1-2
workspace $ws18 output HDMI-2 DP-1-2
workspace $ws19 output HDMI-2 DP-1-2
workspace $ws20 output HDMI-2 DP-1-2

This means that if I'm connected to a screen via HDMI, all F<1-10> workspaces are displayed on that screen. If I'm connected to external Monitors using Displayport (using my dock), The workspaces F<1-4> get displayed on the first screen and the workspaces F<5-10> get displayed on the second screen (altough I'd actually only use F<5-8>).

I've been using this setup for about two years now and It's been working really well, because I'm totally aware of where each workspace is.

Toggle window border

I somethimes toggle the window borders using $mod+Shift+m, mainly to hide them when watching a movie in a floating window (more on that in the "Sticky windows" section).

# Toggle the sticky mode
bindsym $mod+Shift+m border toggle

Sticky windows

I sometimes have the urge to watch a movie, but also code something at the same time. Setting up a second monitor on which the movie runs primarily is a waste of display space in my opinion, so I've started watching movies in a fairly small part of my screen in a floating mpv window in the bottom right corner of my screen. This works for a single workspace, but the window stays on that workspace, so I've set the combination $mod+Shift+s to toggle the "sticky" state of the window. If this is activated, the window "sticks" to the screen, so it's always there, no matter on what workspace I'm on.

bindsym $mod+Shift+s sticky toggle

Hiding the bar

I configured $alt+m to toggle the bar:

bindsym $alt+m bar mode toggle

Spotify control

Controlling Spotify from whatever workspace I'm on, for example for pausing the current track or skipping to the next track is done using playerctl like this:

# spotify
bindsym $mod+bracketleft exec "playerctl previous"
bindsym $mod+bracketright exec "playerctl next"
bindsym $mod+space exec "playerctl play-pause

I'm using an ANSI Keyboard layout allowing me to easily access the bracketleft ({) as well as the bracketright (}) keys. This might not be the case for you, but you might be able to find two such keys that could be used to play the next or previous track.

Using $mod+space as a way to play or pause the current track has been really useful and has proven itself well.

Default window borders

I've used i3 a long time with window borders enabled, but about a year ago, I realized I didn't really have the need to see the content of the window borders. I actually know that the window is firefox and seeing the page title isn't really critical for most pages, as it is already displayed on the tab.

I've changed the default window border to "pixel", meaning that the border is only one pixel thick:

default_border pixel

This makes it possible to still locate the active window, as it it's border is highlighted, but leaves the place to the content of the window.


I love the feature to follow the mouse focus, so the window my mouse is in is automatically focused:

focus_follows_mouse yes

Another thing I've finally found, is how to make the mouse follow the focus (source). This means that when I change the focus for example to the left using $mod+h, the mouse jumps to the middle of the focused window.

This is done using xdotool, a "command-line X11 automation tool":

$ nix-shell -p xdotool
[nix-shell:~]$ i3-msg -t subscribe -m '[ "window" ]' | while read; do eval $(xdotool getwindowfocus getwindowgeometry --shell); xdotool mousemove $((X + WIDTH / 2)) $((Y + HEIGHT / 2)); done

Split up a bit:

Get a shell with xdotool (you might want to install xdotool if not using nixos):

$ nix-shell -p xdotool

Subscribe to window changes

i3-msg -t subscribe -m '[ "window" ]'

for every change

while read; do eval

get the current window dimensions

$(xdotool getwindowfocus getwindowgeometry --shell);

and move the mouse there

xdotool mousemove $((X + WIDTH / 2)) $((Y + HEIGHT / 2)); done