How to Bind Shortcut Keys to Commands in i3

September 14, 2023

If you're a Linux enthusiast, you probably enjoy customizing your system to work exactly the way you want it to. One way to enhance your Linux experience is by binding commands or scripts to shortcut keys. If you have read my articles on dunst/notify-send and changing brightness on a Linux laptop, you might remember that I talked about binding commands/scripts to shortcut keys. In this article, I will delve deeper into how to do it.

How to Find Standard Names of Keys on Your Keyboard

Before we dive into the process of binding keys in i3, it's essential to understand how Linux recognizes keys. Linux uses standard names for keys, which can vary depending on your keyboard layout and configuration. Here are two methods to discover these key names:

Method 1: Using Xmodmap

Xmodmap is a utility that allows you to view and modify key mappings on your system. To install it on your system, use the following command based on your Linux distribution:

Debian/Ubuntu/Raspbian/Kali Linux:

apt-get install x11-xserver-utils

Arch Linux/Manjaro:

pacman -S xorg-xmodmap

CentOS:

yum install xorg-xmodmap

Fedora:

dnf install xorg-xmodmap

Once installed, you can use the following command to list modifier keys (Modifier keys are keys that are pressed in conjunction with other keys. For example, Shift, Control, Windows, Alt, etc.):

[ajay@legion ~]$ xmodmap -pm

 xmodmap to print modifier keys

Figure 1: xmodmap to print modifier keys

This command will display a list of modifier keys like Super_L for the left Windows key and Alt_L for the left Alt key. You can use these key names when binding commands in i3.

To find the names of other keys, such as alphanumeric keys, use the command:

[ajay@legion ~]$ xmodmap -pke

figure 2: use xmodmap to print key names; use grep to filter it

In the output, keycodes are represented by numbers, and keysyms are the associated key names.

For more details on how to remap keys using Xmodmap, refer to our Xmodmap article.

Method 2: Using Xev

Another method to discover key names is by using the xev utility. Install it if you haven't already, and then run the following command:

[ajay@legion ~]$ xev

With xev running, press any key on your keyboard, and it will display the name of the key as you press it.

use xev to print key names

figure 3: use xev to print key names

Other Standard Keys

While Xev can help identify most keys, it may not recognize some special function keys like brightness controls. Here are some standard keys that might be useful for custom bindings:

  • XF86Display: the monitor button

  • XF86WLAN: WLAN toggle button

  • XF86TouchpadToggle: touchpad on-off toggle button

  • XF86AudioMute: audio mute button

  • XF86AudioLowerVolume: volume down button

  • XF86AudioRaiseVolume: Volume up button

  • XF86MonBrightnessDown: brightness down button

For a comprehensive list of XF86* keys, refer to this LinuxQuestions page.

the top row keyboards like brightness control might not be recognised by xev.

Figure 4: the top row keyboards like brightness control might not be recognised by xev.

Bind Keys in i3

Keys in i3 are bound by appending them in i3's configuration files ~/home/ajay/.config/i3/config

In i3, you can bind keys using both keycodes and keysyms. Here's how you can use them.

💡Recommendation: If you frequently switch between keyboard layouts (e.g., US, RU) but prefer to maintain consistent key bindings in their physical positions on the keyboard, consider using keycodes. However, if you don't frequently switch layouts and prefer a more readable configuration file, opt for keysyms.

Bind Using Keysyms in i3

To bind keys using keysyms in i3, use the following syntax:

bindsym [--release] [<Group>+][<Modifiers>+]<keysym> command

The items in square brackets are optional.

  • --release: Some commands may only work when the shortcut keys are released. Use this option if needed. Examples of such commands in my case include the screenshot tool imagemagick'simport`. Use it if your command does not work without it.

  • [<Group>+]: You can specify groups as well (Group1, Group2, Group3, Group4) for different keyboard layouts. This way i3 will make sure that these keybindings will be active only in a certain layout. If you omit this, the shortcut keybinding will be active for all layouts. To learn more, read the official i3 documentation.

  • [<Modifiers>+]: Available modifiers include Mod1-Mod5, Shift, and Control.

  • <keysym>: The key name, such as 'a' or 'b'.

  • command: For example, exec ... or gaps inner current plus 1 in the i3 tiling window manager. I will talk more about this in my other article. Just know that for your script, it would be …

For example, the following binds the brightness key in my laptop's i3:

bindsym XF86MonBrightnessUp exec brightness.sh -bu

