Does anyone know how to configure xkb?

I usually prefer to leave my keyboard layout and shortcuts in default configuration. Partly, this makes switching between machines easier and partly it helps me learn what the defaults are, so I can avoid breaking them in programs I write.

Some customizations, however, are just too valuable to forgo: super+arrow to move windows between monitors and shift+space to type an underscore. Kde has global shortcut configuration for the first, but what about the second?

I’ve used xmodmap before, but had problems where it wouldn’t stick throughout a session, apparently forgetting my configuration from time to time. So I end up using dumb tricks like a shell script with an infinite loop. Nowadays, though, the Internet says that xkb is the new and shinier replacement.

So, xkb it is. The best documentation I can find is An Unreliable Guide to Xkb Configuration. I learn that configuration lives in /usr/share/X11/xkb. There’s also something about /etc/X11/xorg.conf.d. Bad start… where does user-configuration go? I have no idea. I think the concept is probably that I should define a custom layout for me specifically, maybe naming it something like kingdom_of_joe, then pick that as my keyboard layout somewhere else in my window manager or login script. Screw it. I’ll just edit the files in /usr/share and if some barbarian who maps shift+space to backspace shares my computer, we’ll have to go to war.

Now, to look at the configuration. There are 275 config files. It’s slightly less than obvious where I should start.

Lampson attributes the aphorism that started our exploration (all problems in computer science can be solved by another level of indirection) to David Wheeler, the inventor of the subroutine. Significantly, Wheeler completed his quote with another phrase: “But that usually will create another problem.”

From Beautiful Code

Back to xkb. What do these six directories represent?

First layer of indirection is translating a scancode (some bytes the keyboard firmware generates) to a mysterious all-caps alphanumeric identifier that looks like FROB or AE01. This symbol is supposedly a mnemonic for the key’s physical position, except when it isn’t. The mapping happens via files in the “keycodes” directory and I think I can ignore it.

I can also ignore the “geometry” directory; it apparently contains specs for how to draw keyboards.

Thus, I eliminate 50 config files from consideration. Only 225 to go.

The “rules” directory seems like a promising place to look. This is hopeless. The files look like an almost-but-not-quite scripting language that refer to other parts of the configuration. Maybe the docs will enlighten me.

The main advantage of rules over formerly used keymaps is a possibility to simply parameterize (once) fixed patterns of configurations… A sample rules file looks like this:

! model = keycodes
 macintosh_old = macintosh
 ...
 * = xorg

! model = symbols
 hp = +inet(%m)
 microsoftpro = +inet(%m)
 geniuscomfy = +inet(%m)

! model layout[1] = symbols
 macintosh us = macintosh/us%(v[1])
 * * = pc/pc(%m)+pc/%l[1]%(v[1])

! model layout[2] = symbols
 macintosh us = +macintosh/us[2]%(v[2]):2
 * * = +pc/%l[2]%(v[2]):2

! option = types
 caps:internal = +caps(internal)
 caps:internal_nocancel = +caps(internal_nocancel)

I think the writer has a different idea of “simple” than I. Having given up on rules files, I move on to “compat”, “symbols” and “types”.

The docs make it sound like these configurations all do just about the same thing:

  • Types “…describe how the produced key is changed by active modifiers…”
  • Compat “…defines internal behaviour of modifiers…”
  • Symbols “…defines what values (=symbols) are assigned to what keycodes [depending] on a key type and on modifiers state…”

I cross my fingers and hope I won’t need compat, so I look at types. The files are full of incantations like this:

type "TWO_LEVEL" {
    modifiers = Shift;
    map[Shift] = Level2;
    level_name[Level1] = "Base";
    level_name[Level2] = "Shift";
};

It appears that xkb abstracts the concept of a modifier key to something called a “level.” Level one means no modifiers, level two is with shift pressed, level three is alt or ctrl or super or something, and so on. I guess, maybe, if I wanted space to behave as shift, I might do that in the types (or compat?) folder, but since those files don’t appear to mention specific keys like space, they probably are not what I want today.

