X 键盘扩展

出自 ArchWiki
(重定向自 Xkbmap

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[键码][0..4][0..1] 而不是 S[键码][0..4][0..256]

键符和状态

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

通常,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 表示组合锐音符。请注意,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 ] };

在某些惠普笔记本电脑键盘上,上述方法不起作用。相反,必须重新定义键码本身

   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 的方向键

本文或章节需要语言、wiki 语法或样式改进。请参阅 Help:Style 以获取参考。

理由: 本节的目的不明确。(在 Talk:X keyboard extension 中讨论)

如果目标按键要在键盘快捷键中使用,则创建清除按键修饰符的键映射是必要的。例如,如果发送了额外的 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 ] };
   }

就足够了,您可以省略 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) 方法一样,这种配置可以动态完成:使用 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 之外,您还可以使用 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”块。更好的是,尝试注释掉与特定键符相关的任何内容。有关说明,请参阅第 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); };

因为大多数键盘没有专用的鼠标控制键,所以将 MouseKeysOverlay 标志之一结合使用可能是一个好主意

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

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

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

参见