libxkbcommon: The rules file

libxkbcommon 1.13.2 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):

- key codes,
- compatibility,
- geometry (not supported by xkbcommon),
- symbols,
- types.

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

## Rules and rule sets

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)

## Groups

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)

## Wild cards

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

*

  Legacy wild card:

  - For model and options: always match.
  - For layout and variant: match any non-empty value.

  This wild card usually appears near the end of a rule set to set default values.

  Note
    Prefer using the wild cards <some> or <any> for their simpler semantics, as it does not depend on the context.
<none>

  Match empty values

  Since
    1.9.0
<some>

  Match non-empty value

  Since
    1.9.0
<any>

  Match any (optionally empty) value. Its behavior does not depend on the context, contrary to the legacy wild card *.

  This wild card usually appears near the end of a rule set to set default values.

  Since
    1.9.0

! 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 ::= "*" | "<none>" | "<some>" | "<any>" | 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 extended layout indices 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.

      In case the mapping uses an extended layout index, %i corresponds to the index of the matched layout.

      ℹ️ Notes:

      - Added in version 1.8.0
      - Since 1.11.0: This feature can only be used if the rule header has a layout or variant MLVO field.

    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

## Process

First of all, the main rules file <ruleset> is extracted from the provided RMLVO configuration (usually evdev). Then the following files are parsed and their results are collected sequentially to form the final rule sets:

Partial <ruleset>.pre files

  Optional: for each include path <ipath>, parse and append the rule sets of the file <ipath>/rules/<ruleset>.pre, if it exists. Multiple such files may be parsed enabling lightweight composition fo rules files.

  Since
    1.13.0
Main <ruleset> file
  Parse and append the rule sets of the canonical <ruleset> file, e.g. the first file in the included paths matching rules/<ruleset>.
Partial <ruleset>.post files

  Optional: for each include path <ipath>, parse and append the rule sets of the file <ipath>/rules/<ruleset>.post, if it exists. Multiple such files may be parsed enabling lightweight composition fo rules files.

  Since
    1.13.0

The resulting rule sets are equivalent to:

! include <include path 1>/rules/<ruleset>.pre // only if defined
…
! include <include path n>/rules/<ruleset>.pre // only if defined
! include <ruleset> // main rules file
! include <include path 1>/rules/<ruleset>.post // only if defined
…
! include <include path n>/rules/<ruleset>.post // only if defined

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

Important
  Contrary to layouts and variants, the options order in a MLVO configuration (e.g. via xkbcli) is irrelevant for its resolution: only the order of the rules matters. See “Example: layout, option and symbols” for an illustration.

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 ‘|’ or ‘^’ to specify respectively the augment or replace 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.

## Examples

### 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 indices 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 indices 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

### Example: layout-specific options

Using the following example:

! layout[first] = symbols
* = pc
! layout[any] = symbols
* = %l[%i]%(v[%i])
// Not layout-specific
! option = symbols
opt1 = +opt1
// Layout-specific: note the range `[any]` and the `:%i` specifier
! layout[any] option = symbols
* opt2 = +opt2:%i

we would have the following resolutions of symbols:

Layout | Option | Symbols | Comment
--- | --- | --- | ---
be,gb | opt1 | pc+be+gb:2+opt1 | Matched: no layout index
be,gb | opt1!1 | pc+be+gb:2 | No match: not a layout-specific option
be,gb | opt1!2 | pc+be+gb:2 | No match: not a layout-specific option
be,gb | opt2 | pc+be+gb:2+opt2:1+opt2:2 | Matched: all layouts
be,gb | opt2!1 | pc+be+gb:2+opt2:1 | Matched: only specified layout
be,gb | opt2!2 | pc+be+gb:2+opt2:2 | Matched: only specified layout

Note
  Such option should use the attribute layout-specific="true" in the corresponding configItem tag of the XML registry file, so that keyboard configuration can expose this feature in their user interface.