On to symbols…

There are a mere 183 config files in symbols. They have names that look mostly like country codes, but some are a little odd. I’ve never heard of a country called “capslock”, for example. How do I know which symbols file applies to me? I have no clue; guessing it is.

I discovered in keycodes that the four-letter word for space in xkb-language is SPCE, so I break out grep to find where it appears in symbols… it is all over, but I sense a pattern. Also, I notice an oddly-named country called “pc”.

$ grep SPCE symbols/pc
key <SPCE> { [ space ] };

In other countries other than the republic of pc, it looks a bit different:

$ grep SPCE symbols/fr 
    key  { [ space, nobreakspace, underscore, U202F ] };
    // ␣ (espace insécable) _ (espace insécable fin)
    ...

The French seem to get four mappings for space. Combined with my knowledge of levels, I finally put this together. It seems that the symbols files are tables of what character to produce, given an key and a particular modifier, if the modifier is level two (shift), you use column two, and so forth. I think.

So, I edit the pc symbols:

$ sudo vim /usr/share/X11/xkb/symbols/pc 
...
    key <SPCE> { [ space, underscore ] };

I log off, and back on again and I finally can type shift+space=__wtf__ in comfort.

Qwerty War: day 5

On day five, I plan to get the leaderboard persistence built and deployed: the only part of the project that requires a database back end. I figure that I’ll build this with Django and deploy on Heroku.

So I switch from JavaScript to Python and naturally this requires that I instate my traditional key customization that maps shift+space to underscore. Previously, I’ve used xmodmap, but the going wisdom around the Internet is that xkb is the newer, better way to do things.

hours pass

I can finally type underscores without over-extending my pinky, so I turn back to the problem of Django. Django normally uses a great many subdirectories to organize things and make them portable, which is great for medium to large applications, but annoying for something as small as Qwerty War. The article Django as a micro-framework is a good start to really stripping Django down, but it’s significantly out of date. To bring it up to date with Django latest, the main thing to realize is the django-admin command line should now read thus:

django-admin runserver --settings settings --pythonpath .

From there, you’ll find there are a series of errors that are all pretty self-explanatory and easily fixed. I skip most middleware, but keep the csrf and clickjacking parts.

Soon enough, Django is serving up my static files: I haven’t built any database stuff yet, but I figure I should try deplying the static bits on Heroku. First hurdle: Heroku wants me to install its command-line tool by piping stuff from wget into a shell. The script would install Heroku’s apt repository and go from there.

Isn’t the whole deployment done with git anyway? Why do I need the heroku command line interface? Much less another dpkg repo. It turns out this is easy enough, if undocumented

  1. Add an ssh public key to your account via the Web gui
  2. Use the Web gui to create a project
  3. Push to the project using the funky-looking location git@heroku.com:[appname].git

Mercurial, of course, does not speak Git natively, so I decide to try to the hg-git extension. Its homepage says to install via the old easy_install. Not an auspicious start, but ok, I just try pip instead, and in Pythonic fashion, it just works.

So far, so good. The first push via hg-git succeeds, but naturally, the app fails to start on Heroku, as I haven’t yet created a Procfile or wsgi.py. I add them, push again, and… nothing. Mercurial reports there are no changes to push. Did my first push really work at all? I figure I’ll try cloning the repo from Heroku to check, using actual Git…. time passes (but much less than spent on xkb) …I figure the problem is that either hg-git or Heroku or both dislike that I pushed some rather large files. In particular, I included a full history of my edits to the binary blobs that are audio files, plus the originals in .wav and .flac format.

Fair enough, pushing all that stuff is pretty wasteful anyway. I abandon the hg-git extension and opt for a simple shell script that deploys via Git proper. It works, so on day five, I at least have a deployable application, if still very much in development mode and without a database.