Skip to content

Commit

Permalink
doc(keymap): Improve xkb_type section
Browse files Browse the repository at this point in the history
- Add a diagram to illustrate how key types work.
- Add examples to explain the key types mappings.
  • Loading branch information
wismill committed Mar 17, 2024
1 parent e5b0215 commit fe2b9d8
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 15 deletions.
59 changes: 59 additions & 0 deletions doc/diagrams/xkb-types-explanation.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
digraph
{
active_modifiers [
label="Active modifiers",
href="@ref xkb_state::xkb_state_mod_index_is_active"
];
modifiers_filter [
shape=box,
label=<<b>Filter modifiers</b><br/>Keep only <i>meaningful</i> modifiers for the key type>,
style=rounded
];
filtered_modifiers [label="Filtered modifiers"];
level_match [
shape=box,
label=<<b>Lookup modifiers combination</b><br/>Find the shift level<br/>matching <i>exactly</i> the modifiers,<br/>defaulting to the base level>,
style=rounded
];
shift_level [
label="Shift level",
href="@ref level-def"
];
consume_modifiers [
shape=box,
label=<<b>Consume modifiers</b>>,
style=rounded
];
consumed_modifiers [
label="Consumed modifiers",
href="@ref consumed-modifiers"
];

key_type [shape=none, label=<
<table border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td href="@ref the-xkb_types-section"><b>Key type</b></td></tr>
<hr/>
<tr><td port="modifiers" href="@ref key-type-modifiers">modifiers</td></tr>
<tr><td port="map" href="@ref key-type-map">map</td></tr>
<tr><td port="preserve" href="@ref key-type-preserve">preserve</td></tr>
</table>
>];

{ rank="same"; key_type; filtered_modifiers }
{ rank="same"; level_match; consume_modifiers }

active_modifiers -> modifiers_filter;
key_type:modifiers -> modifiers_filter
// [
// label=<<i>xkb_compat</i> section>,
// labelhref="@ref the-xkb_compat-section"
// ];
//active_modifiers -> filtered_modifiers [label=""];
modifiers_filter -> filtered_modifiers;
filtered_modifiers -> level_match;
key_type:map -> level_match;
level_match -> shift_level;
filtered_modifiers -> consume_modifiers;
key_type:preserve -> consume_modifiers;
consume_modifiers -> consumed_modifiers;
}
185 changes: 170 additions & 15 deletions doc/keymap-format-text-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@ Some additional resources are:
how to derive the active level from the modifiers states. Examples:
- `ONE_LEVEL`: the key has only one level, i.e. it is not affected
by any modifiers. Example: the modifiers themselves.
- `TWO_LEVEL`: the key has two levels:
- [`TWO_LEVEL`](@ref TWO_LEVEL): the key has two levels:
- Level 1: default level, active when the `Shift` modifier is _not_ active.
- Level 2: level activated with the `Shift` modifier.
- `FOUR_LEVEL`: see the example in the previous section.
- [`FOUR_LEVEL`](@ref FOUR_LEVEL): see the example in the previous section.

See [xkb_types] for further details.
</dd>
Expand Down Expand Up @@ -775,6 +775,11 @@ section and by the user.

## The “xkb_types” section {#the-xkb_types-section}

<!--
Ivan Pascal’s doc:
https://web.archive.org/web/20190724070654/http://pascal.tsu.ru/en/xkb/gram-types.html
-->

