author: 韩勇 date: 2019-02-03

春节在家用笔记本,才发现笔记本键盘布局不合理(平时都是用外接键盘), 正常 Shift 键的位置放了 Up 键,导致按 Shift 键时经常错按到 Up 上,于是想到要改键。 Linux X11 下按键配置叫做键盘布局(Keyboard Layout),找到一篇文档:Custom keyboard layout definitions , 大概介绍了一些相关知识和操作,应该可以帮助我们完成操作。

Linux X11 键盘布局

Linux X11 下键盘布局相关的软件包简称为 xkb 。

  • 每个按键会产生一个按键码(Key Code),即一个数值,对应一个简短名字定义。 Ubuntu 下 /usr/share/X11/xkb/keycodes/evdev 文件可查看按键码名字与数值的对应关系。 如按键 Q 的按键码为 <AD01> = 24,这个名字通常描述了这个键在键盘上的位置, AD 表示从下往上第 4 行按键,01 表示从左往右第 2 个按键(索引从 0 开始)。 按键 ECS 的按键码为 <ESC> = 9,这是一个特殊命名。

  • 按键之后应用程序接收到的信息叫做按键符号(Key Symbol),同样有一个名字。 头文件 /usr/include/X11/keysymdef.h 可查看按键符号名字(添加了 XK_ 前缀)与其 Unicode 值的对应关系。 该头文件中 Unicode 值使用了固定的 4 位 16 进制值,因此很方便检索, 如字符 0 Unicode 值为 0x0030,字符 A Unicode 值为 0x0041,可看到相关定义如下:

#define XK_0                             0x0030  /* U+0030 DIGIT ZERO */
#define XK_1                             0x0031  /* U+0031 DIGIT ONE */
#define XK_2                             0x0032  /* U+0032 DIGIT TWO */
#define XK_3                             0x0033  /* U+0033 DIGIT THREE */
... ...
#define XK_at                            0x0040  /* U+0040 COMMERCIAL AT */
#define XK_A                             0x0041  /* U+0041 LATIN CAPITAL LETTER A */
#define XK_B                             0x0042  /* U+0042 LATIN CAPITAL LETTER B */
#define XK_C                             0x0043  /* U+0043 LATIN CAPITAL LETTER C */
  • 键盘布局即按键码与按键符号之间的映射关系。 Ubuntu 下 /usr/share/X11/xkb/symbols/ 目录下即包含了各种不同的键盘布局配置。 最常用(通常默认使用)的布局即 English (US),其在 us 文件中配置,可看到相关配置内容如下:
default  partial alphanumeric_keys modifier_keys
xkb_symbols "basic" {

    name[Group1]= "English (US)";

    key <TLDE> {	[     grave,	asciitilde	]	};
    key <AE01> {	[	  1,	exclam 		]	};
    key <AE02> {	[	  2,	at		]	};
    ... ...

    key <AD01> {	[	  q,	Q 		]	};
    key <AD02> {	[	  w,	W		]	};
    ... ...

    key <BKSL> {	[ backslash,         bar	]	};
};
一个按键可对应一个或多个符号,对应使用不同修饰键时产生的输入(一键多用),依次为:

unmodified, shift, altgr, shift+altgr,其中 altgr 表示右 Alt 键。

按键映射测试

回到我的需求,即希望将 Up 键修改成 Shift 键。 参考文档大概说了如何添加新的键盘布局,但没太看懂,已有布局配置包含大量文件,也没太看懂, 但已经找到我当前使用的 English (US) 布局配置,因此尝试直接修改此配置添加 UpShift 的映射。

  • keycodes/evdev 文件找到按键码定义 <UP> = 111;
  • X11/keysymdef.h 文件找到 Shift 相关定义如下(可看到 Shift 区分左右):

    #define XK_Shift_L                       0xffe1  /* Left shift */
    #define XK_Shift_R                       0xffe2  /* Right shift */
  • 键盘布局添加配置 key <UP> { [ Shift_R ] };

  • 执行命令 sudo dpkg-reconfigure xkb-data 并重启电脑(重启 X 窗口系统)。

测试发现修改成功。

使用 showkey 查看按键码

如何知道一个按键的按键码呢? 可使用 showkey -k 命令查看,但注意只有在原始控制台(TTY)下才能成功获取到原始按键码(可使用 Ctrl+Alt+F1 切换到 tty1 控制台操作)。 在 X11 或伪终端(PTY)下报错 "无法获取指向控制台的文件描述符",只能使用 showkey -a 查看映射后的输入值。 另外本机测试发现 showkey -k 查看到的按键码比 keycodes/evdev 中定义的数值小 8 (原因未知?), 应以 keycodes/evdev 中的值为准,即 showkey -k 看到的值加 8 再去 keycodes/evdev 查按键码名。

最终配置

原本打算 Up 和 Shift 按键互换,但这样 Up 与 Down 上下不对齐,使用有点别扭,将 Down 也右移,发现更别扭,方向键经常按错。 考虑到 Fn 组合产生的 PageUpPageDown 不常用,打算将其映射为 UpDown,废弃掉这两个键。

查询相关键码后,最终添加按键映射配置如下:

  key   <UP> {    [ Shift_R   ]   };
  key <RTSH> {    [ Up        ]   };
  key <PGUP> {    [ Up        ]   };
  key <PGDN> {    [ Down      ]   };

另外 X11 下还有个命令 xmodmap 也可以用来修改按键映射,暂未详细了解(暂时不需要了?)。

综上可知,通过按键映射,我们可以实现任意语言字符的输入布局(是否可实现直接汉语拼音输入?)。