New laptop part 3: i3wm configuration

Thursday, January 19, 2023

laptopframeworki3wm

Introduction

Continuing the framework laptop blog post series, where I first wrote about my impression of the framework laptop, then about what I had to do to fix EndeavourOS / Archlinux on it and lastely about the installation and configuration of some “core” softwares. This post is about my i3wm configuration that I have redone from scratch, heavily inspired by EndeavourOS one.

The TLDR; as usual, is look at my dotfiles :)

Mandatory screenshot:

Figure 1: My desktop screenshot

Figure 1: My desktop screenshot

Be careful, this post is very long due to covering many topics. For this reason, I’m adding for the first time a table of content for this post:

Configuring i3wm

The basics

The basic settings, with comments that shouldn’t need more explaination:

# set the mod key to the winkey:
set $mod Mod4
# default i3 tiling mode:
workspace_layout default
# Back and forth between workspaces
workspace_auto_back_and_forth yes

#border indicator on windows:
new_window pixel 1

# Set inner/outer gaps
gaps inner 3
gaps outer 2

# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod

Workspaces

Workspaces naming convention

First, let’s define the different workspaces. I have both “personal” and “work” related workspace. To visualy separate them, I add “🌢” in front of the “name” of each work related workspace (yes there is a connection^^).

I use 10 workspaces each, they are the same: browsers (web and gemini, in tabbed mode), “terminal”, emails, chats, files, 7, 8, 9, 10.

## Personal workspaces
set $ws1 "1:"
set $ws2 "2:"
set $ws3 "3:✉️"
set $ws4 "4:"
set $ws5 "5:"
set $ws6 "6:"
set $ws7 "7:7"
set $ws8 "8"
set $ws9 "9"
set $ws10 "10"
## Work workspaces
set $wsw1 "11:🌢"
set $wsw2 "12:🌢"
set $wsw3 "13:🌢✉️"
set $wsw4 "14:🌢"
set $wsw5 "15:🌢"
set $wsw6 "16:🌢"
set $wsw7 "17:🌢7"
set $wsw8 "18:🌢8"
set $wsw9 "19:🌢9"
set $wsw10 "20:🌢10"

Assigning workspaces to screens

To assign workspace to specific screens, it is easy:

workspace $ws1 output DP1
workspace $ws2 output DP2-2
workspace $ws3 output DP2-2
workspace $ws4 output eDP1
workspace $wsw1 output DP1
workspace $wsw2 output DP2-2
workspace $wsw3 output DP1
workspace $wsw4 output DP2-1

Switching between workspaces

Of course, naming is fine, but more important is to switch between them. To switch between personal workspaces, I’m using the window ($mod) key and a number key. To switch between work workspaces, I’m using the window ($mod) key + Alt (Mod1) key + a number key.

# switch to workspace
## Personal workspaces
bindsym $mod+1    workspace  $ws1
bindsym $mod+2    workspace  $ws2
bindsym $mod+3    workspace  $ws3
bindsym $mod+4    workspace  $ws4
bindsym $mod+5    workspace  $ws5
bindsym $mod+6    workspace  $ws6
bindsym $mod+7    workspace  $ws7
bindsym $mod+8    workspace  $ws8
bindsym $mod+9    workspace  $ws9
bindsym $mod+0    workspace  $ws10
## Work workspaces
bindsym $mod+Mod1+1    workspace  $wsw1
bindsym $mod+Mod1+2    workspace  $wsw2
bindsym $mod+Mod1+3    workspace  $wsw3
bindsym $mod+Mod1+4    workspace  $wsw4
bindsym $mod+Mod1+5    workspace  $wsw5
bindsym $mod+Mod1+6    workspace  $wsw6
bindsym $mod+Mod1+7    workspace  $wsw7
bindsym $mod+Mod1+8    workspace  $wsw8
bindsym $mod+Mod1+9    workspace  $wsw9
bindsym $mod+Mod1+0    workspace  $wsw10

Moving workspaces around

To move workspaces between screens (outputs), window ($mod) + shift + Left/Right/Up/Down:

