It started out pretty simple. And with a real world case, too. I'm debating a new keyboard, as this one starts to act up — I'm sick of wireless, I'm sick of batteries, I'm sick of PS/2-to-USB adapters, and it seems the hardware starts failing me, too. That said, I was very happy with the ergonomic ("split keyboard") Logi — it was good to my wrists, it held up for ten years, and it certainly is one gorgeous keyboard —, and if the SafeType doesn't do the trick for me, I'll be looking to get a variant of the Logi that's not wireless. Anyway, the SafeType is also a split keyboard, but they mean it. It's like hacking a typewriter in two in the middle, and then putting both halves of the keyboard upright so they keyboards aren't horizontal, they're vertical. When you type, your hand are not parallel to your desk, but orthogonal to it. It did wonders for me when I tried the "upright mouse", so I think the keyboard may be beneficial, too. The rub is, you don't have cursor keys that way. So I needed to find a way to put the cursor keys on the main keyboard.
Before I get started, let me run the basic vocabulary by you, so we're on the same page. If you know your xmodmap, you can safely skip this bit.
First, you have keycodes. 666, the number of the key. They're pretty arbitrary in that they have no meaning and depend on your hardware. Then you have maps saying what keycode generates what keysym — if the key that has printed A on its plastic keycap has keycode 666, the standard mapping might be for keycode 666 to be translated to keysym A. Only, it will probably be keysym a. Keys may have several layers, like with and without shift. That makes one group, a and A. If you use AltGr (right alt) to get funny extra characters like € and @, that's another layer. That layer is in turn part of another group: AltGrwith and without shift. If you have more than one national layout (e.g. US keyboard and Ivrit, or, if you appreciate pain, US and French layouts), that also adds one group (e.g. Ivrit with and without shift). Now, I keep saying Shift …
Let the insanity begin …
Shift of course is a key you can hold and then press another key. You use it to get an uppercase letter where normally, you'd get a lowercase one, for instance. Or to get a symbol instead of a digit. Or to get niqqud. CapsLock or AltGr can similarly be pressed together with other "normal" keys. This is called a chord — like on an organ, you press several keys at once. Holding down the Shift key creates a Shift keysym, of course. But that doesn't print a "shift character". It sets a Shift modifier, a special flag that says to interpret other keys in a new way. Shift-a of course gives A. Or expressed in our new vocabulary: When the Shift modifier is engaged, the second (A) rather than the first (a) keysym from the active group is taken. Given that Shift selects within a group, it stands to reason that another key can select between groups. One such example is our AltGr of course, which generates Mode_switch or Iso_Level3_Shift.
So. The shift keys have shift keysyms (Shift_L and Shift_R, respectively, for left and right), and they set shift modifiers. (If any other keysyms are set to generate shift modifiers, or if shift keysyms are to to create non-shift modifiers, X will kill a puppy.) Similarly, the keys that have the Control_L and Control_R keysyms (should) generate the Control modifier bit. Then, there's the Lock modifier, for Shift Lock or Caps Lock.
Here's the twist: there isn't a Hyper modifier, or an Alt modifier in plain X. There are Shift, Lock, Control, and Mod1 … Mod5 modifier bits. If you stick "generate Mod3" on a key that has a Hyper keysym (i.e., Hyper_L or Hyper_R), Mod3 means Hyper (and no keys that aren'tHyper may create Mod3). If you'd stuck Mod3 to Alt_L and Mod4 to Hyper_L, then Mod3 would mean Alt, and Mod4 would mean Hyper. Let me repeat, you can't be certain what, say, Mod2means until you've checked. Not that you need to known — unless you wish to modify your keyboard setup, or write almost any sort of interesting GUI application.
The correct application to set up these things for plain X is xkeycaps. It shows your keyboard, and what keysyms the keys generate. In the case of modifier-keys, it also shows what modifier is generated. You can also modify those settings, and save them in a file ~/.Xmodmap that you can apply using the xmodmap command (in fact that may happen automatically when your X session starts) without having to call xkeycaps every time.
So, as we've discussed, there may be various groups. Suppose we're looking at a single key ("A"). Suppose group 1 has the US keyboard (a, A), group 2 has Ivrit (ש, ְ ), and group 3 has some AltGr rubbish (æ, Æ). Now it stands to reason that you can change all those keysyms using xkeycaps/xmodmap. And you can. You can even ask X to have AltGr-A generate "cursor left" instead of æ. Here's the rub: you can do it with the A key. You can't do it with the J key.
What!, you say. Actually, on vanilla X, this will work. Where vanilla X is "X without the XKB extension." If you disable XKB using export XKB_DISABLE=1 before calling the program you need the funny key-layout in (usually your entire session), this will also work. Why doesn't it with XKB? It doesn't work with XKB apparently because in the XKB setup for "my" country, the A key has some keysyms defined in the AltGr layer, and the J key doesn't. And if they don't exist, you can't overwrite them. I consider this a bug. On the other hand, while this would solve the problem at hand, I don't want to just switch off XKB. It has its uses, and without it, the compiz window manager behaves strangely. For instance, the ring switcher (Alt-Tab / Super-Tab) suddenly requires me to confirm my selection by pressing Return rather than just release Alt.
So, I learned the XKB syntax, and made my own map (which you may download as an example, but which is an ugly hack that's not as modular as it could be — to use it, save it as /usr/share/X11/xkb/symbols/azou and e.g. setxkbmap -layout azou -variant nodeadkeys). The syntax is funky, but not as funky as it could be, not by a long shot. So, I put the cursor keysyms in the AltGr layer of J K L I. (I actually duplicated the entire cursor block, PgUp/PgDn, Insert/Delete etc., but let's keep things simple.) That part was surprisingly simple. I set the left Win-key to generate be "Super." I set the right Win-key up to be "Hyper" (for use in xemacs, mostly). Then in order to not muck up my wrists when using the new cursor keys a lot, I had the CapsLock key generate ISO_Level3_Shift, same as "AltGr". This also worked fine. Except that Control-AltGr-J would correctly give "word left" in xemacs, while Control-Caps-J, which should do exactly the same, did nothing at all. Did I forget to set a modifier? Did I set a modifier I shouldn't? What was going on? After almost an hour of experimenting, I was enlightened when it suddenly occurred to me to try Control-Caps-J on the laptop's keyboard, where it worked famously. After first finding one of only two keys (J and N) on my keyboard that weren't fully accessible to xmodmap, I had now found one of the few chords that my external keyboard could not generate (or that were eaten in the PS/2-to-USB adapter). Thanks ever so bloody much, life!
Of course once the problem was identified, it could be remedied (bind Super-Caps-J to backward-word in xemacs, and Super-Caps-L to forward-word). Plus of course there is no guarantee that the new keyboard will even have that limitation. I think I might still amend Azundris' rule of debugging to say: If finding a bug in your own code takes more than half an hour, make sure it's not the library, or the compiler — or the hardware.
On a semi-related note, it helps to have left-to-right/right-to-left (RTL/LTR) markers on your keyboard. Apparently GTK2 recognises which unicode-blocks are RTL and implies RTL-markers(!?). This breaks display in, say, compiz. Yeah, compiz once more. Workspace names, for one thing. So anyway, since apparently, the GUI won't let you fudge in LTR-markers(!?), you could in theory use some latin alpha character as the first character (implied LTR, latin character, implied RTL, Ivrit characters), but obviously, that's rubbish. You can however fake it in from the command-line if you have LTR-markers (or ISO 14755 support as in rxvt-unicode; press Control-Shift, then enter 200e before releasing those modifier keys) on your keyboard.
This will make the call look like e.g. (note that you cannot copy and paste this for usable results; follow the instructions instead):
gconftool-2 -s "/apps/compiz/plugins/workspacenames/screen0/options/names" -t list --list-type string "[\,ə'zʌndrɪs,Солярис,<200e>נוֹסָף]"
Hybrid Rainbow on :
Thank you for writing this. This is probably the most straight-forward piece I've come across in my quest to be able to type λ or ≉ without making a custom .XCompose.
Glad it was of use.
As an aside, you do realise that your ~/.XCompose can include a stock one? So if you just want to change a very limited number of things, you can include a "close" set-up as a default, and then override the few things that you'd like different/additionally. Of course those will still be compose-sequences, so if you want chords instead (like my AltGr-. for … or AltGr-- for —), the method(s) in the article might be more relevant.
As another aside, you might find GTK apps don't respect your ~/.XCompose. After swearing at GTK developers, you can workaround by installing uim-xim and setting XMODIFIERS="@im=uim". Even if you don't actually need uim (to input japanese or IPA), that will be enough to make ~/.XCompose work in gtk.
Now if only I could tell why the hell compose:menu doesn't do anything...
It seems there is a mistake in your main explanation. A group consist of up to 8 layers, not just 2, though usually only 4 at most are used. AltGr gives access to a layer, not to another group. So does also Shift+AltGr. Cf. for instance info xmodmap (Go then to "Expression grammar"→"keycode"). What could be explained in this article is the link between modifier keys and layers. If I understand well, no modifier key → normal layer. Shift → shift layer. AltGr → another layer; Shift+AltGr → yet another one. CapsLock would give access to shift layer for letters, and normal layers for other caps. Is this correct? And what about Ctrl and Alt?
xev gives the keycode and keysym of pressed keys.
Another thing I don't understand is how dead keys work. In Ancient greek, for instance, through dead keys, we have access to much more than 8 characters (and keysyms) per cap: αάὰᾶἀἄἂἁἅἃᾳᾴᾲᾷᾀᾄᾂᾁΑΆᾺἈἌἊἉἍἋ etc. How does the system compute those keysyms?
seth on :
Thanks for this. I have laughed tears while reading. Note that I'm trying to create an intricate keyboard layout variant since about a week and been through xmodmap and now back to xkb. It's still very confusing.