X 键盘扩展
X 键盘扩展(X keyboard extension),简称 XKB,定义了 X 中键盘码的处理方式,并提供了对内部转换表的访问。它是使用 X 中多个键盘布局的基本机制。
本文档描述了如何修改和创建键盘布局。如果您想了解如何配置键盘,请参阅 Xorg/Keyboard configuration。
注意事项和准备工作
为了应对 X 服务器崩溃或键盘进入无法使用的状态的可能性,请做好准备:
- 确保您有一种不使用键盘即可终止会话的方式。(除了电源按钮,拥有远程 `killall X` 或重启主机的能力可能也是个好主意。)
- 确保您已保存所有工作,以防止数据丢失。
- 如果您使用 GNOME,可以安装 gnome-tweaks,以便使用鼠标恢复设置。例如,当键盘变得无法使用时,用鼠标导航到“键盘和鼠标 > 其他布局选项”,然后勾选和取消勾选所有设置,这将把键盘重置为系统默认行为。
获取和设置 XKB 布局
使用规则
查看 /usr/share/X11/xkb/rules/ 中的 *.lst 文件或 XKB 主页,以获取关于如何配置规则的灵感。您自己的配置可以放在 /etc/X11/xorg.conf.d/ 中。
例如,您可能想将 `Caps Lock` 键重新映射为 `Escape`。
90-custom-kbd.conf
Section "InputClass"
Identifier "keyboard defaults"
MatchIsKeyboard "on"
Option "XKbOptions" "caps:escape"
EndSection
使用键映射
使用 `xkbcomp(1)`(软件包 xorg-xkbcomp)来操作 XKB 数据。要获取当前配置,请运行:
$ xkbcomp $DISPLAY output.xkb
要将数据上传回服务器,请运行:
$ xkbcomp input.xkb $DISPLAY
请注意,如果没有 `$DISPLAY` 参数,`xkbcomp(1)` 将尝试将 .xkb 文件编译为(几乎无用的).xkm 文件,而不会将任何内容上传到服务器。但是,它会检查语法并报告错误。
准备好布局后,将其保存为 `~/.Xkeymap` 并让 `~/.xinitrc` 在启动时加载它:
~/.xinitrc
... test -f ~/.Xkeymap && xkbcomp ~/.Xkeymap $DISPLAY
实际文件名无关紧要。请注意,与通过 `xorg.conf(5)` 进行标准的系统范围配置不同,这是一个用户级别的键映射。另外,在 X 运行时更改 XKB 配置也没有问题。
XKB 基本信息
XKB 的核心功能非常简单,在处理键映射之前了解它是如何工作的很有必要。
工具和值
使用 `xev`(软件包 xorg-xev)来获取键码并检查您的键映射工作情况。
$ xev -event keyboard
KeyPress event, serial 45, synthetic NO, window 0x2200001,
root 0xad, subw 0x0, time 183176240, (796,109), root:(867,413),
state 0x1, keycode 21 (keysym 0x2b, plus), same_screen YES,
XLookupString gives 1 bytes: (2b) "+"
XmbLookupString gives 1 bytes: (2b) "+"
XFilterEvent returns: False
注意键码 `21`,状态 `0x1` 和键符号 `0x2b`(即 `plus`)。键码 `21` 是输入设备提供给 X 的,通常是某种物理按键索引。状态代表修饰键,`0x01` 是 `Shift`。键码与状态值一起是 X 在 `XKeyEvent(3)` 结构中发送给应用程序的内容。键符号及其对应的字符串是客户端使用 `XLookupString(3)` 及相关函数获得的。
状态字段中的位具有预定义名称:`Shift`、`Lock`、`Control`、`Mod1`、`Mod2`、`Mod3`、`Mod4` 和 `Mod5`,从低到高。因此,`Ctrl+Shift` 是 `0x05`,依此类推。客户端应用程序通常只检查它们需要的位,因此一个具有正常键盘输入和 `Ctrl+key` 快捷键的应用程序通常不会区分 `Control` 和 `Control+Mod3` 状态。
键符号也是数字。其中许多都有名称,在 `/usr/include/X11/keysymdef.h` 中以 `KP_` 为前缀声明。然而,数字才是客户端实际收到的。键符号只有在应用程序期望某些特定值时才重要;通常是箭头、回车、退格、F 键以及各种快捷键。对于其他键,则使用字符串。
键码转换
XKB 主要在 `XLookupString` 阶段工作,根据其自身的内部状态(组和状态值)将输入的键码转换为键符号。
(keycode, group, state) → keysym
组通常代表“布局”,例如美式英语、法语 AZERTY、俄语、希腊语等。最多可以有 4 个组。
内部转换涉及额外的步骤:
(keycode [, group]) → type (state, type) → level (keycode, group, level) → S[keycode][group][level]
其中 `S` 是转换表(实际上称为 `xkb_symbols`,见下文)。
类型用于指示哪些修饰键会影响哪些按键;本质上,这是一种减少 `S` 的第三维的方法。例如,一个典型的字母数字键仅受 `Shift` 影响,因此其类型设置为 `TWO_LEVEL`,并且:
(state, TWO_LEVEL) → level = ((state >> 0) & 0x01) = state & 0x01
为 `0` 或 `1`。因此,它变成了 `S[keycode][0..4][0..1]` 而不是 `S[keycode][0..4][0..256]`。
键符号和状态
在 X 的术语中,`a` 和 `Ctrl+a` 表示相同的键符号和不同的状态,但 `a` 和 `A` 是不同的键符号。
通常,XKB 的任务是提供不同的键符号,但状态由各个应用程序稍后处理。
另外,XKB 中的状态具有一定的延迟效果,也就是说,您必须在按下按键之前设置好状态。
示例:`Ctrl+h` 可以在 `rxvt`(应用程序设置)中配置为充当退格键。这样,`rxvt` 将收到 `h` 键符号,其状态值中 `Control` 位被设置,它将与 `Backspace` 键符号明显区分开。或者,XKB 可以用于使 `Ctrl+h` 组合生成 `Backspace` 键符号,并且 `Control` 位被设置;在这种情况下,只要 `Ctrl` 键被按下,`rxvt` 就不会看到物理 `Backspace` 键和 `h` 键之间的任何区别。使 `Ctrl+h` 组合生成 `Backspace` 键符号且 `Control` 位未被设置也是 XKB 的任务,但实现起来比 `Control+Backspace` 困难得多。
操作
从上表获得的键符号也可以触发某些动作。
(keysym, state) → action
对于 XKB 来说,设置或锁定一个修饰键位是一个动作,任何 X 服务器交互,如切换控制台、终止服务器、移动指针等,也都是动作。动作通常不影响键符号,生成键符号也不是动作。
对于每个(键符号,状态)对,只有一个可能的动作。
编辑布局
从服务器的任何默认配置开始。尽可能逐步进行更改并进行测试。
`xkbcomp(1)` 生成的 .xkb 文件是一个简单的文本文件。允许使用 C++ 风格的注释,即 // 直到行尾。段名,例如 `xkb_keycodes "name-here"`,此时是无关紧要的,可以省略。
xkb_keycodes
键码定义。文件的其余部分不使用数字键码,只使用此部分定义的符号键标签。
最好只保留您实际拥有的键盘上的键。
标签本身是任意的。它们仅在后面的 `xkb_symbols` 部分使用。
xkb_types
此部分在 `xkb_symbols` 之前,所以可以看一下,但暂时不要尝试修改。标准类型很大程度上依赖于虚拟修饰键,这将在后面解释。目前,只需找到您需要的类型。从以下开始:`ONE_LEVEL`、`TWO_LEVEL`、`ALPHABETIC`。
`ONE_LEVEL` 键不受修饰键影响;通常是回车、空格、Escape、F 键、Shift/Alt/Ctrl 键等。`TWO_LEVEL` 和 `ALPHABETIC` 键根据 Shift 状态产生不同的键符号。所有字母数字键都属于这些类型。`ALPHABETIC` 还会考虑 `CapsLock`。
类型本身的描述非常简单。这一行:
modifiers= Shift+NumLock+LevelThree;
表示此类型的键受 Shift、NumLock 和 LevelThree 位的影响。类似 `map` 这样的行:
map[Shift+LevelThree]= Level4;
定义哪个组合对应哪个级别值。`xkbcomp(1)` 在转储数据时使用“LevelN”,但也可以使用简短得多的“N”。
`level_name` 行无关紧要,可以忽略。
xkb_compatibility
动作定义(`interpret`)和键盘 LED(`indicator`)等。您可以删除您没有或不使用的内容,例如数字键盘操作、鼠标控制或额外的修饰键。
注意 `key+AnyOfOrNone(all)` 等同于 `key`,但 `key` 更易读。
如果需要,请检查组切换。如果您有四个组,`LockGroup(group=N)` 可能很有用,否则 `ISO_Next_Group`/`ISO_Prev_Group` 就足够了。`LatchGroup` 可能对不寻常的设置很有用。
xkb_symbols
定义每个按键作用的主要部分。语法:
key <LABL> { [ G1L1, G1L2, G1L3, ... ], [ G2L1, G2L2, G2L3, ... ], ... }
<LABL> 是来自 `xkb_keycodes` 部分的键标签,`GiLj` 是组 i、级别 j 的键符号。每个组中的键符号数量必须与此类型定义的级别数量匹配(如果数量不匹配,`xkbcomp(1)` 会发出警告)。
查看 `/usr/include/X11/keysymdef.h` 获取可能的键符号列表。除了列出的符号,您还可以使用 `Unnnn` 来表示十六进制代码为 nnnn 的 Unicode 符号,例如 `U0301` 表示组合重音符号。请注意,`a` 和 `U0061` 的处理方式不同(例如,大多数应用程序期望 `Ctrl+a`,而不是 `Ctrl+U0061`,因为它们的数值不同)。
按键类型在此处也指定,方式为:
key.type = "T1";
key <...> { ... };
key <...> { ... };
key <...> { ... };
key.type = "T2";
key <...> { ... };
key <...> { ... };
或者为每个按键单独指定:
key <...> { type = "T", [ .... ], [ .... ] };
在不同的组中,按键类型可能不同。这有些违反直觉,但实际上有一些有用的应用。要为每个组设置类型,请使用此:
key <...> { type[1] = "T1", type[2] = "T2", [ ... ], [ ... ] };
您可以使用以下方式设置组标签:
name[1] = "EN"; // group 1 name[2] = "RU"; // group 2 name[3] = "UA"; // group 3
如果 `xxkb(1)` 启用了标签,它将显示这些标签。
该部分还包含 `modifier_map` 行。暂时不要动它们,或查看下面的“虚拟修饰键”。
xkb_geometry
完全无关的部分,描述了物理键盘布局。可以删除而没有任何后果。
基本示例
先检查您现有的布局,因为它可能包含许多常用按键的标准定义。
在整个文本中,“xkb_keycodes { text }”表示“text”应该添加到 `xkb_keycodes` 部分。在上下文清楚的情况下,段名将被省略。
简单的按键分配
启用附加的(又称多媒体)按键。
xkb_keycodes {
<VOL-> = 122; // check with xev
<VOL+> = 123;
}
xkb_symbols {
key.type = "ONE_LEVEL";
key <VOL-> { [ XF86AudioLowerVolume ] };
key <VOL+> { [ XF86AudioRaiseVolume ] };
}
Caps Lock 键映射为 Escape,主要面向 Vim 用户。
key.type = "ONE_LEVEL";
key <CAPS> { [ Escape ] };
交换 Ins 和 PrintScreen 键(如果它们颠倒了——发生在 Dell 笔记本键盘上)。
key.type = "ONE_LEVEL";
key <IN?> { [ Print ] };
key <PRSC> { [ Insert ] };
在某些 HP 笔记本键盘上,上述方法不起作用。反而需要重新定义键码本身:
partial xkb_keycodes "insert" {
alias <I118> = <IN?>;
<INS> = 218;
<I218> = 118;
};
将 Shift 更改为粘滞键版本。
替换:
key <LFSH> { [ Shift_L ] };
与
key <LFSH> { [ ISO_Level2_Latch ] };
您可能还需要将以下内容添加到 `/usr/share/X11/xkb/compat/basic`:
interpret ISO_Level2_Latch+AnyOf(all) {
useModMapMods=level1;
action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
};
interpret ISO_Level2_Latch+AnyOfOrNone(all) {
action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
};
多布局
对于常规字母数字键,只需在按键定义中添加第二个/第三个/第四个 `[ ]` 部分:
key.type = "ALPHABETIC";
key <AD01> { [ q, Q ], [ a, A ] }; // QWERTY-AZERTY
key <AC02> { [ s, S ], // two cyrillic layouts
[ U044B, U042B ],
[ U0456, U0406 ] };
布局切换通过触发 `LockGroup` 动作来完成。
interpret ISO_Next_Group { action = LockGroup(group=+1); };
interpret ISO_Prev_Group { action = LockGroup(group=-1); };
通常这意味着将 `ISO_Next_Group` 和 `ISO_Prev_Group` 键符号放在正确的组/级别位置。请注意,组是循环的,因此如果您有两个组并两次按下 `ISO_Next_Group`,您将回到您开始的组。
使用专用按键在两个或多个布局之间循环切换。
key.type = "ONE_LEVEL";
key <RWIN> { [ ISO_Next_Group ] }
如果您有两个以上的布局并且有一些额外的按键,为每个布局设置一个专用按键可能是更好的选择。例如,用于三个布局:
key.type = "ONE_LEVEL";
key <RCTL> { [ ISO_Next_Group ], // g1: switch to g2
[ ISO_Prev_Group ], // g2: switch back to g1
[ ISO_Prev_Group ] }; // g3: switch to g2
key <MENU> { [ ISO_Prev_Group ], // g1: switch to g3
[ ISO_Next_Group ], // g2: switch to g3
[ ISO_Next_Group ] }; // g3: switch back to g1
使用四个布局时,您可能需要使用 `ISO_First_Group` 和 `ISO_Last_Group`。
可以使用单个按键通过使用 `TWO_LEVEL` 类型来实现相同的想法:
key.type = "TWO_LEVEL";
key <MENU> { [ ISO_Next_Group, ISO_Prev_Group ],
[ ISO_Prev_Group, ISO_Next_Group ],
[ ISO_Prev_Group, ISO_Next_Group ] };
这样,`Menu` 键对应组 2,`Shift-Menu` 键对应组 3。要使用 `Ctrl` 或 `Alt` 而不是 `Shift`,请分别将 `TWO_LEVEL` 替换为 `PC_CONTROL_LEVEL2` 或 `PC_ALT_LEVEL2` 类型。
通过两个修饰键(Shift+Shift、Ctrl+Shift 等)切换可以通过为这些键使用 `ONE_LEVEL` 以外的类型来实现。Shift+Shift 示例:
key.type = "TWO_LEVEL";
key <LFSH> { [ Shift_L, ISO_Prev_Group ] };
key <RTSH> { [ Shift_R, ISO_Next_Group ] };
要锁定组(即临时切换;仅在你按住按键时有效),请使用 `LatchGroup` 动作,通常绑定到 `ISO_Group_Latch` 键符号。
key <RCTL> { [ ISO_Group_Latch ] }
在 `xkb_compatibility` 部分调整 `ISO_Group_Latch` 定义以使用正确的组。
interpret ISO_Group_Latch { action = LatchGroup(group=3); };
查看 `/usr/share/X11/xkb/symbols/group` 获取更多标准示例。
Caps hjkl 作为 vim 风格的箭头键
创建清除按键的修饰键的键映射是必要的,如果目标按键要用于键盘快捷键。例如,从主键盘(`Shift+Left`)突出显示文本,或在大多数信使中切换聊天(`Alt+Down`)将不起作用,如果发送了额外的 `Caps` 修饰键。然而,如果用户通过简单地将键符号放入符号部分来重新绑定字母键,则必须发送一个额外的修饰键。如下重新绑定允许类似 AHK 的 blind 命令的功能。
`types` 部分(定义了层映射)必须包含一个条目,使得:
- 当没有按下修饰键时,使用第一级键符号(小写字母)。
- 当仅按下 Shift 键时,使用第二级键符号(大写字母)。
- 当仅按下 Lock 键时,使用第三级键符号(箭头键)。
- 当按下 Shift 和 Lock 键时,也使用第三级键符号(Shift+箭头键)。
将此添加到您的 `types` 部分的末尾:
xkb_types "complete" {
...
type "CUST_CAPSLOCK" {
modifiers= Shift+Lock;
map[Shift] = Level2; //maps shift and no Lock. Shift+Alt goes here, too, because Alt isn't in modifiers.
map[Lock] = Level3;
map[Shift+Lock] = Level3; //maps shift and Lock. Shift+Lock+Alt goes here, too.
level_name[Level1]= "Base";
level_name[Level2]= "Shift";
level_name[Level3]= "Lock";
};
};
现在,通过修改 `compatibility` 中已有的 `LockMods` 到 `SetMods` 的定义,将 Caps Lock 从切换(toggle)改为设置(press):
(这意味着您无法像正常那样使用 Caps Lock 了)。
xkb_compatibility "complete" {
...
interpret Caps_Lock+AnyOfOrNone(all) {
action= SetMods(modifiers=Lock);
};
...
};
最后,像这样修改您的符号文件:
xkb_symbols "pc_us_inet(evdev)" {
...
key <AC06> {
type= "CUST_CAPSLOCK",
symbols[Group1]= [ h, H, Left],
actions[Group1]= [ NoAction(), NoAction(), RedirectKey(keycode=<LEFT>, clearmods=Lock) ]
};
附加符号
用相同的按键输入更多内容。
组合键
易于设置,对于输入常用 Unicode 字符非常有用。
key <RALT> { [ Multi_key ] };
Level3
这个想法与 Alt 或 AltGr 的原始含义类似:字母数字键获得额外的字符,通过按住某个修饰键来激活。
首先,设置修饰键。
xkb_symbols {
key <LWIN> { [ISO_Level3_Shift ] };
modifier_map Mod5 { ISO_Level3_Shift };
}
另外,以下内容应该已经在相关部分定义,但以防万一:
xkb_compatibility {
interpret ISO_Level3_Shift { action= SetMods(modifiers=Mod5); };
}
xkb_types {
type "THREE_LEVEL" {
modifiers= Shift+Mod5;
map[Shift]= Level2;
map[Mod5]= Level3;
map[Shift+Mod5]= Level3;
level_name[Level1]= "Base";
level_name[Level2]= "Shift";
level_name[Level3]= "Level3";
};
type "FOUR_LEVEL" {
modifiers= Shift+LevelThree;
map[Shift]= Level2;
map[LevelThree]= Level3;
map[Shift+LevelThree]= Level4;
level_name[Level1]= "Base";
level_name[Level2]= "Shift";
level_name[Level3]= "Alt Base";
level_name[Level4]= "Shift Alt";
};
}
请注意,标准定义在 `xkb_compatibility` 和 `xkb_types` 中使用 `LevelThree` 而不是 `Mod5`。只要上面的 `modifier_map` 使用 `Mod5`,就没有实际区别,您最终将使用 `Mod5` 位。
现在,按键本身,此处为 vi 风格的指针:
key.type = "THREE_LEVEL";
key <AC06> { [ h, H, Left ] };
key <AC07> { [ j, J, Down ] };
key <AC08> { [ k, K, Up ] };
key <AC09> { [ l, L, Right ] };
正如您通过 `xev(1)` 可能发现的,这会产生 `Mod5+Left` 而不是 `Left`。但没关系,因为大多数应用程序会忽略它们不使用的状态位。有关替代解决方案,请查看下面的“覆盖”。
Meta、Super 和 Hyper
真实修饰键
一些应用程序(尤其是 Emacs)允许有意义地使用更高的状态位。通常假设键盘上除了标准的 Shift、Ctrl 和 Alt 之外,还有 Meta、Super 和 Hyper 这样的修饰键,它们控制着这些位。
从 XKB 的角度来看,这意味着设置 Mod2、Mod3、Mod4 和 Mod5 修饰键位。因为您只需要这些位本身,所以不需要像 Level3 示例那样编辑类型。
xkb_compatibility {
interpret Super_L { action = SetMods(modifiers=Mod3); };
}
xkb_symbols {
key <LWIN> { [ Super_L ] };
modifier_map Mod3 { Super_L };
}
标准定义在 `xkb_compatibility` 中使用 Super 修饰键而不是 Mod3。您可以保留它,只需确保 `modifier_map` 行已就位。
请记住,ModN 和 Super、Hyper 甚至 Alt 等命名修饰键之间没有严格的对应关系。Mod1 是唯一被广泛使用的;有些应用程序称其为 Meta,有些称其为 Alt。对于其他键,请检查特定应用程序如何处理状态位,或/和查看下面的“虚拟修饰键”。
键符号跟踪
至少有一个应用程序(openbox)已知会跟踪 Meta_[LR]、Super_[LR] 和 Hyper_[LR] 键符号的 KeyPress/KeyRelease 事件,而不是依赖于状态位。在这种情况下:
xkb_symbols {
key <LWIN> { [ Super_L ] };
}
就足够了,您可以省略 `interpret` 和 `modifier_map` 行。
说到 Openbox,请注意它实际上允许两种方法:“S-h”跟踪 Super_[LR] 事件,而“Mod3-h”检查相关状态位。
预设配置
XKB 通常通过指定 XkbTypes/XkbCompat/XkbSymbols,或 XkbModel/XkbLayout (+XkbVariant/XkbOptions),或 XkbKeymap 来配置,通常在 `/etc/X11/xorg.conf` 或 `/etc/X11/xorg.conf.d/*.conf` 中,如下所示:
Option "XkbModel" "thinkpad60" Option "XkbLayout" "us,sk,de" Option "XkbVariant" "altgr-intl,qwerty," Option "XkbOptions" "grp:menu_toggle,grp_led:caps"
这些值通过组合 `/usr/share/X11/xkb` 下的几个文件来定义完整的 XKB 映射(可以由 `xkbcomp(1)` 转储的那个)。实际上,可以使用 `setxkbmap -print` 获得等效于 `xkbcomp(1)` 的 .xkb 文件:
$ setxkbmap -model thinkpad60 -layout us,sk,de -variant altgr-intl,qwerty \
-option -option grp:menu_toggle -option grp_led:caps -print
注意输出中的 include 语句。每个部分的 are fetched from relevant subdirectories under `/usr/share/X11/xkb`,即:
xkb_types { include "complete" };
表示 `xkbcomp(1)` 将查找 `/usr/share/X11/xkb/types/complete`。加号表示连接,所以:
xkb_keycodes { include "evdev+aliases(qwerty)" };
表示:
xkb_keycodes {
include "evdev";
include "aliases(qwerty)";
};
括号用于选择文件中的命名部分。查看 `/usr/share/X11/xkb/keycodes/aliases` 并注意:
xkb_keycodes "qwerty" { ... };
这是 `aliases(qwerty)` 引用的部分。最后,冒号允许将布局的部分转移到另一个组。
与直接定义相关 .xkb 文件部分的 XkbTypes/XkbCompat/XkbSymbols/XkbGeometry 值不同,XkbModel、XkbLayout 和 XkbRules 指向 `/usr/share/X11/xkb/rules/` 下的其他非 xkb 文件,这些文件将模型和布局值与特定的符号和几何形状进行匹配。XkbKeymap 指向完整的键映射。请查看 Ivan Pascal 的页面以获取详细描述。
与 `xkbcomp(1)` 方法一样,这种配置可以在运行时完成:使用 `setxkbmap(1)` 而不带 -print 选项。
`/usr/share/X11/xkb` 中的文件是很好的示例来源,尤其是在处理具有非平凡 XKB 实现的标准键盘功能(例如,数字键盘/NumLock 处理)时。此外,这些文件也是您需要编辑以将更改推送到上游的文件。但在这样做之前,请查看 X Keyboard Config Rules。
xmodmap
xmodmap 与 XKB 无直接关系;它使用不同的(XKB 之前的)代码键处理方式。特别是,它缺少组和类型的概念,因此尝试为每个按键设置多个键符号不太可能成功。总的来说,除了最简单的键映射或指针按钮映射修改之外,应使用 `xkbcomp(1)` 代替。
指示器
如同“键盘 LED”一样。指示器名称用于在 `xkb_keycodes` 部分中与物理 LED 匹配。否则,它们无关紧要。未匹配任何 LED 的指示器称为“虚拟”;`xkbvleds(1)`(软件包 xorg-xkbutils)可用于检查其状态。示例:
xkb_keycodes {
indicator 1 = "LED1"; // first physical LED
}
指示器始终反映 XKB 内部状态的指定部分。两个常见模式是显示修饰键状态:
xkb_compatibility {
indicator "LED1" { modifiers = Lock; }; // CapsLock indicator
}
或当前组:
xkb_compatibility {
indicator "LED1" { groups = 0x06; }; // "group 2 or group 3 is active"
}
值是位掩码。对于组,位 1 是组 1,位 2 是组 2,依此类推。
修饰键和类型
在某些时候,清理 `types` 部分和/或引入不寻常的类型可能变得必要。
类型和修饰键紧密相关,因此在进行任何类型描述的更改之前,先从修饰键位开始进行是有意义的。
决定您将使用哪些位。只有八个位,其中 Shift、Control 和 Mod1 在应用程序中被广泛使用,而 Lock(又名 CapsLock)具有预定义的含义,这也可能很难覆盖。然而,其余四个则可以随意使用。
警告:四个标准类型 ONE_LEVEL、TWO_LEVEL、ALPHABETIC 和 KEYPAD 在 `xkbcomp(1)` 中受到特殊处理。它们可能工作方式不同,仅仅因为它们是这样命名的。避免删除它们。如果某些更改未按预期工作,请尝试添加新类型。
在标准类型中使用真实修饰键
根据您的基础配置,可能存在许多未使用的标准类型,如 EIGHT_LEVEL 或 PC_RCONTROL_LEVEL2。删除它们以避免不必要的工作。
现在,一些标准类型使用虚拟修饰键。如果您决定使用它们,请查看下面的“虚拟修饰键”并跳过此部分。否则,最好完全摆脱它们。检查您需要的类型,并用相应的真实类型替换它们,或删除相关定义。示例:
type "KEYPAD" {
modifiers= Shift+NumLock;
map[Shift]= Level2;
map[NumLock]= Level2;
level_name[Level1]= "Base";
level_name[Level2]= "Number";
};
如果您将 Mod2 用于 NumLock,则将类型更改为:
type "KEYPAD" {
modifiers= Shift+Mod2;
map[Shift]= Level2;
map[Mod2]= Level2;
level_name[Level1]= "Base";
level_name[Level2]= "Number";
};
如果您不打算使用 NumLock 修饰键,则将其更改为:
type "KEYPAD" {
modifiers= Shift;
map[Shift]= Level2;
level_name[Level1]= "Base";
level_name[Level2]= "Number";
};
在 `xkb_compatibility` 部分也做同样的事情。完成后,您应该可以删除文件中的所有“virtual_modifiers”行。
切换单个修饰键位
基本上,您只需要一个具有相关解释条目的键符号。例如,使用 `LWIN` 键切换 Mod5,使用 `ISO_Level3_Shift` 作为键符号:
xkb_compatibility {
interpret ISO_Level3_Shift { action = SetMods(modifiers=Mod5); };
}
xkb_symbols {
key <LWIN> { [ISO_Level3_Shift ] };
}
除了 `SetMods`,您还可以使用 `LockMods` 或 `LatchMods`。`SetMods` 创建一个常规的“按下时有效”的修饰键。`LockMods` 创建一个“开关”键,如 CapsLock 或 NumLock。`LatchMods` 意味着“直到下一次按键有效”的粘滞修饰键。
modifier_map
修饰符映射表是一个将八个修饰符位中的每一个映射到最多4个键的表。
modifier_map Mod1 { Alt_L, Alt_R };
在核心协议中,不使用XKB时,它的意思与
interpret Alt_L { action = SetMods(modifiers=Mod1); };
interpret Alt_R { action = SetMods(modifiers=Mod1); };
XKB不以其原始含义使用修饰符映射表。在XKB内部,它的唯一功能是映射虚拟修饰符(见下文)。
然而,该表可以被客户端轻松访问,并且有一个反直觉的(但广为人知的)与之相关的技巧:修饰符映射表用于指示ModX位中的哪个是Alt。因此,最好将一个修饰符映射到Alt_L或Alt_R,如上所示。除非您有非常充分的理由这样做,否则它应该是Mod1。
多个键盘
XKB仅允许为单个连接的物理键盘设置键映射。此功能对于多键盘设置非常有用,当键盘不同时;考虑连接了一个全尺寸USB键盘的笔记本电脑。
首先,使用xinput(包xorg-xinput)获取设备ID。
AT Translated Set 2 keyboard id=11 [slave keyboard (3)]
现在,
$ xkbcomp -i 11 file.xkb $DISPLAY
或者
$ setxkbmap -device 11 ...
将仅为指定的键盘设置键映射。转储XKB配置也同样有效。
$ xkbcomp -i 11 $DISPLAY file.xkb
注意:xkbcomp -i11将不起作用,也不会给出清晰的错误消息。请确保在-i后面有一个空格。
调试XKB
当按键不按预期工作时,首先要检查的是XKB内部状态:修饰符、有效组和控制位。所有这三者都可以用来驱动LED;使用xkbvleds(1)来检查它们。
indicator "LED1" { modifiers = Lock; };
indicator "LED2" { groups = 2; };
indicator "LED3" { controls = audiblebell; };
此外,xkbwatch(1)显示所有(实际)修饰符及其锁定/锁存状态。修饰符也由xev(1)报告。xxkb(1)可用于监视有效组,但请确保two_state模式已关闭。
如果解释部分不起作用,请确保检查是否有重复的“interpret”块。更好的是,尝试注释掉任何与特定keysym相关的内容。有关解释,请参阅第9.2节。
通过以下方式下载键映射以检查服务器收到的内容也很有意义:
$ xkbcomp $DISPLAY out.xkb
结果往往与输入文件不同。对此没有已知的解决方法。
虚拟修饰符
XKB中最令人头疼的部分之一是虚拟修饰符,尽管它们是相对次要且大多无用的功能,但它们在所有标准键映射中都占有突出地位。术语本身具有误导性,而且大多数文档也无济于事。
所以,首先:虚拟修饰符不像实际修饰符那样是修饰符。如果有什么的话,这是一种命名一些实际修饰符的方式。它们不是16个额外的位,可以在级别定义中使用。它们是16个可能的名称,每个名称指向8个修饰符位中的一个(或一些,或无)。
实际的修饰符位被称为Shift、Lock、Control和Mod1-Mod5。其中没有Alt。引入虚拟修饰符是为了允许向愿意使用此信息的应用程序说类似
#define Alt Mod1
的话。
可以在不定义任何虚拟修饰符的情况下制作一个可用的布局。在标准修饰符中,只有Alt/Meta真正需要这种处理,因为Shift和Control本身就是实际修饰符,而NumLock通常不作为修饰符使用。
此外,与影响使用基本Xlib函数的所有应用程序的键映射相关的大多数事物不同,虚拟修饰符必须使用XKBlib调用显式查询。并非所有应用程序都这样做。
定义虚拟修饰符
虚拟修饰符和实际修饰符之间的映射是以一种相当奇怪的方式定义的,使用keysym作为媒介。有关原因,请参阅XKBproto。将实际修饰符M分配给一个键使用
modifier_map M { <keysym> };
使用以下方式将虚拟修饰符V分配给一个键:
interpret <keysym> { virtualMod = V; };
如果虚拟修饰符V与实际修饰符M共享至少一个keysym,它就被绑定到M。
请注意,虚拟修饰符名称不是预定义的,并且在使用前必须在xkb_compatibility和xkb_types部分声明。
xkb_compatibility "complete" {
virtual_modifiers LevelThree,NumLock,Alt;
}
Keysym解释
虚拟修饰符可以在interpret <keysym>块中使用,就像它们被定义为相应的实际修饰符一样。对于未绑定到任何实际修饰符的虚拟修饰符V,这意味着
#define V
类型声明,以及
interpret <key> { }
interpret <key>+V { }
块将被视为重复。只有一个,即文件中的最后一个,会起作用。xkbcomp(1)在这种情况下通常会给出警告。
客户端注意事项
在客户端处理XKB虚拟修饰符需要一些非平凡的服务器交互。大多数应用程序只是不 bother,坚持使用XKeyEvent.state中提供的8个实际修饰符。
然而,应用程序有可能获取与按键关联的虚拟修饰符。例如,Gtk有[3],这可能被特定应用程序使用,也可能不被使用。
有些其他应用程序可能实现了看起来像虚拟修饰符支持的东西,但实际上并非如此,请参阅#Keysym tracking中的Openbox示例。关于Alt处理,请参阅#modifier_map。
XKB控制位
一组影响XKB功能各个方面的位标志。要控制它们,请使用{Set,Latch,Lock}Controls操作。
鼠标控制
XKB允许通过键盘控制鼠标指针。正确设置后,它会非常有用。然而,它的可用性很大程度上取决于特定的物理键盘布局和用户的个人偏好。
从XKB的角度来看,实现它相对简单,只需触发相关的操作即可。在/usr/share/X11/xkb/compat/mousekeys中可以找到一个相当完整的实现。
请注意,除非设置了MouseKeys控制位,否则操作将不起作用。
interpret Pointer_EnableKeys { action= LockControls(controls=MouseKeys); };
由于大多数键盘没有专用的鼠标控制键,将MouseKeys与其中一个Overlay标志结合使用可能是一个好主意。
interpret Pointer_EnableKeys { action= LockControls(controls=MouseKeys+Overlay1); };
这允许将指针控制键移动到适当的覆盖块。
xkb_keycodes {
<MUP> = 218;
<MDWN> = 212;
<MLFT> = 214;
<MRHT> = 216;
}
xkb_symbols {
key <UP> { [ Up ], overlay1 = <MUP> };
key <LEFT> { [ Left ], overlay1 = <MLFT> };
key <RGHT> { [ Right ], overlay1 = <MRHT> };
key <DOWN> { [ Down ], overlay1 = <MDWN> };
key <MUP> { [ Pointer_Up ] };
key <MDWN> { [ Pointer_Down ] };
key <MLFT> { [ Pointer_Left ] };
key <MRHT> { [ Pointer_Right ] };
}
这样,就可以为用于控制鼠标的键分配非鼠标操作,从而,例如,使用修饰符键来生成鼠标按钮事件。
本地XKB文件夹
您可以使用以下命令从本地文件设置X键映射:
$ xkbcomp keymap.xkb $DISPLAY
其中keymap.xkb的结构必须是:
keymap.xkb
xkb_keymap {
xkb_keycodes { ... };
xkb_types { ... };
xkb_compat { ... };
xkb_symbols { ... };
// Geometry is completely optional.
// xkb_geometry { include "pc(pc104)" };
};
您可以从此文件中使用*includes*,其中包含的引用将指向本地文件夹而不是/usr/share/X11/xkb。您需要为此使用xkbcomp(1)的-I/path/参数。完整示例:
$ xkbcomp -I$HOME/.xkb $HOME/.keymap.xkb $DISPLAY
$HOME/.keymap.xkb
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+custom+inet(evdev)" };
};
符号文件必须与上面xkb_symbols中指定的名称相同。
$HOME/.xkb/symbols/custom
partial alphanumeric_keys xkb_symbols "custom" { ... };
配置工具
大多数桌面环境都允许通过其设置管理器更改XKB选项。其他相关工具包括:
- input-remapper-gitAUR - 一个用于键重映射的GUI工具。
- klfcAUR - Keyboard Layout Files Creator 是一个CLI工具,用于从JSON规范生成各种格式的布局。
故障排除
通过xorg.conf或.xinitrc设置布局不起作用
如果您在Xorg启动后,通过setxkbmap(1)成功设置了XKB布局,但在启动时使用Xorg配置文件或~/.xinitrc(使用预编译的XKB映射的xkbcomp(1)或setxkbmap(1))时却不行,那么您可能有一个启用了输入法,它正在覆盖XKB。这种行为存在,并且可以为Fcitx和Fcitx5禁用。
我有一个USB键盘,拔掉它后设置就丢失了
使用规则而不是静态键映射配置将为您提供更灵活和永久的键映射,无需手动(或通过脚本)重新加载。
参见
- XKB配置指南
- https://www.x.org/wiki/XKB
- 一个不可靠的XKB配置指南.
- Ivan Pascal XKB文档。最古老、最著名的指南之一。非常关注细节,并解释了一些XKB的奇特功能。
- xkbcommon - XKB简介。包含XKB简介,如何设置自定义键映射的指南,以及对XKB文本格式和规则文本格式的广泛(尽管仍不完整)参考文档。来自xkbcommon XKB库的开发人员。
- XKB协议规范。XKB所有功能的全面描述。对于理解XKB的工作原理非常有用,包括对虚拟修饰符等功能的良好描述。然而,强烈建议进行一些xkbcomp(1)的实践,因为这些功能是在协议级别上描述的。