# Move workspaces to output
bindsym $mod+Shift+Mod1+Left move workspace to output left
bindsym $mod+Shift+Mod1+Right move workspace to output right
bindsym $mod+Shift+Mod1+Up move workspace to output up
bindsym $mod+Shift+Mod1+Down move workspace to output down

Switch between workspace

To replicate the usual “alt+tab” but for workspaces, I use window + tab (with shift to go backward).

# switch/iterate between workspaces
bindsym $mod+Tab workspace next
bindsym $mod+Shift+Tab workspace prev

Renaming workspace

I very often rename workspace, specially for work related workspace, I usually rename them with a customer or specific project name. For this I’m using window + x to rename personal workspace (ask for name and use it as is) and window + alt + x to rename a work related workspace (use the name with “🌢” before).

# rename workspace
bindsym $mod+x exec "i3-input -F 'move container to workspace %s; workspace %s' -P 'Rename workspace to: '"
bindsym $mod+Mod1+x exec "i3-input -F 'move container workspace 🌢%s; workspace 🌢%s' -P 'Rename workspace to: 🌢'"

Open new empty workspace

In case all workspace are taken (never really happens…), I use window + shift + n to open a new empty one using a script called empty_workspace. The script came with the i3wm configuration of EndeavourOS, thanks to them!

# open new empty workspace
bindsym $mod+Shift+n exec ~/.config/i3/scripts/empty_workspace

The empty_workspace script:

#!/usr/bin/env bash
MAX_DESKTOPS=20
WORKSPACES=$(seq -s '\n' 1 1 ${MAX_DESKTOPS})
EMPTY_WORKSPACE=$( (i3-msg -t get_workspaces | tr ',' '\n' | grep num | awk -F:  '{print int($2)}' ; \
            echo -e ${WORKSPACES} ) | sort -n | uniq -u | head -n 1)

i3-msg workspace ${EMPTY_WORKSPACE}

Containers / Window

Changing focus between windows

Using window + Left/Up/Right/Down keys:

# change focus
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right

Also, for the usual “alt+tab” behaviour:

# alt tab old school style
bindsym Mod1+Tab focus right
bindsym Mod1+Shift+Tab focus left

For focusing the parent container:

# focus the parent container
bindsym $mod+a focus parent

Moving to the latest “urgent” window

# urgent
bindsym $mod+Escape [urgent=latest] focus

Move focused window around

window ($mod) + shift + a Left/Up/Down/Right to windows around.

# move focused window
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right

Moving focused containers (windows) between workspaces

Easy, window ($mod) + shift + a key number to move to a personal workspace. window ($mod) + alt (Mod1) + shift + a key number to move to a work workspace.

# move focused container to workspace
## Personal workspaces
bindsym $mod+Shift+1    move container to workspace  $ws1
bindsym $mod+Shift+2    move container to workspace  $ws2
bindsym $mod+Shift+3    move container to workspace  $ws3
bindsym $mod+Shift+4    move container to workspace  $ws4
bindsym $mod+Shift+5    move container to workspace  $ws5
bindsym $mod+Shift+6    move container to workspace  $ws6
bindsym $mod+Shift+7    move container to workspace  $ws7
bindsym $mod+Shift+8    move container to workspace  $ws8
bindsym $mod+Shift+9    move container to workspace  $ws9
bindsym $mod+Shift+0    move container to workspace  $ws10
## Work workspaces
bindsym $mod+Mod1+Shift+1    move container to workspace  $wsw1
bindsym $mod+Mod1+Shift+2    move container to workspace  $wsw2
bindsym $mod+Mod1+Shift+3    move container to workspace  $wsw3
bindsym $mod+Mod1+Shift+4    move container to workspace  $wsw4
bindsym $mod+Mod1+Shift+5    move container to workspace  $wsw5
bindsym $mod+Mod1+Shift+6    move container to workspace  $wsw6
bindsym $mod+Mod1+Shift+7    move container to workspace  $wsw7
bindsym $mod+Mod1+Shift+8    move container to workspace  $wsw8
bindsym $mod+Mod1+Shift+9    move container to workspace  $wsw9
bindsym $mod+Mod1+Shift+0    move container to workspace  $wsw10

Floating and resizing

Toggle tiling / floating of a window:

# toggle tiling / floating
bindsym $mod+Shift+f floating toggle

