Edited at

xkeysnailでキーリマップする

xkeysnailでキーコードの交換やワンショットモディファイヤができるとわかったので、試してみました。

Antergos、Xorg、GDMの環境にインストールします。

$ lsb_release -a

LSB Version: 1.4
Distributor ID: Arch
Description: Arch Linux
Release: rolling
Codename: ISO-Rolling
$ uname -r
4.14.15-1-ARCH
$ python --version
Python 3.6.4


導入

venvで/opt/xkeysnail配下にインストールします。

$ sudo python -m venv /opt/xkeysnail

[sudo] miy4 のパスワード:
$ . /opt/xkeysnail/bin/activate
(xkeysnail) $ sudo pip install xkeysnail
Collecting xkeysnail
Downloading xkeysnail-0.1.0.tar.gz
Collecting evdev (from xkeysnail)
Downloading evdev-0.7.0.tar.gz
Collecting python-xlib (from xkeysnail)
Downloading python_xlib-0.21-py2.py3-none-any.whl (123kB)
100% |████████████████████████████████| 133kB 345kB/s
Collecting six>=1.10.0 (from python-xlib->xkeysnail)
Downloading six-1.11.0-py2.py3-none-any.whl
Installing collected packages: evdev, six, python-xlib, xkeysnail
Running setup.py install for evdev ... done
Running setup.py install for xkeysnail ... done
Successfully installed evdev-0.7.0 python-xlib-0.21 six-1.11.0 xkeysnail-0.1.0
(xkeysnail) $ deactivate

2019/1/27追記:PyPIとGithubにあるxkeysnailのバージョンが違うので、ご注意ください。リリース版ではないのかもしれませんが、Githubの方が開発が進んでおり、機能追加、バグ修正がされています。


キーリマップの設定

以下の方針で設定します。


  • CapsLockキー


    • 単体で押すとESC、他のキーと一緒に押すと(つまり修飾キーとして使うと)Ctrlとして使う

    • 単純にCapsLockCtrlを交換するなら、define_modmapが使える



  • 変換キー


    • 単体で押すと変換、修飾キーとして使うとCtrl



  • 無変換キー


    • 単体で押すと無変換、修飾キーとして使うとAlt



右手、左手、それぞれでCtrlAltを押しやすくなるので、Emacsを使う環境でよく設定しています。


/etc/opt/xkeysnail/config.py

# -*- coding: utf-8 -*-


import re
from xkeysnail.transform import *

#define_modmap({
# Key.CAPSLOCK: Key.LEFT_CTRL
#})

define_multipurpose_modmap({
Key.CAPSLOCK: [Key.ESC, Key.LEFT_CTRL],
Key.MUHENKAN: [Key.MUHENKAN, Key.LEFT_ALT],
Key.HENKAN: [Key.HENKAN, Key.RIGHT_CTRL]
})


これだけ!かんたん!


起動:失敗

$ sudo /opt/xkeysnail/bin/xkeysnail /etc/opt/xkeysnail/config.py                    


██╗ ██╗██╗ ██╗███████╗██╗ ██╗
╚██╗██╔╝██║ ██╔╝██╔════╝╚██╗ ██╔╝
╚███╔╝ █████╔╝ █████╗ ╚████╔╝
██╔██╗ ██╔═██╗ ██╔══╝ ╚██╔╝
██╔╝ ██╗██║ ██╗███████╗ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
███████╗███╗ ██╗ █████╗ ██╗██╗
██╔════╝████╗ ██║██╔══██╗██║██║
███████╗██╔██╗ ██║███████║██║██║
╚════██║██║╚██╗██║██╔══██║██║██║
███████║██║ ╚████║██║ ██║██║███████╗
╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝╚══════╝
v0.1.0

Traceback (most recent call last):
File "/opt/xkeysnail/bin/xkeysnail", line 6, in <module>
cli_main()
File "/opt/xkeysnail/lib/python3.6/site-packages/xkeysnail/__init__.py", line 44, in cli_main
eval_file(args.config)
File "/opt/xkeysnail/lib/python3.6/site-packages/xkeysnail/__init__.py", line 5, in eval_file
exec(compile(file.read(), path, 'exec'), globals())
File "/etc/opt/xkeysnail/config.py", line 4, in <module>
from xkeysnail.transform import *
File "/opt/xkeysnail/lib/python3.6/site-packages/xkeysnail/transform.py", line 13, in <module>
def get_active_window_wm_class(display=Xlib.display.Display()):
File "/opt/xkeysnail/lib/python3.6/site-packages/Xlib/display.py", line 89, in __init__
self.display = _BaseDisplay(display)
File "/opt/xkeysnail/lib/python3.6/site-packages/Xlib/display.py", line 71, in __init__
protocol_display.Display.__init__(self, *args, **keys)
File "/opt/xkeysnail/lib/python3.6/site-packages/Xlib/protocol/display.py", line 167, in __init__
raise error.DisplayConnectionError(self.display_name, r.reason)
Xlib.error.DisplayConnectionError: Can't connect to display ":1": b'No protocol specified\n'

Can't connect to display ":1"と言っており、Xサーバに繋がらなかったようです。

xhostで確認すると、rootの接続は許可されていないのがわかります。

