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
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.
- 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:
-
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
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])
! 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