This [section] is the second to be processed, after `xkb_keycodes`.
However, it is completely independent and could have been the first to
be processed (it does not refer to specific keys as specified in the
Expand All @@ -793,20 +798,50 @@ chosen according to the state of the Num Lock (or Shift) modifiers.
Another example is a type called `ONE_LEVEL`, which is usually
assigned to keys such as Escape; these have just one level and are not
affected by the modifier state. Yet more common examples are
`TWO_LEVEL` (with Shift choosing the second level), `ALPHABETIC`
(where Caps Lock may also choose the second level), etc.
[`TWO_LEVEL`](@ref TWO_LEVEL) (with Shift choosing the second level),
[`ALPHABETIC`](@ref ALPHABETIC) (where Caps Lock may also choose the second level), etc.

### How key types work

Key types define a _mapping_ between the [modifiers] and [shift levels].
Key types have four parameters:

<dl>
<dt>@ref key-type-level-name "Shift level names"</dt>
<dd>Declare [shift levels]. Mainly for documentation</dd>
<dt>@ref key-type-modifiers "Modifiers filter"</dt>
<dd>Declare what modifiers should be taken into account in the mapping.</dd>
<dt>@ref key-type-map "Modifiers mapping"</dt>
<dd>Lookup table to translate modifiers combinations into shift levels</dd>
<dt>@ref key-type-preserve "Modifiers preservation"</dt>
<dd>Tweak the computation of [consumed modifiers]</dd>
</dl>

[consumed modifiers]: @ref consumed-modifiers
[shift levels]: @ref level-def

Key types are used to compute:
- the [shift level][]: see xkb_state::xkb_state_key_get_level().
- the [consumed modifiers][]: see xkb_state::xkb_state_key_get_consumed_mods() and
xkb_state::xkb_state_key_get_consumed_mods2().


The following diagram present an overview of theses computations:

@anchor xkb-types-explanation-diagram
@dotfile xkb-types-explanation "Use of key types to compute shift level and consumed modifiers"

### Type definitions

Statements of the form:

type "FOUR_LEVEL" { ... }

The above would create a new type named `FOUR_LEVEL`.
The above would create a new type named [`FOUR_LEVEL`](@ref FOUR_LEVEL).
The body of the definition may include statements of the following
forms:

#### “level_name” statements
#### “level_name” statements {#key-type-level-name}

level_name[Level1] = "Base";

Expand All @@ -818,7 +853,7 @@ for anything.
Note: A level may be specified as Level\[1-8\] or just a number (can
be more than 8).

#### “modifiers” statement
#### “modifiers” statement {#key-type-modifiers}

modifiers = Shift+Lock+LevelThree;

Expand All @@ -829,7 +864,7 @@ being considered when matching the modifier state against the type.
The other modifiers, whether active or not, are masked out in the
calculation.

#### “map” entry statements
#### “map” entry statements {#key-type-map}

map[Shift+LevelThree] = Level4;

Expand All @@ -842,18 +877,19 @@ above, if in the current keyboard state the `Shift` and `LevelThree`
modifiers are active, while the `Lock` modifier is not, then the
keysym(s) in the 4th level of the group will be returned to the user.

#### “preserve” statements
#### “preserve” statements {#key-type-preserve}

map[Shift+Lock+LevelThree] = Level5;
preserve[Shift+Lock+LevelThree] = Lock;

When a key type is used for keysym translation, its modifiers are said
to be “consumed”. For example, in a simple US keymap, the “g” key
is assigned an ordinary `ALPHABETIC` key type, whose modifiers are
Shift and Lock; then for the “g” key, these two modifiers are consumed
by the translation. This information is relevant for applications
which further process the modifiers, since by then the consumed
modifiers have already “done their part” and should be masked out.
to be “consumed”. For example, in a simple US keymap, the “g” key is
assigned an ordinary [`ALPHABETIC`](@ref ALPHABETIC) key type, whose
modifiers are Shift and Lock; then for the “g” key, these two modifiers
are consumed by the translation. This information is relevant for
applications which further process the modifiers, since by then the
consumed modifiers have already “done their part” and should be masked
out.

However, sometimes even if a modifier had already affected the key
translation through the type, it should *not* be reported as consumed,
Expand All @@ -865,6 +901,125 @@ added). The right hand side should consists of modifiers from the
type's modifiers; these modifiers are then “preserved” and not
reported as consumed.

### Key types examples {#key-type-examples}

#### Definitions examples

The following examples compare two basic types: [`TWO_LEVEL`](@ref TWO_LEVEL)
and [`ALPHABETIC`](@ref ALPHABETIC). They differ on their handling of the `Lock`
modifier. See the [next section](@ref key-type-mappings-examples) for illustrion
with concrete layouts.

<div class="example-container">
<div class="example">
<div class="example-inner">
<div class="example-title">[`TWO_LEVEL`](@ref TWO_LEVEL)</div>
*Definition code:*
```c
type "TWO_LEVEL" {
// Only care about Shift; Lock will be filter out
modifiers = Shift;
// Define mapping
map[None] = Level1; // No modifier -> level 1
map[Shift] = Level2; // Exactly Shift -> level 2

level_name[Level1] = "Base";
level_name[Level2] = "Shift";
};
```
*Mapping test:*
| *Active* modifiers | *Filtered* modifiers | Match? | Shift level |
| ------------------ | -------------------- | ------ | ----------- |
| (none) | (none) | Yes | 1 |
| `Shift` | `Shift` | Yes | 2 |
| `Lock` | (none) | Yes | 1           |
| `Shift + Lock` | `Shift` | Yes | 2 |
</div>
</div>
<div class="example">
<div class="example-inner">
<div class="example-title">[`ALPHABETIC`](@ref ALPHABETIC)</div>
*Definition code:*
```c
type "ALPHABETIC" {
// Only care about Shift and Lock
modifiers = Shift + Lock;
// Define mapping
map[None] = Level1; // No modifier -> level 1
map[Shift] = Level2; // Exactly Shift -> level 2
map[Lock] = Level2; // Exactly Lock -> level 2
level_name[Level1] = "Base";
level_name[Level2] = "Caps";
};
```
*Mapping test:*
| *Active* modifiers | *Filtered* modifiers | Match? | Shift level |
| ------------------ | -------------------- | ------ | ----------- |
| (none) | (none) | Yes | 1 |
| `Shift` | `Shift` | Yes | 2 |
| `Lock` | `Lock` | Yes | 2           |
| `Shift + Lock` | `Shift + Lock` | No | 1 |
</div>
</div>
</div>

