Keymap and Ioctls
Keymap and Ioctls
Keymap and Ioctls
In issue #86 of the Linux Gazette there's a nice example of how to remap keys from a C
application:
http://linuxgazette.net/issue86/misc/bint/kbe.c.txt
The author, the late Stephen Bint, was working on an editor for the text console and a library for
such an editor. He didn't like to have the screen "dirtied" when pressing <PgUp> or <PgDn>
while also pressing the <Shift> key. Indeed, that combination is reserved by default to scroll
backward or forward through console output: the kernel will intervene and do things before the
editor comes to know what keys you have pressed.
There is a way around it: redefine the keys temporarily, restoring them when exiting your
application. Of course, if you can understand the C source for loadkeys and dumpkeys, you can
skip both Stephen's examples and what follows. However, since I had problems getting even a
vague idea of what loadkeys and dumpkeys do, I decided to write down a few details, to
complement the old LG article referenced above.
All of this is relevant only for the GNU/Linux text console. You can forget about anything this
"simple" if you work only under X11.
1. Introduction
A PC keyboard sends signals to the Linux kernel - more precisely, to the kernel keyboard driver -
telling it that a certain key has been pressed or released. Normally, applications let the kernel
translate those signals into strings using the current (screen) character set and the traditional
ASCII encoding, where one character equals one byte. This used to be the normal approach, until
recently. Nowadays, Unicode is spreading, and you are now likely to have Unicode for the
screen, rather than a character set as in the old days. The keyboard will then produce Unicode
strings, using the UTF-8 encoding, where one character may need more than one byte. For the
purpose of this article, however, there is no difference between the two modes; we refer to them
both as translation mode.
Direct handling of keyboard signals (raw and semi-raw mode) by the application is rare.
The keymap tells you about the translation the kernel is doing for you. However, if you want to
know what's going on, don't look into the local keymap: It is not complete, since it includes other
files; it is formatted in an inconvenient way; and your distro may have modified it at boot time
anyway. If you want to know what is really in force, and be able to read it, issue
at the shell prompt and examine (the nicely formatted) my_keymap.txt. Consider the following:
some keys insert characters: you press <a> and you see an 'a' inserted at the cursor.
some keys insert nothing but have some effect: you press <left>, the cursor usually
moves one space to the left.
some keys are defined as 'VoidSymbol', no action will be taken on them.
some keys are listed but do not seem to exist. Indeed, they do not exist, since the Linux
keymap also applies to keyboards different from the standard PC keyboard you are using.
Whatever the keys do, they do it on the basis of an 'action code' assigned to them. For keymap
programming, you must know these action codes. How do you get them? Issue
at the prompt. You get a list of action codes (left column) and the key labels for them (right
column). Some labels have synonyms: for instance, the action code for <PgUp> is 0x0118, but
you will not read 'PgUp' next to it; you will see the label 'Prior'.
Keys like <Left> may not insert anything, but still they have their effects:
An example of the first kind is <Shift><PgUp> where the console output is scrolled bypassing
the current application.
However, more often than not we are concerned about (ii). We press <Left>, the cursor moves
one space to the left, the application is in control. That implies the application has been notified
that <Left> was pressed. How was it notified? The keyboard driver knows the action code for the
key <Left>. Although it does not arrange directly for the cursor movement, the action code
arranges for a string to be sent to the application, so the application can do the right thing. The
string for <Left> is normally "\033[D", where the octal \033 represents the escape character. It is
the application that decides to move the cursor left upon receiving "\033[D".
In translation mode, the application receives strings of one character, normally for insertion of
that character, or multi-character strings for functional keys triggering some action. They are
called functional keys because they are not just the F1 to F12 keys across the upper row of the
keyboard; the <Left> key is also a functional key. Unfortunately, the keymap utilities say
'function keys' - i.e., <Left> is a function key to them. To put it mildly, this is just a bit
confusing.
So, beside those few exceptions like <Shift><PgUp>, the application is running the show. It
receives strings of one or more characters. The one-character strings are very often for insertion,
but not always so. You surely have been exposed to old-style user interaction: press 'a' for All or
'c' for Cancel.
OK, but where do those strings like "\033[D" for <Left> come from?
For that, look into the local keymap - e.g., the US default keymap. Almost at the top of the
keymap, you will see a line that says
strings as usual
and that's it. That line defines the strings for F1-F20 and for Home, End,..., Left, Right, and so
on. The strings originate from DEC hardware, and are still around after DEC passed away in the
mid-90s.
Now, why is the keymap defining F1-F20, if the keyboard has only F1-F12? Because Unix
keyboards were not PC keyboards. The default Linux keymaps (any language) set
So F13-F20 are not useless; actually they are not enough. Indeed, you will notice that
are defined in the US keymap, although no strings are assigned to F21 and up. These entries
mean, e.g., that <Shift><F1> will give F13 and <Ctrl><F1> will give F25. However, since F25
has an empty string, an empty string is forwarded to the application, and the application does
exactly nothing.
Why is it so? Why are those keys set to send an empty string? Well, Linux 1.0 had a default
keymap where <Ctrl><F1> was same as F1 - and so on. Sometime down the road, there was a
change, for reasons which to me do not seem worth an investigative effort. Just note that the
Russian Linux keymap has not changed on this point, and that FreeBSD has low-level operations
on <Ctrl><F1> to <Ctrl><F10>.
Summing up, the keys not used by the kernel for its own purposes send a standard string to the
application, possibly an empty string. Other keys are just void - i.e., undefined.
Category: Programming
Whatever the keys do, they do it on the basis of an 'action code' assigned to them. For
keymap programming, you must know these action codes.
Before we get into some C code, let's mention the obvious: You can modify your keymap in an
editor and then activate it. You are supposed to be already in the know, in this respect, otherwise
you would not be able to understand what this article is about.
For instance, take those shifted <PgUp> and <PgDn> for scrolling the console. You edit the
keymap to
save it, load it with loadkeys and the console scrolling will be done with <Alt><Ctrl><PgUp>,
<Alt><Ctrl><PgDn> , which is not in anybody's way.
This is, of course, a lot easier to do and to understand than Stephen's code; however, Stephen
gave a nice example of how to use ioctls, and here is where we resume his efforts. We'll add a
little example, and show how to use a couple of those ioctls.
In a second step, we instruct the keyboard driver to associate our string with F50:
ks.kb_func = 50;
ks.kb_string = "\033[M~4";
ioctl(fileno(stdin), KDSKBSENT, &ks);
// if assignment fails, an error will be set
// in global variable ERR
This and the kbentry/kbsentry structures are scantily documented in the manpage
console_ioctl, written in 1995 and never updated. Same for KDSKBSENT, which can be
memorized as 'Keyboard Driver Set KeyBoard String ENTry'.
The kb_table is determined by the modifier used. We are pressing two keys, <Ctrl> and
<Enter>, so we are using modifier <Ctrl> which has number 4 in the keymap.
The kb_index is the keycode, the kb_value is the action code, both already explicitly
stated above.
ioctl(fd,KDSKBENT,&ke);
// if assignment fails, an error will be set
// in global variable ERR
and we are done - just pay attention when typing KDSKBSENT vs. KDSKBENT. F50 is
now associated with the string of our choice. This string is sent to the application when
<Ctrl><Enter> is pressed.
3. Moral
<Enter>
<Ctrl><m>
<Ctrl><Enter>
to be all distinct. Think about it: The first two send the same string, namely "^M", character 13.
They can be told apart by checking the modifier, since the first one is produced with 1 key, while
the second one needs 2 keys.
The third entry, <Ctrl><Enter>, can not, by default, be distinguished from <Ctrl><m>, since
both produce "^M", and both signal that <Ctrl> was pressed. (You would have to move to raw or
semi-raw mode to find out - not recommended.) With our little modification, we now have all
our ducks in a row. Also, the trick can be applied elsewhere, for instance, to <Tab>, <Ctrl><i>,
<Ctrl><Tab>.
Note that applications other than the one you are working on may rely on the keymap defaults,
and get confused by your ioctls intervention. A well-behaved application should thus reverse the
changes made. However, if the application crashes for whatever reasons, the reversing code will
not be executed, and the keyboard will not be reset.
In other words: when you exit this application, just reload the default keymap. For that, the
command will be
loadkeys us
A. N. Onymous has been writing for LG since the early days - generally by sneaking in
at night and leaving a variety of articles on the Editor's desk. A man (woman?) of
mystery, claiming no credit and hiding in darkness... probably something to do with large
amounts of treasure in an ancient Mayan temple and a beautiful dark-eyed woman with a snake
tattoo winding down from her left hip. Or maybe he just treasures his privacy. In any case, we're
grateful for his contribution.
-- Editor, Linux Gazette
Copyright © 2007, Anonymous. Released under the Open Publication License unless otherwise
noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its
prior host, SSC, Inc.
The Free International Online Linux Monthly ISSN: 1934-371X Main site: http://linuxgazette.net
Home > March 2007 (#136) > Article