Linux内核层面和XWindow层面的键盘映射的一点研究


本文是2008年左右在红旗帮助他们开发新的发布版的时候,发在RF maillist里面的讨论。今天在整理Gmail里面的
邮件时找到,还是比较有保留的价值吧,赶紧往博客里存一篇。

Linux内核层面和XWindow层面的键盘映射研究

Linux内核

以前Rock在mail-list里面发过一个对于键盘的文章,最近我重新安装Debian系统,需要将terminal和X里面的Caps_Lock键和Ctrl键互换,
所以又针对性的研究了一下键盘。在Rock的文章里面已经又提到,系统处理键盘其实是一个挺复杂的过程。这里面要注意几个概念:

scancode 扫描码

扫描码在我们上计算机基础课程的时候,可能会有提及,在DOS系统的键盘处理中首先出现的
就是扫描码。键盘是个硬件设备,其实键盘也是有电路逻辑的,甚至可编程。从键盘中输出的就是
扫描码,这个是由键盘决定的,不是由系统来决定。
由于缺乏各种键盘型号、布局的测试环境,所以我们基本无法测试在Dvarok键盘上的按键是怎么
做扫描码布局的。或许就是扫描码是一样,通过系统设置不同实现布局不同。
键盘上的符号只是默认的缺省扫描码到最终字符的映射的,由于可以修改映射关系,所以也就可以
实现不同的keyboard layout。这个例子可以想像你买了一个二手日文键盘的笔电。

keycode 键码

其实不知道这个是否应该翻译。keycode不是扫描码那么初级的东西了,一次按键可以产生至多6个
扫描码,内核的键盘驱动会进行scancode 的parsing,实现到keycode的转换。keycode目前来说,大部分
是等于scancode的,这一点没有约定,但是这么做容易些。如果有部分按键是特殊的,可能没有对应固定的
keycode。但是从原理上来说每个按键都应该会产生scancode,内核会生成keycode,所以对于多媒体键盘,
应该是每个按键都会产生scancode,这一点我不同意rock的说法。
内核的键盘驱动,一般设定在127个keycode,对于一般的键盘都够用了。press和release产生不同的扫描码,
release事件高位(8th bit)至1。如果存在没有keycode的特殊按键,可以使用setkeycodes来给内核中的scancode
到keycode的mapping table添加entry。

keysyms 键符号

keysym就代码最终按键的处理,例如是给client application输入一个小写字符,还是一个大写字符,
还是执行一个命令。都是可以通过给keycode绑定不同的keysym来实现,我们可以理解keysym代表的是一个
函数,直接输入一个ASCII字符,只是其中的常用功能。keycode到keysyms的映射关系,就是keymap。
内核内部有一个缺省的keymap用来进行转换,典型的就是backspace按键在内核中不是backspace,
而是delete。keymap就是可以理解是linux层的keyboard layout。在/usr/share/keymaps类似的地方,
按照不同的arch存放了多种keymap。在Debian中,使用dpkg-reconfigure console-data可以设置键盘。
我们常用的都是US标准键盘。想像一个Dvarok键盘,keycode和scancode匹配,那么你看到键盘时候就是
看到相应的scancode和keycode的时候,要将keycode转化为键盘的字符,就是找匹配的keymap。
keymap的作用就是将你手头的键盘和你预想中的按键需求做一个链接,找到合适的keymap正确的表现了你键盘的
物理keycode布局和相应的按键字符布局。

那么我们可以理解,

loadkeys << EOF keycode 58 = Control keycode 29 = Caps_Lock EOF 就是实现了将 Control这个keysym赋值给58这个keycode。
关于delete和backspace,好像是bash做了相应的改变, 以实现正常的用法。如果你使用最简单的vi,应该可以体验到backspace的特殊反应。
linux层面的keymap实现,是keyboard layout的一个实现,而且,由于这个实现涉及scancode、keycode、keysym等等,
其中的数据结构需要内核的参与,所以这个实现是需要root权限的,而且一旦改变,host上的所有用户都发生了改变。
所以, 我的设置也是放在rc.local中。

相关程序: loadkeys, dumpkeys, setkeycode, keymaps(5) showkeys kbd软件包

XWindow

那么X层面的keyboard layout ? 首先,我们要了解X没有使用terminal下面的keycode。
X有使用一个类似linux的keymap,也使用keycode这个名称,但是 两者是不同的。
X是运行在Linux上的一个应用程序,所以它的键盘输入来源也是linux内核。
linux键盘驱动有一个raw模式, 在这个模式下,linux不过滤任何信息。
X作为一个操作环境,需要所有的输入信息,包括每个press和release event,所以 它要使用raw模式。
那么X会有自己的键盘驱动,来处理键盘和pointer事件。

下面我们来研究一下,如何改X环境下的Control 和 Caps_Lock。

1. 使用菜单里面的键盘设置,system >> preferences >> keyboard 在layout页面,打开layout option,其中可以设置 swap这两个键。

这个方法有个问题,键盘的LED灯还是按照原来的按键反应按键。可以看出,这个设置,就是在X键盘驱动的上面构建了一层
来实现切换。也就是说X的keyboard layout是逻辑的layout。逻辑的keyboard layout其实是一个不错的概念,但是应该使得
修改按键的方案全面。在Fedora中,似乎是可以正确显示LED灯。

2. 使用xmodmap
X的keymap格式类似linux的,两者不同的是linux下的modifiers是8个,X下是4个。

remove Lock = Caps_Lock
remove Control = Control_L
keysym Control_L = Caps_Lock
keysym Caps_Lock = Control_L
add Lock = Caps_Lock
add Control = Control_L

使用xmodmap可以正确实现键盘布局的改变,由于是从keycode层面改变,所以LED可以正确反应。
xmodmap可以是用户级别的,所以可以在.xsesssionrc中加入xmodmap ~/.xmodmaprc来实现加载修改。

有的文章说,XFree86在2.0后,就会读入系统keymap来初始化X的keymap,但是经过测试,如果仅改变
linux层的keymap,X不会起作用,所以两者应该没有关系。而且,X的键盘设置完全脱离linux的影响,也
可以比较好的保证自己实现的独立性。

相关程序 xev xmodmap xkb( x keyboard extension)

reference :

http://www.linuxjournal.com/article/1080 The Linux keyboard driver
http://tldp.org/HOWTO/Keyboard-and-Console-HOWTO.html

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s