Key Event Processing in the Client
The XKB
client map
for a keyboard is the collection of information a client needs to interpret
key events that come from that keyboard. It contains a global list of
key types
, described in Key Types,
and an array of
key symbol map
s, each of which describes the symbols bound to one particular key and the
rules to be used to interpret those symbols.
Notation and Terminology
XKB associates a two-dimensional array of symbols with each key. Symbols are
addressed by keyboard group (see
Keyboard State) and shift level, where level is defined as in the
ISO9995 standard:
Level
One of several states (normally 2 or 3) which govern which graphic
character is produced when a graphic key is actuated. In certain cases the
level may also affect function keys.
Note that shift level is derived from the modifier state, but not necessarily
in the same way for all keys. For example, the
Shift
modifier selects shift level 2 on most keys, but for keypad keys the modifier
bound to
Num_Lock
(i.e. the
NumLock
virtual modifier) also selects shift level 2.gray symbols on a key
We use the notation G
n
L
n
to specify the position of a symbol on a key or in memory:
The gray characters indicate symbols that are implied or expected but are not
actually engraved on the key.
Unfortunately, the "natural" orientation of symbols on a key and
the natural orientation in memory are reversed from one another, so keyboard
group refers to a column on the key and a row in memory. There’s no real help
for it, but we try to minimize confusion by using "group" and "level" (or
"shift level") to refer to symbols regardless of context.Determining the KeySym Associated with a Key Event
To look up the symbol associated with an XKB key event, we need to know the
group and shift level that correspond to the event.
Group is reported in bits 13-14 of the state field of the key event, as
described in Computing A State
Field from an XKB State. The keyboard group reported in the event might
be out-of-range for any particular key because the number of groups can vary
from key to key. The XKB description of each key contains a
group info
field which is interpreted identically to the global groups wrap control (see
Computing Effective Modifier and
Group) and which specifies the interpretation of groups that are
out-of-range for that key.
Once we have determined the group to be used for the event, we have to
determine the shift level. The description of a key includes a
key type
for each group of symbols bound to the key. Given the modifiers from the key
event, this key type yields a shift level and a set of "leftover" modifiers, as
described in Key Types
below.
Finally, we can use the effective group and the shift level returned by the
type of that group to look up a symbol in a two-dimensional array of symbols
associated with the key.
Key Types
Each entry of a key type’s
map
field specifies the shift level that corresponds to some XKB modifier
definition; any combination of modifiers that is not explicitly listed
somewhere in the map yields shift level one. Map entries which specify unbound
virtual modifiers (see Inactive
Modifier Definitions) are not considered; each entry contains an
automatically-updated
active
field which indicates whether or not it should be used.
Each key type includes a few fields that are derived from the contents of the
map and which report some commonly used values so they don’t have to be
constantly recalculated. The
numLevels
field contains the highest shift level reported by any of its map entries; XKB
uses
numLevels
to insure that the array of symbols bound to a key is large enough (the number
of levels reported by a key type is also referred to as its width). The
modifiers
field reports all real modifiers considered by any of the map entries for the
type. Both
modifiers
and
numLevels
are updated automatically by XKB and neither can be changed explicitly.
Any modifiers specified in
modifiers
are normally
consumed
(see Transforming the KeySym
Associated with a Key Event), which means that they are not considered
during any of the later stages of event processing. For those rare occasions
that a modifier
should
be considered despite having been used to look up a symbol, key types include
an optional
preserve
field. If a
preserve
list is present, each entry corresponds to one of the key type’s map entries
and lists the modifiers that should
not
be consumed if the matching map entry is used to determine shift level.
For example, the following key type implements caps lock as defined by the core
protocol (using the second symbol bound to the key):
type "ALPHABETIC" {
modifiers = Shift+Lock;
map[Shift]= Level2;
map[Lock]= Level2;
map[Shift+Lock]= Level2;
};
The problem with this kind of definition is that we could assign completely
unrelated symbols to the two shift levels, and "Caps Lock" would choose the
second symbol. Another definition for alphabetic keys uses system routines to
capitalize the keysym:
type "ALPHABETIC" {
modifiers= Shift;
map[Shift]= Level2;
};
When caps lock is applied using this definition, we take the symbol from shift
level one and capitalize it using system-specific capitalization rules. If
shift and caps lock are both set, we take the symbol from shift level two and
try to capitalize it, which usually has no effect.
The following key type implements shift-cancels-caps lock behavior for
alphabetic keys:
type "ALPHABETIC" {
modifiers = Shift+Lock;
map[Shift] = Level2;
preserve[Lock]= Lock;
};
Consider the four possible states that can affect alphabetic keys: no
modifiers, shift alone, caps lock alone or shift and caps lock together. The
map contains no explicit entry for
None
(no modifiers), so if no modifiers are set, any group with this type returns
the first keysym. The map entry for
Shift
reports
Level2
, so any group with this type returns the second symbol when
Shift
is set. There is no map entry for
Lock
alone, but the type specifies that the
Lock
modifier should be preserved in this case, so
Lock
alone returns the first symbol in the group but first applies the
capitalization transformation, yielding the capital form of the symbol. In the
final case, there is no map entry for
Shift+Lock
, so it returns the first symbol in the group; there is no preserve entry, so
the
Lock
modifier is consumed and the symbol is not capitalized.
Key Symbol Map
The
key symbol map
for a key contains all of the information that a client needs to process
events generated by that key. Each key symbol mapping reports:
The number of groups of symbols bound to the key (
numGroups
).
The treatment of out-of-range groups (
groupInfo
).
The index of the key type to for each
possible
group (
kt_index[MaxKbdGroups]
).
The width of the widest type associated with the key (
groupsWidth
).
The two-dimensional (numGroups
×
groupsWidth) array of symbols bound to the key.
It is legal for a key to have zero groups, in which case it also has zero
symbols and all events from that key yield
NoSymbol
. The array of key types is of fixed width and is large enough to hold key
types for the maximum legal number of groups (
MaxKbdGroups
, currently four); if a key has fewer than
MaxKbdGroups
groups, the extra key types are reported but ignored. The
groupsWidth
field cannot be explicitly changed; it is updated automatically whenever the
symbols or set of types bound to a key are changed.
If, when looking up a symbol, the effective keyboard group is out-of-range for
the key, the
groupInfo
field of the key symbol map specifies the rules for determining the
corresponding legal group as follows:
If the
RedirectIntoRange
flag is set, the two least significant bits of
groupInfo
specify the index of a group to which all illegal groups correspond. If the
specified group is also out of range, all illegal groups map to
Group1
.
If
ClampIntoRange
flag is set, out-of-range groups correspond to the nearest legal group.
Effective groups larger than the highest supported group are mapped to the
highest supported group; effective groups less than
Group1
are mapped to
Group1
. For example, a key with two groups of symbols uses
Group2
type and symbols if the global effective group is either
Group3
or
Group4
.
If neither flag is set, group is wrapped into range using integer
modulus. For example, a key with two groups of symbols for which groups wrap
uses
Group1
symbols if the global effective group is
Group3
or
Group2
symbols if the global effective group is
Group4
.
The client map contains an array of key symbol mappings, with one entry for
each key between the minimum and maximum legal keycodes, inclusive. All
keycodes which fall in that range have key symbol mappings, whether or not any
key actually yields that code.
Transforming the KeySym Associated with a Key Event
Any modifiers that were not used to look up the keysym, or which were
explicitly preserved, might indicate further transformations to be performed on
the keysym or the character string that is derived from it. For example, If the
Lock
modifier is set, the symbol and corresponding string should be capitalized
according to the locale-sensitive capitalization rules specified by the system.
If the
Control
modifier is set, the keysym is not affected, but the corresponding character
should be converted to a control character as described in Default Symbol Transformations.
This extension specifies the transformations to be applied when the
Control
or
Lock
modifiers are active but were not used to determine the keysym to be used:
ModifierTransformation
ControlReport the control character associated with the symbol. This
extension defines the control characters associated with the ASCII alphabetic
characters (both upper and lower case) and for a small set of punctuation
characters (see
Default Symbol Transformations).
Applications are
free to associate control characters with any symbols that are not specified by
this extension.
LockCapitalize the symbol either according to capitalization rules
appropriate to the application locale or using the capitalization rules defined
by this extension (see Default Symbol Transformations).
Interpretation of other modifiers is application dependent.
This definition of capitalization is fundamentally different from
the core protocol’s, which uses the lock modifier to select from the symbols
bound to the key. Consider key 9 in the
client map example;
the core protocol provides no way to generate the capital form
of either symbol bound to this key. XKB specifies that we first look up the
symbol and then capitalize, so XKB yields the capital form of the two symbols
when caps lock is active.
XKB specifies the behavior of
Lock
and
Control
, but interpretation of other modifiers is left to the application.
Client Map Example
Consider a simple, if unlikely, keyboard with the following keys (gray
characters indicate symbols that are implied or expected but are not actually
engraved on the key):
The core protocol represents this keyboard as a simple array with one row per
key and four columns (the widest key, key 10, determines the width of the
entire array).
KeyG1L1G1L2G2L1G2L28QNoSymbolatNoSymbol9odiaeresisegraveNoSymbolNoSymbol10ANoSymbolÆNoSymbol11ssharpquestionbackslashquestiondown12KP_EndKP_1NoSymbolNoSymbol13Num_LockNoSymbolNoSymbolNoSymbol14NoSymbolNoSymbolNoSymbolNoSymbol15ReturnNoSymbolNoSymbolNoSymbol
The row to be used for a given key event is determined by keycode; the column
to be used is determined by the symbols bound to the key, the state of the
Shift
and
Lock
Modifiers and the state of the modifiers bound to the
Num_Lock
and
Mode_switch
keys as specified by the core protocol.
The XKB description of this keyboard consists of six key symbol maps, each of
which specifies the types and symbols associated with each keyboard group for
one key:
KeyGroup: TypeL1L28G1: ALPHABETICqQG2: ONE_LEVEL@NoSymbol9G1: TWO_LEVELodiaeresisegrave10G1: ALPHABETICaAG2: ALPHABETICaeAE11G1: TWO_LEVELssharpquestionG2: ONE_LEVELbackslashquestiondown12G1: KEYPADKP_EndKP_113G1: ONE_LEVELNum_Lock14No Groups15G1: ONE_LEVELReturn
The keycode reported in a key event determines the row to be used for that
event; the effective keyboard group determines the list of symbols and key type
to be used. The key type determines which symbol is chosen from the list.
Determining the KeySym Associated
with a Key Event details the procedure to map from a key event to a
symbol and/or a string.