#### Mappings examples {#key-type-mappings-examples}

The following table compares the mappings of various key types for the modifiers
`Shift`, `Lock` and `LevelThree`, using the standard layouts `us` (US English)
and `es` (Spanish).

| Key | Layout | Key type | Active modifiers | Level | Keysym | Comment |
| ------ | ------ | ------------------------------- | ------------------------------- | ----- | ------------ | ----------- |
| `AE01` | `us` | `TWO_LEVEL` @anchor TWO_LEVEL | (none) | 1 | `1` | |
| ^ | ^ | ^ | `Shift` | 2 | `exclam` | |
| ^ | ^ | ^ | `Lock` | 1 | `1` | `Lock` filtered out |
| ^ | ^ | ^ | `Shift` + `Lock` | 2 | `exclam` | `Lock` filtered out |
| ^ | ^ | ^ | `LevelThree` | 1 | `1` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 2 | `exclam` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 1 | `1` | Modifiers `LevelThree` and `Lock` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 2 | `exclam` | Modifiers `LevelThree` and `Lock` filtered out |
| ^ | `es` | `FOUR_LEVEL` @anchor FOUR_LEVEL | (none) | 1 | `1` | |
| ^ | ^ | ^ | `Shift` | 2 | `exclam` | |
| ^ | ^ | ^ | `Lock` | 1 | `1` | `Lock` filtered out |
| ^ | ^ | ^ | `Shift` + `Lock` | 2 | `exclam` | `Lock` filtered out |
| ^ | ^ | ^ | `LevelThree` | 3 | `bar` | |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 4 | `exclamdown` | |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 3 | `bar` | `Lock` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 4 | `exclamdown` | `Lock` filtered out |
| `AD01` | `us` | `ALPHABETIC` @anchor ALPHABETIC | (none) | 1 | `q` | |
| ^ | ^ | ^ | `Shift` | 2 | `Q` | |
| ^ | ^ | ^ | `Lock` | 2 | `Q` | |
| ^ | ^ | ^ | `Shift` + `Lock` | 1 | `q` | `Lock` cancelled by `Shift` |
| ^ | ^ | ^ | `LevelThree` | 1 | `q` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 1 | `q` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 2 | `Q` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 1 | `q` | `LevelThree` filtered out, `Lock` cancelled by `Shift` |
| ^ | `es` | `FOUR_LEVEL_SEMIALPHABETIC` | (none) | 1 | `q` | |
| ^ | ^ | ^ | `Shift` | 2 | `Q` | |
| ^ | ^ | ^ | `Lock` | 2 | `Q` | |
| ^ | ^ | ^ | `Shift` + `Lock` | 1 | `q` | `Lock` cancelled by `Shift` |
| ^ | ^ | ^ | `LevelThree` | 3 | `at` | |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 4 | `Greek_OMEGA`| |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 3 | `at` | `Lock` does not affect `LevelThree` combos |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 4 | `Greek_OMEGA`| `Lock` does not affect `LevelThree` combos |
| `AD05` | `us` | `ALPHABETIC` | (none) | 1 | `t` | |
| ^ | ^ | ^ | `Shift` | 2 | `T` | |
| ^ | ^ | ^ | `Lock` | 2 | `T` | |
| ^ | ^ | ^ | `Shift` + `Lock` | 1 | `t` | `Lock` cancelled by `Shift` |
| ^ | ^ | ^ | `LevelThree` | 1 | `t` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 1 | `t` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 2 | `T` | `LevelThree` filtered out |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 1 | `t` | `LevelThree` filtered out, `Lock` cancelled by `Shift` |
| ^ | `es` | `FOUR_LEVEL_ALPHABETIC` | (none) | 1 | `t` | |
| ^ | ^ | ^ | `Shift` | 2 | `T` | |
| ^ | ^ | ^ | `Lock` | 2 | `T` | |
| ^ | ^ | ^ | `Shift` + `Lock` | 1 | `t` | `Lock` cancelled by `Shift` |
| ^ | ^ | ^ | `LevelThree` | 3 | `tslash` | |
| ^ | ^ | ^ | `LevelThree` + `Shift` | 4 | `Tslash` | |
| ^ | ^ | ^ | `LevelThree` + `Lock` | 4 | `Tslash` | |
| ^ | ^ | ^ | `LevelThree` + `Shift` + `Lock` | 3 | `tslash` | `Lock` cancelled by `Shift` |


## The “xkb_compat” section {#the-xkb_compat-section}

Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ You can disable the documentation with -Denable-docs=false.''')
'README.md',
'doc/diagrams/xkb-configuration.dot',
'doc/diagrams/xkb-keymap-components.dot',
'doc/diagrams/xkb-types-explanation.dot',
'doc/doxygen-extra.css',
'doc/introduction-to-xkb.md',
'doc/quick-guide.md',
Expand Down

0 comments on commit fe2b9d8

Please sign in to comment.