6 Getting Started
dxtr edited this page 2026-10-17 04:51:55 +02:00

Getting Started

Requirements

  • SBCL.
  • cl-ffi, which is not in Quicklisp: with stift, cl-xkbcommon's stiftfile brings 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 xkbcommon or 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:

  1. with-context makes a context, where keymaps and compose tables are compiled.
  2. with-keymap compiles a keymap for the Swedish layout. A keymap says what every key produces with every combination of modifiers.
  3. with-keyboard-state makes a keyboard state, which follows one keyboard: the modifiers held and the active layout. update-key tells it that a key went down or up.
  4. with-compose-table and with-compose-state handle compose sequences such as dead keys.
  5. key-press-text returns the text a key press types, with the compose state composing sequences. active-modifiers lists 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")
    ...))

Next

  • Concepts explains keycodes, keysyms, levels and modifiers.
  • Recipes shows text input, shortcuts and a Wayland client.