libxkbcommon 1.8.0
Library implementing the XKB specification for parsing keyboard descriptions and handling keyboard state
Loading...
Searching...
No Matches
The rules file

The purpose of the rules file is to map between configuration values that are easy for a user to specify and understand, and the configuration values that the keymap compiler, xkbcomp, uses and understands. The following diagram presents an overview of this process. See the XKB introduction for further details on the components.

XKB keymap configurations

libxkbcommon’s keymap compiler xkbcomp uses the xkb_component_names struct internally, which maps directly to include statements of the appropriate sections (called KcCGST for short):

These are not really intuitive nor straightforward for the uninitiated. Instead, the user passes in a xkb_rule_names struct, which consists of the following fields (called RMLVO for short):

  • the name of a rules file (in Linux this is usually “evdev”),
  • a keyboard model (e.g. “pc105”),
  • a set of layouts (which will end up in different groups, e.g. “us,fr”),
  • a set of variants (used to alter/augment the respective layout, e.g. “intl,dvorak”),
  • a set of options (used to tweak some general behavior of the keyboard, e.g. “ctrl:nocaps,compose:menu” to make the Caps Lock key act like Ctrl and the Menu key like Compose).

Format of the file

The file consists of rule sets, each consisting of rules (one per line), which match the MLVO values on the left hand side, and, if the values match to the values the user passed in, results in the values on the right hand side being added to the resulting KcCGST. See RMLVO resolution process for further details.

// This is a comment
// The following line is a rule header.
// It starts with ‘!’ and introduces a rules set.
// It indicates that the rules map MLVO options to KcCGST symbols.
! option = symbols
// The following lines are rules that add symbols of the RHS when the
// LHS matches an option.
ctrl:nocaps = +ctrl(nocaps)
compose:menu = +compose(menu)
// One may use multiple MLVO components on the LHS
! layout option = symbols
be caps:digits_row = +capslock(digits_row)
fr caps:digits_row = +capslock(digits_row)

Since some values are related and repeated often, it is possible to group them together and refer to them by a group name in the rules.

// Let’s rewrite the previous rules set using groups.
// Groups starts with ‘$’.
// Define a group for countries with AZERTY layouts
! $azerty = be fr
// The following rule will match option `caps:digits_row` only for
// layouts in the $azerty group, i.e. `fr` and `be`.
! layout option = symbols
$azerty caps:digits_row = +capslock(digits_row)

Along with matching values by simple string equality and for membership in a group defined previously, rules may also contain wildcard values “*” with the following behavior:

  • For model and options: always match.
  • For layout and variant: match any non-empty value. These usually appear near the end of a rule set to set default values.
! layout = keycodes
// The following two lines only match exactly their respective groups.
$azerty = +aliases(azerty)
$qwertz = +aliases(qwertz)
// This line will match layouts that are neither in $azerty nor in
// $qwertz groups.
* = +aliases(qwerty)

Grammar

It is advised to look at a file like rules/evdev along with this grammar.

