26
27

More than 3 years have passed since last update.

xkeysnailでキーリマップする

Last updated at Posted at 2018-02-06

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の方が開発が進んでおり、機能追加、バグ修正がされています。
2019/11/11追記:2019年5月にPyPIで0.2.0がリリースされています。GitHubとPyPIで機能的には同等のものが導入できるようになっています。

キーリマップの設定

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

  • 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内臓のキーボードではトラックポイントも問題なく使えているので、同キーボード固有の問題なのだと思う。要調査。←keyイベントとmouseイベントが混在したデバイスに対してのxkeysnailの扱いが影響しているようです。fujimisakariさんのコメントを参照

26
27
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
27