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
xkbcommon’s keymap compiler 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
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.
! option = symbols
ctrl:nocaps = +ctrl(nocaps)
compose:menu = +compose(menu)
! 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.
! $azerty = be fr
! 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
$azerty = +aliases(azerty)
$qwertz = +aliases(qwertz)
* = +aliases(qwerty)
Grammar
It is advised to look at a file like rules/evdev along with this grammar.
- <ident>
- Denotes a lexical identifier: a non-empty maximal sequence of printable ASCII characters in the range ! – ~, excluding backslash \. The first character shall not be one of !, =, $, *, or <.
- <kccgst-ident>
- Same as <ident>, except it excludes also merge mode characters +|^ and the qualifier prefix :.
- Note
- Whitespace, except line feed \n, may appear between lexical tokens unless explicitly prohibited by the lexical grammar. It is not shown in the productions below.
-
The sequence consisting of a backslash \, an optional carriage return \r and a line feed \n is discarded during lexical analysis. It serves only to allow a grammar production to span multiple physical lines and does not affect tokenization.
-
A comment begins with // and extends to, but does not include, the next line feed \n or the end of the input.
-
In the following, comments are treated as whitespace and are omitted from the grammar.
File ::= { "!" (Include | Group | RuleSet) }
Include ::= "include" <ident>
Group ::= GroupName "=" { GroupElement } "\n"
GroupName ::= "$"<ident>
GroupElement ::= <ident>
RuleSet ::= Mapping { Rule }
Mapping ::= Mlvo { Mlvo } "=" Kccgst { 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 { MlvoValue } "=" KccgstValue { KccgstValue } "\n"
MlvoValue ::= Wildcard | GroupName | <ident>
Wildcard ::= "*" | "<none>" | "<some>" | "<any>"
KccgstValue ::= <kccgst-value>
The following is a subgrammar for <kccgst-value>:
- Important
- In the following grammar, whitespace is prohibited between lexical tokens unless explicitly allowed.
<kccgst-value> ::= [ MergeMode ] KccgstEntry { MergeMode KccgstEntry }
MergeMode ::= "+" | "|" | "^"
KccgstEntry ::= <kccgst-ident> [ ":" KccgstQualifier ]
KccgstQualifier ::= "%i" | "all" | NumericIndex
- 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 xkbcommon 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[32].
- 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
- 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 %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:
-
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 |
-
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)
! layout[1] = symbols
* = pc+%l[1]%(v[1])
! 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:
! layout[first] = symbols
* = pc
! layout[any] = 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])
! 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
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
! 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])
! option = symbols
opt1 = +opt1
! 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.