Switch focus between tiling and floating windows:

# change focus between tiling / floating windows
bindsym $mod+space focus mode_toggle

Resizing is done via window + shift + s and then arrow keys or h/j/k/l:

# resize window (you can also use the mouse for that):
mode "resize" {
# These bindings trigger as soon as you enter the resize mode
# Pressing left will shrink the window's width.
# Pressing right will grow the window's width.
# Pressing up will shrink the window's height.
# Pressing down will grow the window's height.
  bindsym j resize shrink width 10 px or 10 ppt
  bindsym k resize grow height 10 px or 10 ppt
  bindsym l resize shrink height 10 px or 10 ppt
  bindsym h resize grow width 10 px or 10 ppt

# same bindings, but for the arrow keys
  bindsym Left resize shrink width 10 px or 10 ppt
  bindsym Down resize grow height 10 px or 10 ppt
  bindsym Up resize shrink height 10 px or 10 ppt
  bindsym Right resize grow width 10 px or 10 ppt

# back to normal: Enter or Escape
  bindsym Return mode "default"
  bindsym Escape mode "default"
}
bindsym $mod+Shift+s mode "resize"

Scratchpad

From time to time, I like putting windows as scratchpad windows, for this I use window (+ shift) + ²:

# scratchpad
bindsym $mod+Shift+twosuperior move scratchpad
bindsym $mod+twosuperior scratchpad show

Layouts

Spliting layout

I like creating a horizontal split with window + v and a vertical split with window + h. It’s the opposite of the default, but it makes more sense for me^^.

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

Workspace layout

Layout selection is done via window + s (stacking), window + z (tabbed) or window + e (split toggle):

# change container layout (stacked, tabbed, toggle split)
bindsym $mod+s layout stacking
bindsym $mod+z layout tabbed
bindsym $mod+e layout toggle split

i3bar

config

Basically, having the i3bar at the top of each screen. System tray displayed on the primary screen (in my case the central one). For the status part, I use i3blocks (see next paragraph).

bar {
    font pango: Noto Sans Regular 10
    status_command i3blocks -c ~/.config/i3/i3blocks.conf
    position top
    tray_output primary
    tray_padding 0
    strip_workspace_numbers yes
}

i3blocks

My status looks like this:

Figure 2: Screenshots of i3 status with i3blocks

Figure 2: Screenshots of i3 status with i3blocks

This example includes the system tray, which represent the last 4 icons, from right to left: syncthing, redshift, nm-applet and blueman-applet.

I’m using mostly default stuff from i3blocks, except 2 custom scripts:

  • One for checking I’ve made recent backup (more on that later in a blog post series about backup with borg)
  • One for toggling notification, see below.
[dunst]
command=~/workspace/contrib/i3blocks-contrib/dunst/dunst
format=json
interval=-1

The ~/workspace/contrib/i3blocks-contrib/dunst/dunst comes from i3blocks-contrib and can be found here.

QoL improvements with rofi

All my rofi configuration can be found on sourcehut

Application launcher

To launch application, I start the launcher with window + d:

Figure 3: Screenshots of the application launcher

Figure 3: Screenshots of the application launcher

bindsym $mod+d exec rofi -modi drun -show drun -config ~/.config/rofi/rofidmenu.rasi

Window selector

Open a window listing all windows (in any workspace) with filter to quickly jump to it. Example:

Figure 4: Screenshots of the window selector

Figure 4: Screenshots of the window selector

bindsym $mod+t exec rofi -show window -config ~/.config/rofi/rofidmenu.rasi

Workspace selector

Open a window listing all workspaces with filter to quickly jump to it. Example:

Figure 5: Screenshots of the workspace selector

Figure 5: Screenshots of the workspace selector

# keybinding in fancy rofi (automated):
bindsym $mod+mod1+Tab exec ~/.config/i3/scripts/keyhint-2

Clipboard

Figure 6: Screenshots of the clipboard selector

Figure 6: Screenshots of the clipboard selector

bindsym $mod+c exec --no-startup-id rofi -modi "clipboard:greenclip print" -show clipboard -config ~/.config/rofi/rofidmenu.rasi

Password store integration

Figure 7: Screenshots of the password selector

Figure 7: Screenshots of the password selector

