X 键盘扩展
X 键盘扩展,或 XKB,定义了键盘代码在 X 中的处理方式,并提供了对内部转换表的访问。它是允许在 X 中使用多种键盘布局的基本机制。
本文介绍如何修改和创建键盘布局。如果您正在寻找如何配置键盘,请参阅 Xorg/键盘配置。
预防措施和准备
为了应对 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_
前缀声明。但是,数字是客户端实际接收的内容。仅当应用程序期望某些特定值时,键符才重要;通常是箭头、Enter、退格键、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[键码][0..4][0..1]
而不是 S[键码][0..4][0..256]
。
键符和状态
在 X 术语中,a
和 Ctrl+a
表示相同的键符和不同的状态,但 a
和 A
是不同的键符。
通常,XKB 的任务是提供不同的键符,但状态由后续的各个应用程序处理。
此外,XKB 中的状态具有一定的延迟效应,也就是说,您必须在按下按键之前设置状态。
示例:Ctrl+h
可以配置为在 rxvt(应用程序设置)中充当退格键。这样,rxvt 将接收带有在状态值中设置的 Control
位的 h
键符,并且它将与退格键符明显不同。或者,XKB 可以用于使 Ctrl+h
组合生成带有 Control
位设置的退格键符;在这种情况下,只要按下 Ctrl
键,rxvt 就不会看到物理 Backspace
键和 h
键之间的任何区别。使 Ctrl+h
组合生成没有 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 键不受修饰符的影响;通常是 Enter、空格、Escape、F 键、Shift/Alt/Ctrl 键等等。TWO_LEVEL 和 ALPHABETIC 键根据 Shift 状态生成不同的键符。所有字母数字键都属于这些类型。ALPHABETIC 额外尊重 CapsLock。
类型描述本身非常简单。行
modifiers= Shift+NumLock+LevelThree;
表示此类型的键仅受 Shift、NumLock 和 LevelThree 位的影响。映射行,例如
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
定义每个按键功能的main section。语法
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 ] }; }
CapsLock 上的 Escape,主要适用于 Vim 用户
key.type = "ONE_LEVEL"; key <CAPS> { [ Escape ] };
交换 Ins 和 PrintScreen(以防它们被颠倒——在戴尔笔记本电脑键盘上会发生这种情况)
key.type = "ONE_LEVEL"; key <IN?> { [ Print ] }; key <PRSC> { [ Insert ] };
在某些惠普笔记本电脑键盘上,上述方法不起作用。相反,必须重新定义键码本身
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 ] };
要锁定组(又名切换;仅在您按住按键时设置),请使用通常绑定到 ISO_Group_Latch 键符的 LatchGroup 动作
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 的方向键
如果目标按键要在键盘快捷键中使用,则创建清除按键修饰符的键映射是必要的。例如,如果发送了额外的 Caps 修饰符,则从主键盘(Shift+Left
)突出显示文本,或在大多数消息应用程序中更改聊天(Alt+Down
)将不起作用。但是,如果用户通过简单地将键符放在符号部分中来重新绑定字母键,则必须发送额外的修饰符。如下重新绑定允许像 AHK 的盲命令这样的功能。
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"; }; };
现在通过修改 compatability 中已有的从 LockMods 到 SetMods 的定义,将 caps 从锁定(切换)更改为设置(按下)
(请注意,这意味着您不能像正常情况下那样使用 capslock)
xkb_compatibility "complete" { ... interpret Caps_Lock+AnyOfOrNone(all) { action= SetMods(modifiers=Lock); }; ... };
最后,按如下方式修改您的 symbols 文件。
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) ] };
附加符号
使用相同的按键输入更多内容。
Compose 键
易于设置,并且对于输入常用 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。但这没关系,因为大多数应用程序都会忽略它们不使用的状态位。对于替代解决方案,请查看下面的 Overlays。
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 语句。每个节的文件都从 /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 之前的)关于键码如何在 X 中处理的想法。特别是,它缺乏组和类型的概念,因此尝试为每个键设置多个键符不太可能起作用。一般来说,除了最简单的键位图或指针按钮映射修改之外,应使用 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 模式已关闭。
如果 interpretations 部分工作不正常,请确保检查是否有重复的“interpret”块。更好的是,尝试注释掉与特定键符相关的任何内容。有关说明,请参阅第 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 虚拟修饰键需要一些复杂的服务器交互。大多数应用程序只是不理会,坚持使用 XKeyEvent.state 中提供的 8 个真实修饰键。
但是,应用程序有可能获取与按键关联的虚拟修饰键。例如,Gtk 具有 [3],它可能在特定应用程序中使用,也可能不使用。
其他一些应用程序可能会实现看起来像虚拟修饰键支持的功能,但实际上并非如此。请查看 5.3.3.2 节中的 Openbox 示例。关于 Alt 处理,请查看 8.3 节。
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 - 键盘布局文件创建器,是一个 CLI 实用程序,用于从 JSON 规范生成各种格式的布局。
故障排除
通过 xorg.conf 或 .xinitrc 设置布局不起作用
如果您在启动 Xorg 后,可以通过 setxkbmap(1) 成功设置 XKB 布局,但在启动时使用 Xorg 配置文件或 ~/.xinitrc
(无论是使用 xkbcomp(1) 使用预编译的 XKB 映射还是 setxkbmap(1))时却不行,那么您可能启用了输入法,该输入法正在覆盖 XKB。这种行为是存在的,并且可以为 Fcitx 和 Fcitx5 禁用。
我有一个 USB 键盘,并且设置在拔下后丢失
使用 规则 而不是静态键盘映射配置将为您提供更灵活和永久的按键映射,而无需手动(或通过脚本)重新加载。
参见
- XKB 配置指南
- https://www.x.org/wiki/XKB
- XKB 配置不可靠指南.
- Ivan Pascal XKB 文档。最古老和最著名的指南之一。非常注重细节,并解释了一些奇异的 XKB 功能。
- XKB 协议规范。所有 XKB 功能的全面描述。对于理解 XKB 的工作原理非常有用,其中包括对虚拟修饰键等的良好描述。强烈建议进行一些 xkbcomp(1) 实践,因为这些功能是在协议级别上描述的。