Shane Xu's Home

Life is too short for so much sorrow.

Keyboard Layout

经过好长好长一段时间的犹豫,我终于下定决心,买下了心仪已久的 Surface Book

于是我也开始用起了,好多年没有正经使用的windows系统。与此同时,我也集齐了三大操作系统,linux,windows,macOS。其实我对Surface并没有抱有太大的期望,我仅仅只是把它当作一个极具生产力的平板。iPad除了玩游戏,还能做什么,至于所谓的iPad Pro,我也不是它的目标用户。虽然我不打算在Surface上写代码,但是也好歹需要装一个Emacs。于是问题也随之而来了——没有用的 Caps Lock 键。在macOS和linux系统我可以分别通过系统设置和gnome-tweak-tools或者xmodmap或者XKB,将 Caps LockCtrl 互换。

那么如何修改windows的 Caps Lock 呢。上一次大规模使用windows的时候,我用修改注册表的方法,改了 Caps Lock 的行为,然而这种方法用过一次就忘记如何设置了,而且也不好打理,万一要改回来呢。注册表可不是个好东西啊。经过一番搜索,我找到了 SharpKeys 这个应用。虽然这个应用归根结底,还是写的注册表,然而有个简易的界面,还是让人比较安心的。

sharpkeys.png

然而这种方法还是有个问题,当我插上我心爱的 HHKB 的时候,我心爱的 Ctrl 就变成了 Caps Lock 了。我并不想这个键位更改发生在我的 HHKB 上。

至于macOS,虽然我之前有一百个槽要吐,但是它总是在一些很奇怪的地方,做的格外用心。macOS的键盘设置可以分不同的键盘。比如内置的键盘,可以交换两个键位,而 HHKB 则什么都不做。

macos_keyboard.png

最后,今天的槽点来了。当我选定用 Emacs 作为我的人生伴侣的时候,我被日益难按的 Ctrl 键,折磨得死去活来。于是,我学会了改键。我最开始用的改键软件就是 xmodmap

xmodmap ~/.Xmodmap

~/.Xmodmap 的内容如下:

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

正如 windows 的尴尬,=xmodmap ~/.Xmodmap= 会把所有的键盘的布局都更改。

在查阅了Archlinux wiki之后,我又发现了一个新命令 setxkbmap

setxkbmap -device 11 -option ctrl:swapcaps
setxkbmap -device 15 -option ""

里面的11就是要修改键位的键盘的device id。通过 xinput 命令可以查看当前所有输入设备的信息。以下是这个命令在我的macbook pro上的输出。

⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ bcm5974                                   id=12   [slave  pointer  (2)]
⎜   ↳ Broadcom Corp. Bluetooth USB Host Controller  id=14   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Video Bus                                 id=7    [slave  keyboard (3)]
    ↳ Video Bus                                 id=8    [slave  keyboard (3)]
    ↳ Power Button                              id=9    [slave  keyboard (3)]
    ↳ Sleep Button                              id=10   [slave  keyboard (3)]
    ↳ Apple Inc. Apple Internal Keyboard / Trackpad id=11   [slave  keyboard (3)]
    ↳ Broadcom Corp. Bluetooth USB Host Controller  id=13   [slave  keyboard (3)]
    ↳ Topre Corporation HHKB Professional       id=15   [slave  keyboard (3)]

然而,总不能每次插上键盘的时候,还要执行一下命令吧,这很不linux。当然我可以写个定时任务,检查是否插入了HHKB,然后设置键盘布局。

xorg.conf中有一个Section —— InputClass,专门用来设置输入设备。于是我在 /etc/X11/xorg.conf.d/00-keyboard.conf ,中加入了这些配置。然而,在 gnome 3.24 下死活不生效。

Section "InputClass"
  Identifier "system-keyboard"
    MatchIsKeyboard "on"
  Option "XkbLayout" "us"
    Option "XkbOptions" "ctrl:swapcaps"
EndSection
Section "InputClass"
  Identifier "hhkb"
    MatchProduct "HHKB"
  Option "XkbLayout" "us"
    Option "XkbOptions" ","
EndSection

推测是 gnome ,在启动 gnome-session 的时候,对键盘布局做了些手脚。网友们说是 gnome-settings-daemon 的手脚。

