Table of Contents
Getting Started
Requirements
- SBCL.
- cl-ffi, which is not in Quicklisp:
with stift, cl-xkbcommon's
stiftfilebrings it in; without, clone it where ASDF finds it. - libxkbcommon 1.10 or newer, the shared library
libxkbcommon.so.0. Debian forky has 1.13; Debian bookworm (1.5) and Ubuntu 24.04 (1.6) are too old.pkg-config --modversion xkbcommonor your package manager tells you which you have. - The xkeyboard-config layouts, for keymaps made from layout names.
- The X11 compose tables, for compose tables made from a locale.
On Debian:
sudo apt install libxkbcommon0 xkb-data libx11-data
libx11-data only provides the compose tables. Install it if you use
make-compose-table. It is not needed when you load compose tables from a
string.
Loading
cl-xkbcommon is not in Quicklisp. Require it with stift, stift get https://fakehacker.cc/dxtr/cl-xkbcommon.git, or clone it where ASDF can
find it, for example under ~/common-lisp/, and load it:
(asdf:load-system :cl-xkbcommon)
The package is cl-xkbcommon. Its symbols are short and general, such as
keymap and key-text, so use a local nickname rather than :use:
(defpackage #:my-app
(:use #:cl)
(:local-nicknames (#:xkb #:cl-xkbcommon)))
All the examples in this wiki use the nickname xkb.
A first program
This program types on a Swedish keyboard: å, then Å with Shift, then a dead key followed by E.
(in-package #:my-app)
;; xkb keycodes: the evdev code plus 8, as Wayland and X11 give them.
(defconstant +shift+ 50)
(defconstant +a-ring-key+ 34) ; right of P: å on a Swedish keyboard
(defconstant +dead-acute-key+ 21)
(defconstant +e-key+ 26)
(xkb:with-context (context)
(xkb:with-keymap (keymap context :layout "se")
(xkb:with-keyboard-state (state keymap)
(xkb:with-compose-table (table context :locale "en_US.UTF-8")
(xkb:with-compose-state (compose table)
(list (xkb:key-press-text state compose +a-ring-key+)
(progn (xkb:update-key state +shift+ :press)
(list (xkb:active-modifiers state)
(xkb:key-press-text state compose +a-ring-key+)))
(progn (xkb:update-key state +shift+ :release)
;; A dead key types nothing, and composes with the E.
(xkb:key-press-text state compose +dead-acute-key+))
(xkb:key-press-text state compose +e-key+)))))))
;; => ("å" ((:SHIFT) "Å") NIL "é")
What each step does:
with-contextmakes a context, where keymaps and compose tables are compiled.with-keymapcompiles a keymap for the Swedish layout. A keymap says what every key produces with every combination of modifiers.with-keyboard-statemakes a keyboard state, which follows one keyboard: the modifiers held and the active layout.update-keytells it that a key went down or up.with-compose-tableandwith-compose-statehandle compose sequences such as dead keys.key-press-textreturns the text a key press types, with the compose state composing sequences.active-modifierslists the modifiers in effect.
Every with- macro destroys its object when the body exits, normally or
not. The make- and destroy- functions are also available, for objects
that live longer than one dynamic extent. See
Lifetimes and Threads.
Repeatable keymaps
make-keymap uses the system's defaults for every name you leave out, and
by default the environment variables XKB_DEFAULT_RULES,
XKB_DEFAULT_MODEL, XKB_DEFAULT_LAYOUT, XKB_DEFAULT_VARIANT and
XKB_DEFAULT_OPTIONS override them. Make the context with
:environment nil to ignore those variables, for example in tests:
(xkb:with-context (context :environment nil)
(xkb:with-keymap (keymap context :layout "us")
...))