I am a huge fun of keyboard shortcuts. Every time I do something with my mouse and I do it often, I look for a way to handle it with hotkeys. A definition of great software for me is something, that can be used - within limits of UX of course - only with key presses and key combos. Ctrl+C and Ctrl+V are huge time savers, but there are many, many more that I use daily. One of the strongest sides of Linux for me is that a lot of software that is created, is created with this power of keyboards in mind. If you’re only willing to learn those, you can do such much with so little effort.

When I still used Windows, it had this concept of switching windows not only with Alt+Tab, but also the Super1+{1..9} hotkeys to switch to apps that are opened or pinned to the taskbar.

Let’s say that you have Firefox as the first app and Explorer as the second one. If you use Super+1 in this situation, it will launch Firefox. If you then use Super+2, it will bring Explorer back. This is cool and I liked using it, but one thing was maddening to me - if you have a couple of Explorer windows, using that key combo always brings up the first one. If you want to view the second, you need to use it twice, while keeping the Super key pressed (so: press Super and 2, release 2 and press 2 again). Cycling through windows was unnecessary hard.

Then I switched to GNOME 3. While this feature wasn’t there out of the box, I have found an extension to simulate it. It worked great, and, unlike on Windows, it cycled through multiple windows of the same app correctly.

Unfortunately, when I switched to i3 Window Manager, this functionality was missing. This is due to the whole idea of managing windows being different in i3 than in fully featured desktop environments. The Super+{1..9} combo by default switches workspaces, not windows, and there is no feature like a dock or a taskbar.

For some time I used this windows manager like it was; some of my old habits died really fast due to the whole part of managing windows was much easier in environment like that. I previously used keyboard shortcuts to bring up full-screen windows like a music player, a code editor or a messaging app; now I had full on spaces where those windows resided. This more or less translated into my own usage habits.

But there were some habits that didn’t die out; on my daily usage I manage a couple of browser windows, a few terminal emulators and other programs that I instantiate multiple times. Sometimes those instances are being shown on the same workspace, thus not allowing me to focus them with a quick key combo that switches workspaces. So some time ago I decided it was time to start scripting.

I knew that i3 has i3-msg - a programmatic interface for the whole window manager, so I started thinking about how I can code this feature myself and what exactly I wanted to accomplish; after I start up my system and start a few programs, I rarely do stuff like launching a new browser window or things like that, so I decided that just switching to already launched apps would be sufficient. I’ve spent a few hours and with a bit of jq magic I managed to get it working. The script itself is quite easy: it gets from i3-msg the list of all opened windows, then in that list it looks for the specified app in the specified direction. It supports look for both next and previous window of the specified instance (hence direction).

I then added this to my config:

set $i3_scripts_dir /home/kamil/tools/i3-scripts

bindsym $mod+a exec notify-send "bindings" "f - firefox\nmod+f - file manager\nc - VSC\nt - thunderbird\nmod+t - terminal\ns - slack\nd - discord"; mode "app-switcher"
mode "app-switcher" {
    bindsym f exec $i3_scripts_dir/switcher.sh firefox; mode "default";
    bindsym Shift+f exec $i3_scripts_dir/switcher.sh firefox previous; mode "default";

    # f stands for [f]ile manager
    bindsym $mod+f exec $i3_scripts_dir/switcher.sh Pcmanfm; mode "default";
    bindsym Shift+$mod+f exec $i3_scripts_dir/switcher.sh Pcmanfm previous; mode "default";

    bindsym c exec $i3_scripts_dir/switcher.sh code-oss; mode "default";
    bindsym Shift+c exec $i3_scripts_dir/switcher.sh code-oss previous; mode "default";

    bindsym t exec $i3_scripts_dir/switcher.sh Thunderbird; mode "default";
    bindsym Shift+t exec $i3_scripts_dir/switcher.sh Thunderbird previous; mode "default";

    # t stands for [t]erminal
    bindsym $mod+t exec $i3_scripts_dir/switcher.sh Alacritty; mode "default";
    bindsym Shift+$mod+t exec $i3_scripts_dir/switcher.sh Alacritty previous; mode "default";

    bindsym s exec $i3_scripts_dir/switcher.sh Slack; mode "default";
    bindsym Shift+s exec $i3_scripts_dir/switcher.sh Slack previous; mode "default";

    bindsym d exec $i3_scripts_dir/switcher.sh discord; mode "default";
    bindsym Shift+d exec $i3_scripts_dir/switcher.sh discord previous; mode "default";

    bindsym Return mode "default"
    bindsym Escape mode "default"
    bindsym $mod+a mode "default"
}

When I use Super+a it shows me a neat notification containing the list of keybinds that I have set and then enters the special mode where I can use simple key combos to switch to specific apps. If I enter this mode by accident (which doesn’t happen often, but it’s good to have a way out) I can use Return, Escape or Super+a to escape it.


  1. This is what you may call a Windows key, which is, well, specific to Windows, although the key itself works in different OSes. I prefer to call it Super, since it doesn’t correspond with a specific OS.↩︎

Tags: english technical programming shell bash i3wm i3-scripts

Posted on: 2021-05-07, last edited on: 2021-11-11, written by: Kavelach