$ gsettings set org.gnome.settings-daemon.plugins.keyboard active false
No such schema “org.gnome.settings-daemon.plugins.keyboard”

然而,gnome 3.24之后已经不能用这个命令了。

$ gsettings list-keys org.gnome.settings-daemon.plugins
whitelisted-plugins
$ gsettings describe org.gnome.settings-daemon.plugins whitelisted-plugins
A list of strings representing the plugins that are allowed to be loaded (default: “all”). This is only evaluated on startup.
$ gsettings get org.gnome.settings-daemon.plugins whitelisted-plugins
['all']

所以通过 gsettings get org.gnome.settings-daemon.plugins whitelisted-plugins 这个值,可以屏蔽 keyboard 插件。

gsettings set org.gnome.settings-daemon.plugins whitelisted-plugins "['a11y-keyboard', 'a11y-settings', 'clipboard', 'color', 'common', 'datetime', 'dummy', 'housekeeping', 'media-keys', 'mouse', 'orientation', 'power', 'print-notifications', 'rfkill', 'screensaver-proxy', 'sharing', 'smartcard', 'sound', 'wacom', 'xrandr', 'xsettings']"

然而并没有什么卵用。

既然是开源,就去查 gnome-settings-daemon 的源代码。然而在读了源代码之后,我发现,这个 whitelisted-plugins ,根本就没有用到过。这个配置项,在 gnome-3.22 才是有用的。这不是坑爹吗。

那我自己把 gsd-keyboard kill掉吧。

$ ps -ef | grep '[g]sd-keyboard'                                    
gdm        717   598  0 00:22 tty1     00:00:00 /usr/lib/gnome-settings-daemon/gsd-keyboard
shane    14166  8246  0 07:22 tty2     00:00:00 /usr/lib/gnome-settings-daemon/gsd-keyboard
$ ps -ef | grep '[g]sd-keyboard' | grep `whoami` | awk '{print $2}' | xargs kill
$ ps -ef | grep '[g]sd-keyboard'
gdm        717   598  0 00:22 tty1     00:00:00 /usr/lib/gnome-settings-daemon/gsd-keyboard
shane    14268  8246  0 07:25 tty2     00:00:00 /usr/lib/gnome-settings-daemon/gsd-keyboard

有守护啊。那我就这样。

ps -ef | grep '[g]sd-keyboard' | grep `whoami` | awk '{print $2}' | xargs kill -9

没想到 gnome-shell 退出了。 gsd-keyboard 应该是 gnome-settings-daemon 包中的一部分,让我来查查这个包里有什么。

$ pacman -Ql gnome-settings-daemon | grep '[kK]eyboard'
gnome-settings-daemon /etc/xdg/autostart/org.gnome.SettingsDaemon.A11yKeyboard.desktop
gnome-settings-daemon /etc/xdg/autostart/org.gnome.SettingsDaemon.Keyboard.desktop
gnome-settings-daemon /usr/lib/gnome-settings-daemon/gsd-a11y-keyboard
gnome-settings-daemon /usr/lib/gnome-settings-daemon/gsd-keyboard

自动启动, 那就把 org.gnome.SettingsDaemon.Keyboard.desktop 移动到别的地方去。

sudo mv /etc/xdg/autostart/org.gnome.SettingsDaemon.A11yKeyboard.desktop ~/Desktop

然后还是启动不了。原来 gnome-session 依赖于 gsd-keyboard ,修改 gnome-session 把这个依赖去掉。

$ sudo vim /usr/share/gnome-session/sessions/gnome.session
[sudo] password for shane:

