The Compose Key

Remco Bloemen

2014-02-27, last updated 2014-03-04

Unless you are shouting on online fora, the tab key is pretty much useless. I’ve remapped it to a compose key. With XCompose you can enter all sorts of unicode characters using the compose key followed by key sequences. But, the format to configure this is a bit verbose:

<Multi_key> <A> <A> : "∀"
<Multi_key> <C> <c> : "∁"
<Multi_key> <p> <d> : "∂"
<Multi_key> <E> <E> : "∃"
<Multi_key> <slash> <E> <E> : "∄"
<Multi_key> <q> <e> <d> : "∎"
<Multi_key> <slash> <o> : "∅"
<Multi_key> <plus> <minus> : "±"
<Multi_key> <minus> <plus> : "∓"
<Multi_key> <v> <slash> : "√"
<Multi_key> <slash> <asciitilde> <minus> : "≄"
<Multi_key> <asciitilde> <equal> : "≅"
<Multi_key> <slash> <asciitilde> <equal> : "≇"
<Multi_key> <asciitilde> <asciitilde> <space> : "≈"

To fix this I developed my own specification format that compiles to the above format. It looks like:

∀     AA
∁     Cc
∂     pd
∃     EE
∄     /EE
∎     qed
∅     /o
±   +-
∓     -+
√     v/
≄     /~-
≅     ~=
≇     /~=
≈     ~~
#!/usr/bin/python
# coding=utf-8
import sys

XComposeNames = {
    "~": "asciitilde",  "=": "equal",       "-": "minus",
    "_": "underscore",  "+": "plus",        ">": "greater",
    "<": "less",        "|": "bar",         "/": "slash",
    "\": "backslash",  ".": "period",      "!": "exclam",
    ":": "colon",       "^": "asciicircum", "?": "question",
    "(": "parenleft",   ")": "parenright",  "[": "bracketleft",
    "]": "bracketright","`": "grave",       "'": "apostrophe",
    """: "quotedbl",   " ": "space",       ",": "comma",
    "@": "at",          "#": "numbersign",  "$": "dollar",
    "%": "percent",     "*": "asterisk",   u"←": "Left",
    u"↑": "Up",        u"→": "Right",      u"↓": "Down",
}

def XComposeSequence(s, target):
    seq = "<Multi_key> "
    for c in s:
        if c in XComposeNames:
            seq += "<" + XComposeNames[c] + "> "
        else:
            seq += "<" + c + "> "
    seq += ": "" + target + """
    return seq

sequences = {}
while True:
    try: line = raw_input().decode("utf-8");
    except EOFError: break
    if line == "" or line[0] == "#":
        print line.encode("utf-8")
        continue
    target, sequence = line.split("t", 2)
    for s in sequences.iterkeys():
        if s.startswith(sequence) or sequence.startswith(s):
                sys.stderr.write(("WARNING: sequence "" + sequence +
                    "" for "" + target + "" clashes with " +
                    "previous sequence "" + s + "" for "" +
                    sequences[s] + ""n").encode("utf-8"))
    sequences[sequence] = target
    print XComposeSequence(sequence, target).encode("utf-8")
$ ./XCompile < UnicodeMath > ~/.XCompose && setxkbmap

xmodmap -pke

Full source is available on https://github.com/Recmo/XCompile.