Note
Comments, whitespace, etc. are not shown.
File ::= { "!" (Include | Group | RuleSet) }
Include ::= "include" <ident>
Group ::= GroupName "=" { GroupElement } "\n"
GroupName ::= "$"<ident>
GroupElement ::= <ident>
RuleSet ::= Mapping { Rule }
Mapping ::= { Mlvo } "=" { Kccgst } "\n"
Mlvo ::= "model" | "option" | ("layout" | "variant") [ Index ]
Index ::= "[" ({ NumericIndex } | { SpecialIndex }) "]"
NumericIndex ::= 1..XKB_MAX_GROUPS
SpecialIndex ::= "single" | "first" | "later" | "any"
Kccgst ::= "keycodes" | "symbols" | "types" | "compat" | "geometry"
Rule ::= { MlvoValue } "=" { KccgstValue } "\n"
MlvoValue ::= "*" | GroupName | <ident>
KccgstValue ::= <ident> [ { Qualifier } ]
Qualifier ::= ":" ({ NumericIndex } | "all")
Note
  • Include processes the rules in the file path specified in the ident, in order. %-expansion is performed, as follows:
    %%
    A literal %.
    %H
    The value of the $HOME environment variable.
    %E
    The extra lookup path for system-wide XKB data (usually /etc/xkb/rules).
    %S
    The system-installed rules directory (usually /usr/share/X11/xkb/rules).
    Note: This feature is supported by libxkbcommon but not by the legacy X11 tools.
  • (Since version 1.8.0) The following special indexes can be used to avoid repetition and clarify the semantics:

    single
    Matches a single layout; layout[single] is the same as without explicit index: layout.
    first
    Matches the first layout/variant, no matter how many layouts are in the RMLVO configuration. Acts as both layout and layout[1].
    later
    Matches all but the first layout. This is an index range. Acts as layout[2] .. layout[4].
    any
    Matches layout at any position. This is an index range. Acts as layout, layout[1] .. layout[4].

    When using a layout index range (later, any), the %i expansion can be used in the KccgstValue to refer to the index of the matched layout.

  • The order of values in a Rule must be the same as the Mapping it follows. The mapping line determines the meaning of the values in the rules which follow in the RuleSet.
  • If a Rule is matched, %-expansion is performed on the KccgstValue, as follows:

    %m, %l, %v
    The model, layout or variant, if only one was given (e.g. %l for “us,il” is invalid).
    %l[1], %l[2], …, %v[1], %v[2], …
    Layout or variant for the specified layout Index, if more than one was given, e.g.: %l[1] is invalid for “us” but expands to “us” for “us,de”.
    %+m, %+l, %+l[1], %+l[2], …, %+v, %+v[1], %+v[2], …
    As above, but prefixed with ‘+’. Similarly, ‘|’, ‘-’, ‘_’ may be used instead of ‘+’. See the merge mode documentation for the special meaning of ‘+’ and ‘|’.
    %(m), %(l), %(l[1]), %(l[2]), …, %(v), %(v[1]), %(v[2]), …
    As above, but prefixed by ‘(’ and suffixed by ‘)’.
    :%i, %l[%i], %(l[%i]), etc.
    (Since version 1.8.0) In case the mapping uses a special index, %i corresponds to the index of the matched layout.

    In case the expansion is invalid, as described above, it is skipped (the rest of the string is still processed); this includes the prefix and suffix. This is why one should use e.g. %(v[1]) instead of (%v[1]). See Example: layouts, variants and symbols for an illustration.

  • (Since version 1.8.0) If a Rule is matched, the :all qualifier in the KccgstValue applies the qualified value (and its optional merge mode) to all layouts. If there is no merge mode, it defaults to override +.

    Examples of :all qualified use
    KccgstValue Layouts count Final KccgstValue
    x:all 1 x:1
    2 x:1+x:2
    +x:all 1 +x:1
    3 +x:1+x:2+x:3
    |x:all 1 |x:1
    4 |x:1|x:2|x:3|x:4
    x|y:all 1 x|y:1
    3 x|y:1|y:2|y:3
    x:all+y|z:all 2 x:1+x:2+y|z:1|z:2

RMLVO resolution process

First of all, the rules file is extracted from the provided RMLVO configuration (usually evdev). Then its path is resolved and the file is parsed to get the rule sets.

Then each rule set is checked against the provided MLVO configuration, following their order in the rules file.

If a rule matches in a rule set, then:

  1. The KcCGST value of the rule is used to update the KcCGST configuration, using the following instructions. Note that foo and bar are placeholders; ‘+’ specifies the override merge mode and can be replaced by ‘|’ to specify the augment merge mode instead.

    Rule value Old KcCGST value New KcCGST value
    bar bar
    bar foo foo (skip bar)
    bar +foo bar+foo (prepend)
    +bar +bar
    +bar foo foo+bar
    +bar +foo +foo+bar
  2. The rest of the set will be skipped, except if the set matches against options. Indeed, those may contain multiple legitimate rules, so they are processed entirely. See Example: layout, option and symbols for an illustration.

Example: key codes

Using the following example:

! $jollamodels = jollasbj
! $azerty = be fr
! $qwertz = al ch cz de hr hu ro si sk
! model = keycodes
$jollamodels = evdev+jolla(jolla)
olpc = evdev+olpc(olpc)
* = evdev
! layout = keycodes
$azerty = +aliases(azerty)
$qwertz = +aliases(qwertz)
* = +aliases(qwerty)

we would have the following resolutions of key codes:

Model Layout Keycodes
jollasbj us evdev+jolla(jolla)+aliases(qwerty)
olpc be evdev+olpc(olpc)+aliases(azerty)
pc al evdev+aliases(qwertz)

Example: layouts, variants and symbols

Using the following example:

! layout = symbols
* = pc+%l%(v)
// The following would not work: syntax for *multiple* layouts
// in a rule set for *single* layout.
//* = pc+%l[1]%(v[1])
! layout[1] = symbols
* = pc+%l[1]%(v[1])
// The following would not work: syntax for *single* layout
// in a rule set for *multiple* layouts.
//* = pc+%l%(v)
! layout[2] = symbols
* = +%l[2]%(v[2]):2
! layout[3] = symbols
* = +%l[3]%(v[3]):3

we would have the following resolutions of symbols:

Layout Variant Symbols Rules sets used
us pc+us #1
us intl pc+us(intl) #1
us,es pc+us+es:2 #2, #3
us,es,fr intl,,bepo pc+us(intl)+es:2+fr(bepo):3 #2, #3, #4

Since version 1.8.0, the previous code can be replaced with simply:

! layout[first] = symbols
* = pc+%l[%i]%(v[%i])
! layout[later] = symbols
* = +%l[%i]%(v[%i]):%i

Example: layout, option and symbols

Using the following example:

! $azerty = be fr
! layout = symbols
* = pc+%l%(v)
! layout[1] = symbols
* = pc+%l[1]%(v[1])
! layout[2] = symbols
* = +%l[2]%(v[2])
// Repeat the previous rules set with indexes 3 and 4
! layout option = symbols
$azerty caps:digits_row = +capslock(digits_row)
* misc:typo = +typo(base)
* lv3:ralt_alt = +level3(ralt_alt)
! layout[1] option = symbols
$azerty caps:digits_row = +capslock(digits_row):1
* misc:typo = +typo(base):1
* lv3:ralt_alt = +level3(ralt_alt):1
// Repeat the previous rules set for indexes 2 to 4

we would have the following resolutions of symbols:

Layout Option Symbols
be caps:digits_row pc+be+capslock(digits_row)
gb caps:digits_row pc+gb
fr misc:typo pc+fr+typo(base)
fr misc:typo,caps:digits_row pc+fr+capslock(digits_row)+typo(base)
fr lv3:ralt_alt,caps:digits_row,misc:typo pc+fr+capslock(digits_row)+typo(base)+level3(ralt_alt)
fr,gb caps:digits_row,misc:typo pc+fr+gb:2+capslock(digits_row)+typo(base):1+typo(base):2

Note that the configuration with gb layout has no match for the option caps:digits_row and that the order of the options in the RMLVO configuration has no influence on the resulting symbols, as it depends solely on their order in the rules.

Since version 1.8.0, the previous code can be replaced with simply:

! $azerty = be fr
! layout[first] = symbols
* = pc+%l[%i]%(v[%i])
! layout[later] = symbols
* = +%l[%i]%(v[%i])
! layout[any] option = symbols
$azerty caps:digits_row = +capslock(digits_row):%i
! option = symbols
misc:typo = +typo(base):all
lv3:ralt_alt = +level3(ralt_alt):all
// The previous is equivalent to:
! layout[any] option = symbols
* misc:typo = +typo(base):%i
* lv3:ralt_alt = +level3(ralt_alt):%i