In the above example, exec brightness.sh -bu is the command (actually, my script is brightness.sh -bu, exec is i3's tool which runs any script).

The XF86MonBrightnessUp is the brightness up key.

Other Examples:

bindsym Mod4+w exec floating_terminal.sh -b "window_switch.sh"
bindsym Mod4+Tab workspace next
bindsym Mod4+shift+d exec floating_terminal.sh -h 35 -w 130 -b "launcher.sh -b"

Notice the key combo Mod4+shift+d, Mod4+Tab, Mod4+w.

Bind Using Keycodes in i3

Everything is the same except you need to replace bindsym with bindcode. Use the keycode instead of keysym.

bindcode Mod4+42 gaps inner current plus 1

In this example, 42 is the keycode corresponding to 'g'.

The gaps inner current plus 1 is the command.

The shortcut keys are Mod4+42, i.e., Super_L + g.

Bind Using Binding Modes in i3

Binding modes are a handy feature, especially when you find yourself running out of available keys on your keyboard. These modes serve as a helpful reminder of which key you need to press, simplifying your navigation and workflow.

Syntax:

mode <name> {
    <keybinding1>
    <keybinding2>
    <keybinding3>
    ...

    bindsym Escape mode "default"
    bindsym Return mode "default"
}
bindsym Mod4+p mode <name>

Example:

mode program {
    bindsym b exec qbittorrent, mode "default", workspace $ws8
    bindsym f exec firefox, mode "default", workspace $ws2

    bindsym Escape mode "default"
    bindsym Return mode "default"
}

bindsym Mod4+p mode program

A slightly more complex example would be:

# Launcher
set $mode_launcher Launch: \
[w]mfocus \
[g]oogle \
q[b]ittorrent \
[r]anger \
[p]cmanfm \
[n]ewsboat

mode "$mode_launcher" {
    bindsym w exec wmfocus, mode "default"
    bindsym g exec google-chrome-stable, mode "default", workspace $ws2
    bindsym b exec qbittorrent, mode "default", workspace $ws8
    bindsym r exec kitty -1 --name ranger ranger, mode "default", workspace $ws9
    bindsym n exec kitty -1 --title newsboat --name newsboat newsboat, mode "default", workspace $ws7
    bindsym p exec pcmanfm, mode "default", workspace $ws9

    bindsym Escape mode "default"
    bindsym Return mode "default"
}
bindsym Mod4+p mode "$mode_launcher"

# DPI Change
set $mode_dpi Scale: 1. 100%, 2. 110%, 3. 120%, 4. 130%. Kill everything and log out.
mode "$mode_dpi" {
    bindsym 1 exec echo "Xft.dpi: 96" > ~/.Xresources.d/dpi.Xresources, mode "default"
    bindsym 2 exec echo "Xft.dpi: 106" > ~/.Xresources.d/dpi.Xresources, mode "default"
    bindsym 3 exec echo "Xft.dpi: 115" > ~/.Xresources.d/dpi.Xresources, mode "default"
    bindsym 4 exec echo "Xft.dpi: 125" > ~/.Xresources.d/dpi.Xresources, mode "default"

    bindsym Return mode "default"
    bindsym Escape mode "default"
}
bindsym Mod4+r mode "$mode_dpi"

# Power Button Mode
set $mode_system System: (k)ill all, (l)ock, log(o)ut, (r)eboot, (s)uspend, shut(d)own (w)indows
mode "$mode_system" {
    bindsym k exec power_menu.sh -k, mode "default"
    bindsym l exec power_menu.sh -l, mode "default"
    bindsym o exec power_menu.sh -o, mode "default"
    bindsym s exec power_menu.sh -s, mode "default"
    bindsym r exec power_menu.sh -r, mode "default" 
    bindsym d exec power_menu.sh -S, mode "default" 
    bindsym w exec power_menu.sh -w, mode "default" 

    bindsym Return mode "default"
    bindsym Escape mode "default"
}
bindsym Mod4+Escape mode "$mode_system"

📔 Please note that in the above example:

  1. set $mode_launcher <any_text> in i3 is used to define a string variable mode_launcher with its value being the any_text. Now, you can use the variable to refer to the text. It is more convenient to use this instead of whole text again and again.

  2. The name is each mode is created intellegently to remind you which key you need to press. I have put these keys into brackets.

  3. There are three modes defined - for launching programs, DPI change, and power modes. You can have as many modes as you want.

  4. To use it, press a shortcut key corresponding to its mode (Mod4+p or Mod4+r or Mod4+Escape). It will show the corresponding mode in your computer screen (figure 5).

  5. Bindings for Return and Escape buttons are necessary. When you press Return/Escape directly without pressing any key in a mode, you will exit the mode, and nothing will happen.

  6. Now, press the key hinted in the brackets. For example, pressing Mod+p and then g opens Google Chrome, and if you press Escape in the mode, nothing will be opened.

  7. The bachslash, like any other programming language/configuration, is for continuation of the previous line.

  8. mode "default" ensures that after launching the programme or running the command, the bindings modes are removed from your screen. That's why they are bound to Escape, Return and other keys.

binding modes in i3

figure 5: binding modes in i3; I have cropped the image because it is too big to fit in this window.

You can also use Pango markup to beautify these modes. Use the flag --pango_markup as shown below:

set $mode_launcher Launch: \
<b>w</b>mfocus \
<b>g</b>oogle \
q<b>b</b>ittorrent \
<b>r</b>anger \
<b>p</b>cmanfm \
<b>n</b>ewsboat \
<b>i</b>3_keys

mode --pango_markup "$mode_launcher" {
    bindsym w exec wmfocus, mode "default"
    bindsym g exec google-chrome-stable, mode "default", workspace $ws2
    bindsym b exec qbittorrent, mode "default", workspace $ws8
    bindsym r exec kitty -1 --name ranger ranger, mode "default", workspace $ws9
    bindsym p exec pcmanfm, mode "default", workspace $ws9
    bindsym n exec kitty -1 --title newsboat --name newsboat newsboat, mode "default", workspace $ws7

    bindsym Escape mode "default"
    bindsym Return mode "default"
}
bindsym Mod4+p mode "$mode_launcher"

binding modes with pango markup in i3

figure 6: binding modes with pango markup in i3; I have cropped the image because it is too big to fit in this window.

In the above examples, the characters w, g, p, r, n will be bolded instead of bracketed. To learn about underscores, superscript, strikethrough, and other Pango markup, look over here.

Conclusion

Customizing your Linux system with shortcut keybindings can greatly improve your workflow and productivity. Whether you prefer using keysyms or keycodes, i3 provides a flexible environment to set up your custom shortcuts. And binding modes are something which I like the most. That's all, folks. Thanks. If you have any questions or suggestions, please ask them in the comment section below.