The rules file {#rule-file-format} ============== 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. @dotfile xkb-configuration "XKB keymap configurations" @tableofcontents{html:2} `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](@ref geometry-support) 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][layout] (which will end up in different groups, e.g. “us,fr”), - a set of [variants][variant] (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). [KcCGST]: @ref KcCGST-intro [RMLVO]: @ref RMLVO-intro [MLVO]: @ref RMLVO-intro [XKB introduction]: @ref xkb-intro [include statements]: @ref xkb-include [sections]: @ref keymap-section-def [key codes]: @ref the-xkb_keycodes-section [compatibility]: @ref the-xkb_compat-section [symbols]: @ref the-xkb_symbols-section [types]: @ref the-xkb_types-section [rules]: @ref config-rules-def [model]: @ref config-model-def [layout]: @ref config-layout-def [variant]: @ref config-variant-def [option]: @ref config-options-def [options]: @ref config-options-def # Format of the file ## Rules and rule sets {#rule-def} @anchor rule-set-def 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][value update] to the resulting [KcCGST]. See @ref rmlvo-resolution for further details. [rule set]: @ref rule-set-def [rule sets]: @ref rule-set-def [rule]: @ref rule-def ```c // 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 {#rules-group-def} 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. [group]: @ref rules-group-def ```c // 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 {#rules-wildcard-def} 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:
* @anchor rules-wildcard-legacy-def
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 @ref rules-wildcard-some-def "\" or @ref rules-wildcard-any-def "\" for their simpler semantics, as it does not depend on the context.
\ @anchor rules-wildcard-none-def
Match *empty* values @since 1.9.0
\ @anchor rules-wildcard-some-def
Match *non-empty* value @since 1.9.0
\ @anchor rules-wildcard-any-def
Match *any* (optionally empty) value. Its behavior does not depend on the context, contrary to the legacy wild card @ref rules-wildcard-legacy-def "*". This wild card usually appears near the end of a rule set to set *default* values. @since 1.9.0
```c ! 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. ```bnf File ::= { "!" (Include | Group | RuleSet) } Include ::= "include" Group ::= GroupName "=" { GroupElement } "\n" GroupName ::= "$" GroupElement ::= 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" | "multiple" | "any" Kccgst ::= "keycodes" | "symbols" | "types" | "compat" | "geometry" Rule ::= { MlvoValue } "=" { KccgstValue } "\n" MlvoValue ::= "*" | "" | "" | "" | GroupName | KccgstValue ::= [ { Qualifier } ] Qualifier ::= ":" ({ NumericIndex } | "all") ``` @note - Include processes the rules in the file path specified in the `ident`, in order. **%-expansion** is performed, as follows: @anchor rules-include-expansion
`%%`
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. - @anchor rules-extended-layout-indices (Since version `1.8.0`) The following *extended layout indices* can be used to avoid repetition and clarify the semantics:
`single` @anchor rules-layout-index-single
Matches a single layout; `layout[single]` is the same as without explicit index: `layout`.
`first` @anchor rules-layout-index-first
Matches the first layout/variant, no matter how many layouts are in the RMLVO configuration. Acts as both `layout` and `layout[1]`.
`later` @anchor rules-layout-index-later
Matches all but the first layout. This is an index *range*. Acts as `layout[2]` .. `layout[32]`.
`multiple` @anchor rules-layout-index-multiple
Matches layouts at any position, but only if there are *at least 2 layouts*. This is an index *range*. Acts as `layout[1]` .. `layout[32]`. Available since version `1.14.0`.
`any` @anchor rules-layout-index-any
Matches layouts at any position. This is an index *range*. Acts as `layout`, `layout[1]` .. `layout[32]`.
When using a layout index *range* (`later`, `multiple`, `any`), the @ref rules-i-expansion "%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][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 ‘)’.
@anchor rules-i-expansion `:%%i`, `%%l[%%i]`, `%(l[%%i])`, etc.
In case the mapping uses an @ref rules-extended-layout-indices "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 @ref rules-symbols-example for an illustration. - @anchor rules-all-qualifier (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 {#rmlvo-resolution} ## Process First of all, the main rules *file* `` is extracted from the provided [RMLVO][RMLVO] configuration (usually `evdev`). Then the following files are parsed and their results are collected sequentially to form the final [rule sets][]:
Partial `.pre` files
*Optional:* for each include path ``, parse and append the [rule sets] of the file `/rules/.pre`, if it exists. Multiple such files may be parsed enabling lightweight composition fo rules files. @since 1.13.0
Main `` file
Parse and append the [rule sets] of the *canonical* `` file, e.g. the *first* file in the included paths matching `rules/`.
Partial `.post` files
*Optional:* for each include path ``, parse and append the [rule sets] of the file `/rules/.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 /rules/.pre // only if defined … ! include /rules/.pre // only if defined ! include // main rules file ! include /rules/.post // only if defined … ! include /rules/.post // only if defined ``` Then *each rule set* is checked against the provided [MLVO] configuration, following their *order* in the rules files. @important @anchor irrelevant-options-order 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 “@ref rules-options-example ""” for an illustration. If a [rule] matches in a @ref rule-set-def "rule set", then:
  1. @anchor rules-kccgst-value-update 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 @ref rules-options-example for an illustration.
[value update]: @ref rules-kccgst-value-update [merge mode]: @ref merge-mode-def ## Examples ### Example: key codes Using the following example: ```c ! $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 {#rules-symbols-example} Using the following example: ```c ! 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 ! layout[4] = symbols * = +%l[4]%(v[4]):4 ``` 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: ```c ! layout[first] = symbols * = pc ! layout[any] = symbols * = +%l[%i]%(v[%i]):%i ``` ### Example: layout, option and symbols {#rules-options-example} Using the following example: ```c ! $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: ```c ! $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 {#rules-layout-specific-options-example} Using the following example: ```c ! 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.