$ xhost

access control enabled, only authorized clients can connect
SI:localuser:miy4


起動:成功

試しにxhost +SI:localuser:rootでアクセスコントロールリストにrootを追加して、xkeysnailが起動することを確認しました。

$ xhost +SI:localuser:root

localuser:root being added to access control list
$ xhost
access control enabled, only authorized clients can connect
SI:localuser:root
SI:localuser:miy4
$ sudo /opt/xkeysnail/bin/xkeysnail /etc/opt/xkeysnail/config.py

██╗ ██╗██╗ ██╗███████╗██╗ ██╗
╚██╗██╔╝██║ ██╔╝██╔════╝╚██╗ ██╔╝
╚███╔╝ █████╔╝ █████╗ ╚████╔╝
██╔██╗ ██╔═██╗ ██╔══╝ ╚██╔╝
██╔╝ ██╗██║ ██╗███████╗ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
███████╗███╗ ██╗ █████╗ ██╗██╗
██╔════╝████╗ ██║██╔══██╗██║██║
███████╗██╔██╗ ██║███████║██║██║
╚════██║██║╚██╗██║██╔══██║██║██║
███████║██║ ╚████║██║ ██║██║███████╗
╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝╚══════╝
v0.1.0

No keyboard devices specified via (--devices) option.
xkeysnail picks up keyboard-ish devices from the list below:

-----------------------------------------------------------------------------------
Device Name Phys
-----------------------------------------------------------------------------------
/dev/input/event0 AT Translated Set 2 keyboard isa0060/serio0/input0
/dev/input/event1 Lid Switch PNP0C0D/button/input0
/dev/input/event2 Sleep Button PNP0C0E/button/input0
/dev/input/event3 Power Button LNXPWRBN/button/input0
/dev/input/event4 PC Speaker isa0061/input0
/dev/input/event5 ThinkPad Extra Buttons thinkpad_acpi/input0
/dev/input/event6 Integrated Camera: Integrated C usb-0000:00:14.0-8/button
/dev/input/event7 SynPS/2 Synaptics TouchPad isa0060/serio1/input0
/dev/input/event8 Video Bus LNXVIDEO/video/input0
/dev/input/event9 HDA Digital PCBeep card0/codec#0/beep0
/dev/input/event10 HDA Intel PCH Mic ALSA
/dev/input/event11 HDA Intel PCH Headphone ALSA
/dev/input/event12 HDA Intel PCH HDMI/DP,pcm=3 ALSA
/dev/input/event13 HDA Intel PCH HDMI/DP,pcm=7 ALSA
/dev/input/event14 HDA Intel PCH HDMI/DP,pcm=8 ALSA
/dev/input/event15 HDA Intel PCH HDMI/DP,pcm=9 ALSA
/dev/input/event16 HDA Intel PCH HDMI/DP,pcm=10 ALSA
/dev/input/event17 TPPS/2 IBM TrackPoint synaptics-pt/serio0/input0
/dev/input/event18 py-evdev-uinput py-evdev-uinput

Okay, now enable remapping on the following device(s):

------------------------------------------------------------------------------
Device Name Phys
------------------------------------------------------------------------------
/dev/input/event0 AT Translated Set 2 keyboard isa0060/serio0/input0


自動起動

ここまでをふまえて、ログイン時にxkeysnailを自動的に起動するようにしたいです。

Xサーバへの接続やuinputの読み書きのアクセスコントロールが必要なので、rootでやるのは怖さを感じる。xkeysnail用のユーザを作ることにします。


起動用のユーザを作成

$ sudo groupadd uinput

$ sudo useradd -G input,uinput -s /sbin/nologin xkeysnail

inputグループは/dev/input/*の所有者グループです。

xkeysnailユーザには/dev/input/*/dev/uinputへのアクセス権限を与えます。


/etc/udev/rules.d/40-udev-xkeysnail.rules

KERNEL=="uinput", GROUP="uinput"



/etc/modules-load.d/uinput.conf

uinput


xkeysnailユーザにsudoし、xkeysnailを実行する時は、パスワードは省略します。


/etc/sudoers.d/10-installer

miy4 ALL=(ALL) ALL,\

(xkeysnail) NOPASSWD: /opt/xkeysnail/bin/xkeysnail

10-installerはAntergos (Arch Linux)導入時に用意されたsudoersファイルです。


設定

GDMはXセッション開始時に~/.xprofileを実行するので、ここでxkeysnailを起動します。


~/.xprofile

if [ -x /opt/xkeysnail/bin/xkeysnail ]; then

xhost +SI:localuser:xkeysnail
sudo -u xkeysnail DISPLAY=:1 /opt/xkeysnail/bin/xkeysnail /etc/opt/xkeysnail/config.py &
fi

Linuxを再起動し、GDMからログインし、キーリマップが働いていることを確認します。


課題


Thinkpad keyboardのトラックポイントが効かない

上記のBluetoothキーボードを使っているのだけど、xkeysnailで同キーボードの/dev/input/event*を指定すると、トラックポイントが使えなくなる。キーボードのリマップはできている。

外付けではない、Thinkpad内臓のキーボードではトラックポイントも問題なく使えているので、同キーボード固有の問題なのだと思う。要調査。