Home / Alt manpages / compose(5)

  • compose(5)
  • File format
  • linux

Add and Safely Test Custom X Compose Sequences

Every Compose-key trick you rely on is just a text rule in a file Xlib reads at startup, and adding your own is safer than it looks. This follows the Compose(5) behaviour installed with libX11 1.8.7.

Allow about fifteen minutes. You need an X11 client that uses Xlib input methods, a text editor and a shell; no root access required.

1. Check the locale and existing configuration

Compose files are read by Xlib clients, not by the shell or terminal emulator alone. Record the locale that will select the system Compose file:

$ locale
  • System mapping. Resolved through /usr/share/X11/locale/compose.dir, with the locale tree itself under /usr/share/X11/locale.
  • Caches. The manual names /var/cache/libx11/compose/ as the system cache and $HOME/.compose-cache/ as the per-user one.

Check whether a user file already exists before you touch anything:

$ if [ -e "$HOME/.XCompose" ]; then
    printf '%s\n' "$HOME/.XCompose exists"
else
    printf '%s\n' 'No user Compose file'
fi

Warning

Do not overwrite an existing file blindly. If it holds rules you need, back it up first:

$ cp --preserve=mode,timestamps "$HOME/.XCompose" "$HOME/.XCompose.backup"

Recovery

Restore that backup with mv "$HOME/.XCompose.backup" "$HOME/.XCompose", after checking the destination is really the file you mean to replace.

2. Create a small user Compose file

Use ~/.XCompose for a personal change: plain text, comments start with #, and each rule is one or more key events, a colon, then a result. Start with an include so the standard locale rules stay available:

include "%L"

# Compose 'o' followed by 'e' to produce the œ character.
<Multi_key> <o> <e> : "œ"

%L means the locale-specific Compose file Xlib would normally pick; the other documented substitutions are %H for your home directory and %S for the system Compose directory. An include beats copying the whole system file: your local file stays reviewable, and ordinary locale rules still come from the package.

If your keyboard has no Compose key, set one up through your desktop or window manager first. The rule above only describes the sequence after Compose has already generated the Multi_key event; it does not assign a physical key.

3. Add a rule with an explicit modifier requirement

Events can name modifier states: a modifier list without ! gives allowed constraints, prefixing the list with ! requires an exact match, a tilde means the modifier must be absent, and None means no modifier at all.

<Multi_key> <a> <!Shift> : "å"

That line uses a literal UTF-8 character. Compose also accepts octal and hexadecimal escapes such as "\303\245" or "\xe5", interpreted for whatever locale the file is used in. A result can be a string, a keysym, or both; a keysym-only rule looks like this:

<Multi_key> <minus> <minus> : emdash

Keysyms omit the XK_ prefix. If you need stable text bytes for a specific locale, write an explicit string rather than trusting Xlib to derive one from the keysym.

4. Reload the client and test the sequence

Save the file and start a new Xlib client; an already-running window may have cached its Compose data, so test in a freshly opened terminal or editor.

$ printf '%s\n' 'Open a new X11 text-entry window and press Compose, o, e'

You should get œ at the cursor. Test an untouched sequence too, to confirm include "%L" still pulls in the normal locale rules.

Checkpoint

If the custom sequence works in a new client but not an old one, just close and reopen the old client. If it works nowhere, check the file for spelling, angle brackets and the colon separator: event names are keysyms, not arbitrary labels, and the result belongs on the right of the colon.

5. Use XCOMPOSEFILE for a controlled test

$XCOMPOSEFILE takes precedence over ~/.XCompose, which is handy for a temporary test or comparing two files without touching your normal configuration.

$ test -f /path/to/test-compose && printf '%s\n' 'test file exists'
$ XCOMPOSEFILE=/path/to/test-compose xterm

The variable applies only to the program you launch and its children; it changes neither the system Compose mapping nor your home directory. No xterm? Launch any other Xlib client that takes text input with the same environment assignment.

Warning

Do not put a test file in /usr/share/X11/locale. Package upgrades own that directory, and editing it affects every user, not just you. Keep test files under a temporary directory and remove them once nothing still needs them.

6. Diagnose cache and parsing surprises

Compose data can be compiled and cached. The documented XCOMPOSECACHE variable picks the directory used for compiled files, so point a test client at a private, disposable one to isolate a cache issue:

$ cache_dir=$(mktemp -d)
$ XCOMPOSECACHE="$cache_dir" XCOMPOSEFILE="$PWD/test-compose" xterm
$ rm -rf "$cache_dir"

That final removal deletes only the temporary cache, and only once the test client has exited; it never touches your real Compose file. Add explicit cleanup in a script so an interrupted test does not leave stray data behind.

When a rule fails, cut it down to one sequence and test in a new client. Check the include file exists, every event sits inside angle brackets, and a literal < or > was not copy-pasted from HTML documentation as markup into the real text file: the parser expects the plain text form shown above.

Done means

  • Include kept. ~/.XCompose starts with include "%L" plus only the rules you actually need.
  • New sequence works. It produces the right character in a freshly started Xlib client.
  • Old sequences still work. Locale rules from the include were not broken.
  • Modifiers tested. Any modifier-sensitive rule was checked both with and without that modifier held.
  • Rollback available. A failed experiment can be undone by restoring the backup or deleting just the user file, without touching system Compose files.