Configuration or rofi-pass:

mkdir ~/.config/rofi-pass && cp /etc/rofi-pass.conf ~/.config/rofi-pass/config

And i3wm configuration:

bindsym $mod+p exec /usr/bin/rofi-pass

Power menu

The power menu allow easy select to hibernate, suspend, lock, logout, reboot or shutdown:

Figure 8: Screenshots of the power menu

Figure 8: Screenshots of the power menu

bindsym $mod+Shift+e exec ~/.config/i3/scripts/powermenu

Power profiles switcher

The power profiles menu allow easy select different power profiles: performance, balanced or power saver:

Figure 9: Screenshots of the power menu

Figure 9: Screenshots of the power menu

bindsym $mod+Shift+p exec ~/.config/i3/scripts/power-profiles

Some keybinds

Audio specific keybind:

# volume
bindsym XF86AudioRaiseVolume exec amixer -D pulse sset Master 5%+ && pkill -RTMIN+1 i3blocks
bindsym XF86AudioLowerVolume exec amixer -D pulse sset Master 5%- && pkill -RTMIN+1 i3blocks

# gradular volume control
bindsym $mod+XF86AudioRaiseVolume exec amixer -D pulse sset Master 1%+ && pkill -RTMIN+1 i3blocks
bindsym $mod+XF86AudioLowerVolume exec amixer -D pulse sset Master 1%- && pkill -RTMIN+1 i3blocks

# mute
bindsym XF86AudioMute exec amixer sset Master toggle && killall -USR1 i3blocks

# audio control
bindsym XF86AudioPlay exec playerctl play
bindsym XF86AudioPause exec playerctl pause
bindsym XF86AudioNext exec playerctl next
bindsym XF86AudioPrev exec playerctl previous

Backlight control

Remember to fix the backlight control as described in the previous post.

# Backlight control
bindsym XF86MonBrightnessUp exec xbacklight +5 && notify-send "Brightness - $(xbacklight -get | cut -d '.' -f 1)%"
bindsym XF86MonBrightnessDown exec xbacklight -5 && notify-send "Brightness - $(xbacklight -get | cut -d '.' -f 1)%"

Screenshots

3 possibility to take screenshots with scrot: Full screenshots (all screens) with Print Screen, screenshot of the focused window with window + Print Screen or a screenshot of a manually selected area with shift + Print Screen (and then just click and select the area to screenshot):

bindsym Print exec scrot ~/Téléchargements/screenshots/%Y-%m-%d-%T-screenshot.png && notify-send "Screenshot saved"
bindsym $mod+Print exec scrot --focus ~/Téléchargements/screenshots/%Y-%m-%d-%T-screenshot.png && notify-send "Screenshot saved"
bindsym --release Shift+Print exec scrot --select ~/Téléchargements/screenshots/%Y-%m-%d-%T-screenshot.png && notify-send "Screenshot saved"

Applications

Open app via Keybinds

Just giving an example, it’s that easy:

bindsym $mod+w exec /usr/bin/firefox-nightly -P bacardi55

Bind app to workspaces

I have some app being binded to specific workspace:

# bind program to workspace and focus to them on startup:
assign [class="(?i)firefox-nightly"] $ws1
assign [class="(?i)firefox"] $wsw1
assign [class="Thunar"] $ws5
assign [class="Emacs"] $ws2

# automatic set focus new window if it opens on another workspace than the current:
for_window [class=Xfce4-terminal] focus
for_window [class=(?i)firefox] focus
for_window [class=Thunar] focus

Floating rules

For some specific windows, I want them to be automatically floating:

for_window [class="Galculator" instance="galculator"] floating enable
for_window [class="Pavucontrol" instance="pavucontrol"] floating enable
for_window [window_role="About"] floating enable

Others

There are other things in my config, such as autostarted programs, theming (will be part of another post regarding the global “theming” of my laptop) or keybind to open window (self explanatory based on the documention). To have a full view, feel free to read the full configuration file on source hut.

Conclusion


Contact

If you find any issue or have any question about this article, feel free to reach out to me via email, mastodon, matrix or even IRC, see the About Me page for details.

See Also

Headless installation of RaspberryPiOS

New laptop part 2: Core tools