Name[zh_CN]=GNOME
Name[zh_HK]=GNOME
Name[zh_TW]=GNOME
RequiredComponents=org.gnome.Shell;org.gnome.SettingsDaemon.A11yKeyboard;org.gnome.SettingsDaemon.A11ySettings;org.gnome.SettingsDaemon.Clipboard;org.gnome.SettingsDaemon.Color;org.gnome.SettingsDaemon.Datetime;org.gnome.SettingsDaemon.Housekeeping;org.gnome.SettingsDaemon.MediaKeys;org.gnome.SettingsDaemon.Mouse;org.gnome.SettingsDaemon.Orientation;org.gnome.SettingsDaemon.Power;org.gnome.SettingsDaemon.PrintNotifications;org.gnome.SettingsDaemon.Rfkill;org.gnome.SettingsDaemon.ScreensaverProxy;org.gnome.SettingsDaemon.Sharing;org.gnome.SettingsDaemon.Smartcard;org.gnome.SettingsDaemon.Sound;org.gnome.SettingsDaemon.Wacom;org.gnome.SettingsDaemon.XRANDR;org.gnome.SettingsDaemon.XSettings;

然后 gnome-session 正常启动了,HHKB插上去,键位还是变了。我有点懵逼了。我决定尝试使用其他桌面环境—— awesome 和 KDE。实验的结果是:awesome完全遵照了,xorg里面的设置,没有修改HHKB的键位;而KDE的行为和gnome的一样。所以肯定是gnome做了些手脚。我把矛头指向了 gnome-shell

修改了 gnome-shell 中关于键盘的代码:

diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
index d4b14d538..5796afd5e 100644
--- a/js/ui/status/keyboard.js
+++ b/js/ui/status/keyboard.js
@@ -266,7 +266,7 @@ const InputSourceSessionSettings = new Lang.Class({
     _init: function() {
         this._settings = new Gio.Settings({ schema_id: this._DESKTOP_INPUT_SOURCES_SCHEMA });
         this._settings.connect('changed::' + this._KEY_INPUT_SOURCES, Lang.bind(this, this._emitInputSourcesChanged));
-        this._settings.connect('changed::' + this._KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._emitKeyboardOptionsChanged));
+        // this._settings.connect('changed::' + this._KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._emitKeyboardOptionsChanged));
         this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged));
     },

keyboard.js 注释掉269行,从字面上看,这行代码应该注册了一个事件。当 _KEY_KEYBOARD_OPTIONS 发生变化时,执行 this._emitKeyboardOptionsChanged ,这样可以防止 org.gnome.desktop.input-sources xkb-options 变化时,设置键盘布局。

diff --git a/js/misc/keyboardManager.js b/js/misc/keyboardManager.js
index 95afb4a8c..129e5bbe9 100644
--- a/js/misc/keyboardManager.js
+++ b/js/misc/keyboardManager.js
@@ -65,27 +65,27 @@ const KeyboardManager = new Lang.Class({
     },
 
     apply: function(id) {
-        let info = this._layoutInfos[id];
-        if (!info)
-            return;
-
-        if (this._current && this._current.group == info.group) {
-            if (this._current.groupIndex != info.groupIndex)
-                this._applyLayoutGroupIndex(info.groupIndex);
-        } else {
-            this._applyLayoutGroup(info.group);
-            this._applyLayoutGroupIndex(info.groupIndex);
-        }
-
-        this._current = info;
+        // let info = this._layoutInfos[id];
+        // if (!info)
+        //     return;
+
+        // if (this._current && this._current.group == info.group) {
+        //     if (this._current.groupIndex != info.groupIndex)
+        //         this._applyLayoutGroupIndex(info.groupIndex);
+        // } else {
+        //     this._applyLayoutGroup(info.group);
+        //     this._applyLayoutGroupIndex(info.groupIndex);
+        // }
+
+        // this._current = info;
     },
 
     reapply: function() {
-        if (!this._current)
-            return;
+        // if (!this._current)
+        //     return;
 
-        this._applyLayoutGroup(this._current.group);
-        this._applyLayoutGroupIndex(this._current.groupIndex);
+        // this._applyLayoutGroup(this._current.group);
+        // this._applyLayoutGroupIndex(this._current.groupIndex);
     },
 
     setUserLayouts: function(ids) {

keyboardManager.js 文件中注释掉 applyreapply 方法,让设置键盘布局的方法失效。

重新打包安装 gnome-shell ,插上HHKB,终于成功了,键盘没有被改。macbook内置的键盘也保持了 Caps LockCtrl 交换。但还是有一点瑕疵,如果键盘是跟着 gnome-shell 启动话,布局还是会被修改,但是只要拔下再插上就好了。

Comments

comments powered by Disqus