- Common Lisp 100%
|
All checks were successful
CI / test (push) Successful in 51s
To the latest commit of cl-ffi, whose allocate boxes no pointer, of Pyssel's step 25. |
||
|---|---|---|
| .forgejo/workflows | ||
| ffi | ||
| src | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| cl-xkbcommon.asd | ||
| CLAUDE.md | ||
| LICENSE | ||
| README.org | ||
| stiftfile | ||
cl-xkbcommon
Keyboards for Common Lisp, through libxkbcommon: compile a keymap, follow the keys pressed, and get the symbols and text they produce, the modifiers held, and compose sequences such as dead keys.
(asdf:load-system :cl-xkbcommon)
(defpackage #:example
(:use #:cl)
(:local-nicknames (#:xkb #:cl-xkbcommon)))
(in-package #:example)
;; 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 "é")
cl-xkbcommon knows nothing about window systems: it takes keycodes and
modifier masks, wherever they come from. Wayland and evdev report a key by
its evdev code, which evdev-keycode turns into the xkb keycode these
functions take. It needs libxkbcommon 1.10 or newer.
Contexts and keymaps
A context is where keymaps and compose tables are compiled. Make one with
make-context, or with-context.
A keymap says what each key produces, in each layout and with each
combination of modifiers. There are two ways to make one:
make-keymapbuilds it from layout names::layout "se","us,se"for two layouts,:variant "dvorak",:options "compose:ralt". Names left out are the system's defaults. Pass:environment niltomake-contextto ignore theXKB_DEFAULT_*environment variables, so the result is repeatable.make-keymap-from-stringreads the text a Wayland compositor sends inwl_keyboard.keymap, or whatkeymap-stringreturns.
keymap-layouts, keymap-modifiers, keymap-modifier-p,
keymap-keycode-range, key-name, find-key and key-repeats-p describe
a keymap. Repeating a held key is the application's job, and
key-repeats-p says which keys should repeat. keyboard-state-keymap
returns the keymap of a keyboard state, so a program that follows a
keyboard can keep just the state.
Keyboard states
A keyboard-state follows one keyboard. There are two ways to feed it:
- With keys:
(update-key state keycode :press)and:release, where the application tracks modifiers itself. - With the modifier masks a Wayland compositor sends in
wl_keyboard.modifiers:(update-modifiers state depressed latched locked layout). Bit n of a mask is modifier n of the keymap.
Both return true when what keys produce changed. Then for any key:
key-press-textreturns the text a press of it types, with dead keys and compose sequences composed (see below):"å","@"with AltGr, ornilfor Shift or an arrow key.key-keysymreturns the keysym it produces, such as the one fora,AorReturn, andkey-textthe text it types without composing.(active-modifiers state)lists the modifiers in effect, such as(:shift :control). The modifiers are:shift,:control,:alt,:super,:caps-lock,:num-lock,:level-3(AltGr),:level-5,:meta,:hyperand:scroll-lock.- For shortcuts,
(key-shortcut state keycode)returns the keysym and the modifiers to match. Shift goes intoA, so Control+Shift+A gives the keysymAand(:control), while Control+A givesaand(:control). Caps Lock and Num Lock change neither. (modifier-active-p state :control)andmodifier-consumed-pask about one modifier, named by a keyword or by its name in the keymap. A keyword the keymap lacks is never in effect;keymap-modifier-psays whether a keymap has it.
Keysyms are integers. keysym-name and find-keysym convert between them
and names such as "Return", and keysym-character and character-keysym
between them and characters such as #\å. Write shortcuts with
character-keysym: (character-keysym #\[) rather than the name
"bracketleft".
Compose
A compose-table holds a locale's compose sequences, such as a dead acute
followed by e making é. There are two ways to make one:
make-compose-tablereads the system's table for a locale, by default the environment's, and the user's~/.XCompose. Not every locale has one: with:errorp nilit returnsnilinstead of signalling, andwith-compose-stateandkey-press-texttakenilas typing without composing.make-compose-table-from-stringreads a table written in the XCompose format.
Pass a compose-state to key-press-text for each key pressed, and it
returns the composed text. To handle sequences yourself, feed it the keysym
of each key pressed with compose-feed, and act on the status it returns:
| Status | What to do |
|---|---|
:nothing |
No sequence is under way: use the key's own text. |
:composing |
The key continues a sequence: type nothing yet. |
:composed |
Type compose-text, then compose-reset. |
:cancelled |
Type nothing, then compose-reset. |
Errors
Errors are xkb-error conditions. When a keymap or compose table does not
compile, the message includes what libxkbcommon logged about it, such as which
file it could not find. take-log-messages returns what else it logged, such
as the lines of a compose table it skipped: the 100 newest messages, or as
many as make-context's :log-limit says. On platforms other than x86-64,
libxkbcommon's messages go to standard error instead.
Every object has a destroy- function and a with- macro. A keyboard state
stays usable after its keymap is destroyed, and a keymap after its context
is destroyed.
Threads
A keymap may be read from several threads at once. Contexts, keyboard states, compose tables and compose states belong to one thread at a time.
Tests
stift test
The tests need libxkbcommon 1.10 or newer, the xkeyboard-config layouts and
the X11 compose tables (Debian and Ubuntu: libxkbcommon0 xkb-data
libx11-data).
License
MIT. See LICENSE.