X 键盘扩展

出自 ArchWiki

X 键盘扩展,或 XKB,定义了键盘代码在 X 中的处理方式,并提供了对内部转换表的访问。它是允许在 X 中使用多种键盘布局的基本机制。

本文介绍了如何修改和创建键盘布局。如果您正在寻找如何配置键盘,请参阅 Xorg/键盘配置

警告: 在使用 XKB 时,您的 X 服务器可能会挂起或崩溃,或者您的键盘可能会进入无法使用的状态,只能通过终止当前会话来恢复。在进行任何更改之前,请遵循 #预防措施和准备 以确保您可以恢复。

预防措施和准备

为了应对 X 服务器崩溃或键盘进入无法使用状态的可能性

  1. 请确保您有某种无需键盘即可终止会话的方法。(除了电源按钮之外,拥有某种远程 killall X 或重启主机的方法可能是一个好主意。)
  2. 确保您已保存所有工作以防止数据丢失。
  3. 如果您使用 GNOME,您可以安装 gnome-tweaks 以使用鼠标恢复设置。例如,当键盘变得无法使用时,使用鼠标导航到键盘 & 鼠标 > 附加布局选项,然后选中并取消选中所有设置,这将把键盘重置为系统行为。
提示: 为了使调试更容易,请停止诸如 xxkb(1) 之类的应用程序,这些应用程序会切换键盘布局或以其他方式主动更改 XKB 状态。

获取和设置 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
注意: 即使您忠实地重新创建了有效的 setxkbmap(1) 命令作为 Xorg 配置文件,由于 Xorg 错误,它可能根本不起作用。[1] [2]

使用键位映射

使用 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 的内容,通常是某种物理按键索引。状态表示修饰键,0x01Shift。键码与状态值一起是 X 在 XKeyEvent(3) 结构中发送给应用程序的内容。键符和相应的字符串是客户端使用 XLookupString(3) 及其友元获得的内容。

状态字段中的位具有预定义的名称:ShiftLockControlMod1Mod2Mod3Mod4Mod5,从低到高。因此,Ctrl+Shift 是 0x05,依此类推。客户端应用程序通常只检查它们需要的位,因此具有正常键盘输入和 Ctrl+key 快捷方式的应用程序通常不会区分 ControlControl+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[keycode][0..4][0..1] 而不是 S[keycode][0..4][0..256]

键符和状态

在 X 术语中,aCtrl+a 表示相同的键符和不同的状态,但 aA 是不同的键符。

通常,XKB 的任务是提供不同的键符,但状态稍后由各个应用程序处理。

此外,XKB 中的状态具有一定的延迟效应,也就是说,您必须在按下按键之前设置状态。

示例:Ctrl+h 可以配置为在 rxvt(应用程序设置)中充当退格键。这样,rxvt 将接收到键符 h,状态值中设置了 Control 位,并且它将与 Backspace 键符明显不同。或者,可以使用 XKB 使 Ctrl+h 组合生成带有 Control 位设置的 Backspace 键符;在这种情况下,只要按下 Ctrl 键,rxvt 将看不到物理 Backspace 键和 h 键之间的任何区别。使 Ctrl+h 组合生成没有 Control 位设置的 Backspace 键符也是 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、Space、Escape、F 键、Shift/Alt/Ctrl 键等等。 TWO_LEVEL 和 ALPHABETIC 键根据 Shift 状态产生不同的 keysyms。 所有字母数字键都属于这些类型。 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 的 keysym。 每个组中的 keysyms 数量必须与为此类型定义的级别数匹配 (xkbcomp(1) 如果不匹配,将会发出警告)。

查看 /usr/include/X11/keysymdef.h 以获取可能的 keysyms 列表。 除了列出的那些之外,您还可以使用 Unnnn 表示十六进制代码为 nnnn 的 Unicode 符号,例如 U0301 表示组合锐音符。 请注意,aU0061 的处理方式不同(例如,大多数应用程序期望 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 ] };

在某些 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 keysyms 放置在正确的组/级别位置。 请注意,组是循环的,因此如果您有两个组并按两次 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 keysym 的 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 的方向键

此文章或章节需要改进语言、wiki 语法或风格。 请参阅 Help:Style 以供参考。

原因: 本节的目的尚不明确。(在 Talk:X keyboard extension 中讨论)

如果目标键要在键盘快捷键中使用,则创建清除按键修饰符的键映射是必要的。 例如,从主键盘高亮显示文本 (Shift+Left),或在大多数消息应用程序中更改聊天 (Alt+Down) 如果发送了额外的 Caps 修饰符,则将无法工作。 但是,如果用户通过简单地将 keysym 放在 symbols 节中来重新绑定字母键,则必须发送额外的修饰符。 如下所示的重新绑定允许像 AHK 的盲命令这样的功能。

types 节(定义层映射)必须包含一个条目,使得

  • 当没有按下修饰键时,使用 keysyms 的第一层(小写字母)。
  • 当仅按下 Shift 键时,使用 keysyms 的第二层(大写字母)。
  • 当仅按下 Lock 键时,使用 keysyms 的第三层(方向键)
  • 当同时按下 Shift 和 Lock 键时,也使用 keysyms 的第三层(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";
   };
 };

现在通过将兼容性中已有的定义从 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。 对于其他的,请检查特定应用程序如何处理状态位,和/或检查下面的虚拟修饰键。

Keysym 跟踪

至少有一个应用程序 (openbox) 已知会跟踪 Meta_[LR]、Super_[LR] 和 Hyper_[LR] keysyms 的 KeyPress/KeyRelease 事件,而不是依赖于状态位。 在这种情况下

   xkb_symbols {
       key <LWIN> { [ Super_L ] };
   }

就足够了,您可以省略 interpretmodifier_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) 方法一样,这种配置可以动态完成:使用不带 -print 选项的 setxkbmap(1)

来自 /usr/share/X11/xkb 的文件是示例的良好来源,尤其是在涉及具有非平凡 XKB 实现的标准键盘功能(例如,小键盘/NumLock 处理)时。 此外,这些是您必须编辑以将更改推送到上游的文件。 在执行此操作之前,请查看 X Keyboard Config Rules

xmodmap

xmodmap 与 XKB 没有直接关系;它使用不同的(XKB 之前的)关于如何在 X 中处理键码的想法。 特别是,它缺乏组和类型的概念,因此尝试为每个键设置多个 keysyms 可能无法工作。 一般来说,除了最简单的键映射或指针按钮映射修改之外,应该使用 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";
   };

如果您为 NumLock 使用 Mod2,请将类型更改为

   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" 行。

切换单个修饰符位

基本上,您只需要一个带有相关解释条目的 keysym。Mod5 切换的示例,使用 LWIN 键,keysym 为 ISO_Level3_Shift

   xkb_compatibility {
       interpret ISO_Level3_Shift { action = SetMods(modifiers=Mod5); };
   }
   
   xkb_symbols {
       key <LWIN> { [ISO_Level3_Shift ] };
   }

除了 SetMods,您还可以使用 LockModsLatchModsSetMods 使修饰键成为常规的“按下时启用”类型。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" 块。更好的是,尝试注释掉任何与特定 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 虚拟修饰符需要一些非平凡的服务器交互。大多数应用程序只是不费心,坚持使用 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); };

这允许将指针控制键移动到适当的 overlay 块

   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。 FcitxFcitx5 存在此行为,并且可以禁用。

我有一个 USB 键盘,并且设置在拔下后丢失

使用 rules 而不是静态键映射配置将为您提供更灵活和永久的键映射,无需手动(或通过脚本)重新加载。

另请参阅