键盘的 XKB client map 是 client 需要解析来自键盘的键事件所需的信息集合。它包含 key types 的全局列表,和 key symbol map s 数组,每个 key symbol map 描述了绑定到一个特定键的 symbols 和用来解析这些 symbols 的规则。
符号和术语
XKB 将二维 symbols 数组与每个键相关联。symbols 由键盘 group 和 shift level 进行寻址,其中 level 的定义在 ISO9995标准为:
Level —— 几种状态中的一种(通常为2或3),用于控制图形键按下时产生的图形字符。 在某些情况下,level 也可能影响功能键。
请注意,shift level 是从 modifier state 派生的,但对于所有键不一定以相同的方式。例如,Shift modifier 在大多数键上选择 shift level 2,但在小键盘键上此 modifier 绑定到 Num_Lock(即 NumLock 虚拟 modifier)也选择 shift level 2。
我们使用符号 GnLn 来指定 symbol 在键或内存中的位置:
灰色字符表示暗示或预期的 symbols,但实际上没有刻在键上。
注意
不幸的是,键上的 symbols 的“自然”方向和内存中的自然方向相反,因此键盘 group 指的是键上的一列和内存中的一行。对它没有任何实际帮助,但我们尝试通过使用“group”和“level”(或“shift level”)来最小化混淆,以引用 symbols 而不管上下文。
确定与键事件关联的 KeySym
要查找与 XKB 键事件关联的 symbol,我们需要知道事件对应的 group 和 shift level。
Group 报告在键事件的 state 字段的 bits 13-14。事件中报告的键盘 group 可能超过特定键的范围,因为 groups 的数量可因键而异。每个键的 XKB 描述包含 group info 字段,该字段与全局 groups wrap control 的解释相同,并指定对该键超出范围的 group 的解释。
一旦我们确定了要用于该事件的 group,我们就必须确定 shift level。键的描述包括绑定到该键的每组 symbols 的 key type。给定事件的 modifier,这个 key type 产出一个 shift level 和一组“剩余” modifiers,如下 键 Types 所述。
最后,我们可以使用有效 group 和该 group 的 type 返回的 shift level 去与该键相关联的二维 symbols 数组中查找 symbol。
键 Types
key type 的 map 字段的每个 entry 指定某些 XKB modifier 定义相应的 shift level; 未在 map 中某处明确列出的 modifier 的任何组合产出 shifit level 1。 指定了未绑定的虚拟 modifiers(参见不活跃 Modifier 定义)的 Map entries 不被考虑; 每个 entry 包含自动更新的 active 字段,指示是否应该使用它。
每个 key type 包含一些从 map 的内容派生而来的字段,这些字段报告一些常用值,因此不必经常重新计算它们。numLevels 字段包含任何它的 map entries 报告的最高 shift level;XKB 使用 numLevels 来确保绑定到键的 symbols 数组足够大(key type 报告的 numLevels 也称为其 width)。modifiers 字段报告由该 type 的任何 map entries 考虑的所有真实 modifiers。modifiers 和 numLevels 都由 XKB 自动更新,两者都不能显式更改。
modifiers 字段中指定的任何 modifiers 通常都被使用(参见转换与键事件关联的KeySym),这意味着在事件处理的任何后续阶段都不会考虑它们。 对于极少数情况下,尽管已经习惯于查找 symbol,但仍应考虑 modifier,key type 包含一个可选的 preserve 字段。如果存在 preserve 列表,则每个 entry 对应 key type 的 map entries 中的一个,并且列出如果匹配的 map entry 用于确定 shift level 则不应消耗的 modifiers。
例如,以下 key type 实现核心协议定义的 caps Lock(使用绑定到键的第二个symbol):
type "ALPHABETIC" {
modifiers = Shift+Lock;
map[Shift]= Level2;
map[Lock]= Level2;
map[Shift+Lock]= Level2;
};
这种定义的问题在于我们可以将完全不相关的 symbols 分配给两个 shift levels,且“Caps Lock”将选择第二个 symbol。字母键的另一个定义使用系统例程来大写 keysym:
type "ALPHABETIC" {
modifiers= Shift;
map[Shift]= Level2;
};
当使用此定义应用大写锁定时,我们将从 shift level 1 获取 symbol,并使用特定于系统的大写规则对其进行大写。如果设置 shift 和 caps lock,我们将从 shift level 2 获取 symbol,并尝试大写它,这通常没有效果。Lock 不在 modifiers 字段中,因此它不会被消耗,并且可以用于指示大写变换。
以下 key type 为字母键实现 shift-cancels-caps lock (shift 取消 caps lock)行为:
type "ALPHABETIC" {
modifiers = Shift+Lock;
map[Shift] = Level2;
preserve[Lock]= Lock;
};
考虑可能影响字母键的四种可能状态:无 modifiers、单独 shift、单独 caps lock 或 shift 和 caps lock 一起。该 map 不包含 None (无 modifiers)的显式 entry,因此如果未设置 modifiers,这种 type 的任何 group 返回第一个 keysym。Shift 的 map entry 报告 Level2,因此单独设置 shift 时,这种 type 的任何 group 返回第二个 symbol。单独 Lock 没有 map entry,但 type 指定这种情况下 Lock modifier 应该被保留,因此单独 Lock 返回 group 中第一个 symbol,但首先应用大写转换,从而产生 symbol 的大写体。在最后一种情况下,Shift + Lock 没有 map entry,因此它返回 group 中的第一个 symbol;没有 preserve entry,因此 Lock modifier 被消耗并且 symbol 不是大写体。
Key Symbol Map
键的 key symbol map 包含 client 需要处理该键生成的事件的所有信息。每个 key symbol mapping 报告:
- 绑定到键的 symbols 的 groups 数量 (numGroups)。
- 处理超出范围的 groups (groupInfo)。
- 每个可能的 group 的 key type 的索引 ( kt_index[MaxKbdGroups] )。
- 与键相关的最宽的 type 的宽度 (groupsWidth)。
- 绑定到键的二维(numGroups × groupsWidth)symbols 数组。
键有 0 个 groups 是合法的,这种情况下,它也具有 0 个 symbols,并且来自该键的所有事件都产生 NoSymbol。key types 数组具有固定宽度,并且足够大以容纳最大合法 groups 数量(MaxKbdGroups,当前为 4)的 key types; 如果键有少于 MaxKbdGroups 个 groups,则额外的 key types 被报告但被忽略。groupsWidth 字段无法显式更改;每当更改绑定到键的 symbols 或 types 集时,它都会自动更新。
如果,当查找一个 symbol,有效键盘 group 超过了键的范围, key symbol map 的 groupInfo 字段指定决定相应合法 group 的规则如下: