libxkbcommon: The rules file

libxkbcommon 1.9.1 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 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

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

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