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

Modifier | Transformation
--- | ---
Control | Report 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.
Lock | Capitalize 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).

Key | G1L1 | G1L2 | G2L1 | G2L2
--- | --- | --- | --- | ---
8 | Q | NoSymbol | at | NoSymbol
9 | odiaeresis | egrave | NoSymbol | NoSymbol
10 | A | NoSymbol | Æ | NoSymbol
11 | ssharp | question | backslash | questiondown
12 | KP_End | KP_1 | NoSymbol | NoSymbol
13 | Num_Lock | NoSymbol | NoSymbol | NoSymbol
14 | NoSymbol | NoSymbol | NoSymbol | NoSymbol
15 | Return | NoSymbol | NoSymbol | NoSymbol

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:

Key | Group: Type | L1 | L2
--- | --- | --- | ---
8 | G1: ALPHABETIC | q | Q
G2: ONE_LEVEL | @ | NoSymbol
9 | G1: TWO_LEVEL | odiaeresis | egrave
10 | G1: ALPHABETIC | a | A
G2: ALPHABETIC | ae | AE
11 | G1: TWO_LEVEL | ssharp | question
G2: ONE_LEVEL | backslash | questiondown
12 | G1: KEYPAD | KP_End | KP_1
13 | G1: ONE_LEVEL | Num_Lock |
14 | No Groups |  |
15 | G1: ONE_LEVEL | Return |

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.