From ArchWiki
Jump to navigation Jump to search

Many keyboards (mainly on laptops) include some "special keys", which are supposed to execute an app or to print special characters (not included in the standard national keymaps). The great variety of layouts of these keyboards determines some initial problems when you install linux in your system, since the keys do not work out of the box; however, once again, linux gives you the freedom to configure these keys to do exactly what you want. Only a minority of these keys are not seen at all by the linux kernel and thus cannot be used.

There are many options to deal with special keys in linux (we are going to call them 'hotkeys' in what follows). The solutions discussed in this howto allow you to use them independently from the window manager or desktop environment you chose; moreover you will be able to use them in three possible ways (the second and the third way can not coexist for the same key):

  1. to print a character in console;
  2. to print a character in X;
  3. to execute an app in X.

Actually, you can do also the fourth thing, i.e. to execute an app in console, but this is made with a silly work-around and will be briefly described when discussing the first use in the list above (see below).

Other approaches use comprehensive applications aiming to solve these problems (see e.g. the wiki howtos for keytouch and for Lineak), but these solutions are generally built on a wide database of known models of keyboards: this database seems to be condemned to be incomplete, since the variety of keyboard models does never stop to grow. The approach here considered allows the user to work with the internals of the system, tailoring the configuration to his own system as it is. Moreover, the other solutions assume that you want to use the hotkeys only when you are in X, while it is possible to take profit of them also if you are a console lover.

All the keys we can hope to use in linux has a scancode. This scancode is generally associated with a keycode. This keycode is generally associated with a character to print in the console keymap. This keycode is also translated by X in another code, which the X keymaps generally bind to a certain character or to a certain command to run. Well, when an hotkey does not work for us, one or more of these things that generally happen do not happen. First of all, we have to discover what is the problem. This diagnosis will be explained in the first section, but we sum up the possible scenarios, so that after the diagnosis we know where to look for a suitable medicine. We have to verify if the key has a scancode. If it has not a scancode, there is no hope that it works, so we have to give up. If it has a scancode but no keycode, we have to associate the scancode with a keycode: this will be explained in the second section. If it has already a keycode or if you gave it a keycode thanks to the second section, then we have to bind it to one or more of the three possible uses listed above: the third section explains how to make the key print a character in console; the fourth section explains how to make the key print a character in X using xmodmap; the fifth section explains how to make the key run an application when in X, using xbindkeys.

The diagnosis

The most global way to diagnose the situation is to go in console and use the 'showkey' utility. Please note that this utility will not work when in X. First of all we run

$ showkey 

without options: 'showkey' waits for ten seconds that we press a key (after ten seconds it quits automatically) and displays the keycode of the key we pressed. Two scenarios are possible:

  1. nothing is displayed: in this case the key has not a keycode and you have to read further this section;
  2. some mysterious hexadecimal numbers are displayed; in this case the kernel already assigns a keycode to the key; we can pass over to sections 3, 4 or 5, according to what we want to do with that key.

In the first scenario, we have to investigate if the key has at least a scancode. The 'showkey' utility can help us again. Just use the '-s' option:

$ showkey -s

When we press our hotkey, two scenarios are possible:

  1. nothing is displayed: the key is not seen in any way by our os, we whould give up;
  2. a decimal code is displayed; the hotkey has a scancode; the [#Associate scancodes to keycodes|next section] will help you to associate this scancode with a keycode.

Associate scancodes with keycodes

In order to accomplish this task, we need to resort to the utility 'setkeycodes', which, as you can see in its man page, associates scancodes to keycodes. Obviously enough, it takes two arguments: the first argument is the scancode, the second argument is the keycode. However, it is not always easy to determine these arguments. In order to determine the scancode, the output of the 'showkey -s' we used in the section above is not always illuminating, since sometimes the hotkeys - and the normal keys too - generate a double scancode. The simplest way to determine the exact scancode is to look at the system log: in fact, any time we press a key which has a scancode but no keycode, the kernel suggests us to use just 'setkeycodes' to assign a keycode to it. In the message the kernel gives us also the scancode we have to use actually. We will find it in the system log. However, also this code is not always usable as it is: when it is composed of four characters and starts with an 'e', then we can use it as it is as the first argument for 'setkeycodes'. On the contrary, when the kernel gives us only two digits, we have to prepend '0x' to it. As an example, suppose that the kernel tells us that the scancode is 71; then the first argument for 'setkeycodes' will be 0x71.

The second argument is partially arbitrary. Partially! In fact:

  1. the keycode has to be in the range 1-127;
  2. it should not conflict with the keycodes already mapped to a character in your keymap.

In order to respect the second condition, you have to look at your keymap. In configuring your system through /etc/rc.conf, you have chosen your keymap, or decided to stay with the default one: in fact rc.conf includes a 'KEYMAP' setting. The keymaps are stored in your system in one of the subfolders into /usr/share/kbd/keymaps/i386/. E.g., mine is the Italian keymap and its path is /usr/share/kbd/keymaps/i386/qwerty/ It is a text file compressed with gzip2. Let us gunzip it.

# gunzip /usr/share/kbd/keymaps/i386/qwerty/

Now we can read it (sometimes less is able to read also gzipped files: in this case the above passage is dispensable):

# less /usr/share/kbd/keymaps/i386/qwerty/

We have to choose a keycode not conflicting with those already associated with characters. Most of the lines in the keymap are of the following format:

keycode <keycode> = <keysym>

These lines are ordered according to the numeric value of the keycode, thus it is easy to find the highest keycode used by the keymap. Thus, the second argument for 'setkeycodes" has to in the range between the successor of this highest keycode and 127.

Let us say that 112 is in this range (this is actually true with the italian keymap): now we can actually use 'setkeycodes':

# setkeycodes 0x71 112

If we run 'showkey' again, we can see that our hotkey has now the keycode 112. However, this is true only for the current session. To make this permanent, we need to execute 'setkeycodes' each time we boot our system. This can be done inserting the following line in /etc/rc.local:

setkeycodes 0x71 112 &

Printing characters in the console

When we are in console, we can use our hotkeys to print a certain character. Moreover we can also print a sequence of characters and some escape sequences. Thus, if we print the sequence of characters constituting a command and afterwards an escape character for a new line, that command will be executed!

In order to do this, we could modify our console keymap. However, I suggest not to do this, since that is a delicate file and since it will be rewritten anytime we update the package it belongs to. It is better to integrate the existing keymap with a personal keymap. The utility 'loadkeys' can do this.

First of all, we need to write down this file. You can put it as you prefer, but I prefer to mimic partially the hierarchy of the default keymaps into /usr/local. So:

# mkdir -p /usr/local/share/kbd/keymaps
# vim /usr/local/share/kbd/keymaps/

As a side note, it is worth noting that such a personal keymap is useful also to redefine the behavior of keys already treated by the default keymap: in fact, when we load it with 'loadkeys' the directives in the default keymap will be replaced when they conflict with the new directives and conserved otherwise.

Anyway, we need two kinds of directives in our personal keymaps. First of all, the keycode directives, with the format we have seen above in the default keymaps. These directives associate a keycode with a keysym. Keysyms represent keyboard actions. The actions available include outputting character codes or character sequences, switching consoles or keymaps, booting the machine etc. (The complete list can be obtained with

$ dumpkeys -l

Anyway, most of them are intuitive. If we want our key to output an 'e', the directive will be:

keycode 112  = e

If we want our hotkey to output the symbol of the euro currency, the directive will be:

keycode 112 = euro

Some keysym are not immediately connected to a keyboard actions. In particular the keysyms constituted by a capital F and two digits constituting a number greater than 30 are always free. This is useful for us if we want our hotkey to output a sequence of characters and other actions. In fact we can first of all bind our character to such a keysym:

keycode 112 = F70

Then we use another kind of directive, which binds the keysym to an action. E.g., if we want our hotkey to output a literal "Hello":

string F70 = "Hello"

This can seem not very useful. But we can use it to print and execute commands. In order to execute the printed command, I need to use an escape sequence corresponding to a new line. E.g., I can want an hotkey to hibernate my laptop also when I am a user (through sudo). In this case I write the following directive in my personal keymap.

string F70 = "sudo /usr/sbin/hibernate\n"

In order to take profit of my personal keymap, I have to load it with 'loadkeys':

$ loadkeys /usr/local/share/kbd/keymaps/

Also in this case, this is valid only for the current session. In order to accomplish the same result each time I boot my machine, I have to insert another line in /etc/rc.local. This line should come after the occurrences of 'setkeycodes' discussed in section 2, since the personal keymap resorts to the keycodes assigned through 'setkeycodes':

loadkeys -q /usr/local/share/kbd/keymaps/

The '-q' option simply avoids that a confirmation message is printed to stdin during the boot process.

Printing characters in X

Fortunately, when a character has a keycode in the system it is also seen inside X. X gives it a keycode: however, it is very important to stress that the X keycode is DIFFERENT from the system keycode. If you had to pass through section 1 of this article to assign a system keycode to your hotkey, you MUST forget this keycode when dealing with X, because X assigns a different keycode to the key (but requires that the key has a system keycode whatsoever).

The most traditional and proficient way to make a key output a character when you are in X is to use xmodmap. Xmodmap is roughly the X equivalent of 'loadkeys' (see section 3): it reads a file containing some directives. As 'loadkeys', it can be used to modify many aspects of the behaviour of your keyboard (such as modifiers, etc.), but I will not cover these aspects in this article. The only kind of directive I am interested in here associates an X keycode to a keysym. 'xmodmap' is included in the 'xorg-server-utils' package.

# pacman -S xorg-server-utils

In order to use it we need to discover the X keycode of our beloved hotkey: we resort to 'xev'. 'xev' is included in the 'xorg-utils' package.

# pacman -S xorg-utils

'xev' (run it from a terminal window) open a white window and outputs in the terminal some codes and infos when you press (or relase) a key or move your mouse. Its output is redundant and difficult. Anyway, you will see clearly into it when you press your hotkey:

keycode: <theXkeycodeofyourhotkey>

Once you have discovered the X keycodes of your hotkeys, you are ready to write the file that 'xmodmap' will parse. You can put it where you like, but since the xserver is executed by a user, it is customary to make it an hidden file in the home directory of the user:

$ vim ~/.Xmodmap

In this file, you have to list the keycode directives, with the following syntax:

keycode <Xkeycode> = <keysym>

The list of X keysyms can be read in /usr/include/X11/keysymdef.h. Anyway, most of them are intuitive. Let us say that 'xev' has said that the X keycode of my hotkey is 239. If I want it to output a literal 'e', I will write the following directive:

keycode 239 = e

If I want it to output the symbol of the American currency, I will write the following directive:

keycode 239 = dollar

Finally I have to source the file with xmodmap:

$ xmodmap ~/.Xmodmap

Obviously, this will work only for the current X session, but it is better to make this automatically anytime I start X. This should be done in different ways according to your system configuration. E.g., if you use the old plain 'startx', you have to put the following line in .xinitrc before the line when you launch your window manager:

xmodmap ~/.Xmodmap &

NOTE: This can also be used to assign functions to multimedia keys. Special functions can be found in /usr/share/X11/XKeysymDB.

My (tkjacobsen) ~/.Xmodmap:

keycode 160 = XF86AudioMute
keycode 176 = XF86AudioRaiseVolume
keycode 174 = XF86AudioLowerVolume

Running applications in X

There are several utilities which aim to help you to bind keys (or keys combinations) to certain apps. Basically, they follow two main philosophies: some of them includes a database of existing keyboards and, once you have defined the model of your keyboard, should associate to keys the 'right' action (i.e. corresponding to the icon on the key...); some others allow you to associate the action you like to whatever key or key combination, disregarding stuff like icons. I have a strong preference for this second philosophy and will propose to use one of the utilities of this second kind. Lineak is an example of the first approach.

Another option is to use the configuration files of the window manager. This is a very good solution, although it often requires that the key is associated (through xmodmap) with a keysym which the window manager then associates to an app to run. However, it is different for each of the existing window managers and can not be covered in this article.

I propose to use xbindkeys. xbindkeys can be used together with any window manager (but its shortcuts can conflict with the shortcuts defined by the window manager).

# pacman -S xbindkeys

xbindkeys is widely documented in its man page:

$ man xbindkeys 

Xbindkeys includes an utility to detect the X keycodes of keys and key combinations without resorting to 'xev'. Its output is much more readable than 'xev' output.

$ xbindkeys -mk

In the configuration file you can associate commands both to X keycodes and to keysyms. Your hotkeys, if you haven't already configured them through 'xmodmap' (see Section 4 above) are actually identifiable only through an X keycode. The man page enlightens exhaustively the syntax of the configuration file (~/.xbinkdekysrc).

Once configured, 'xbindkeys' should be run when an X syntax starts. Thus, provided that you use 'startx' to start your x session, you can add the following line to your .xinitrc:

xbindkeys &

In this way, xbindkeys will fork in the background and be ready to react when you press the hotkeys (or the normal keys, or key combinations) you defined in the config file.