Linuxのトラックパッドを制御する

# Linuxデスクトップでトラックパッドのジェスチャが使えない問題

LinuxのDesktopではトラックパッドの対応がMacやWindowsに比べて非常に遅れている。

いまやMacのトラックパッドによるジェスチャはかなりの市民権を得ていて、トラックパッドを指の動かす方向の逆向きに動かすナチュラルスクロールはLinuxを含め様々なOSに提供されるようになった。

WindowsでもWindows10よりジェスチャの扱いが強化されていて、完全ではないものの、Macbookに近い操作を出来るようになっている。

Linuxのトラックパッドやマウスなどのデバイス制御には以前はSynaptics(GUIパッケージマネージャのSyanpticと間違えやすい)を利用していたが、近年、Ubuntuでは15.04頃からWayland由来のlibinputを利用している。 なお、synapticsはこれからメンテナンスモードに移行して積極的に開発はされない。

Waylandでなくとも、Xorgでlibinputを使用することができる。UbuntuではWaylandではなくXorgで起動している場合には、xf86-input-libinputを利用することでsynapticsの代わりとしてlibinput利用している。


[![Image from Gyazo](https://i.gyazo.com/d35327795c34f485701322d9ccc54867.png)](https://gyazo.com/d35327795c34f485701322d9ccc54867) 
https://wayland.freedesktop.org/libinput/doc/latest/what-is-libinput.html#libinput-and-x-org


また、直近のUbuntuの19.10でGnome(Wayland)にジェスチャのサポートが入ったが未だ不十分な状況である。

# トラックパッドを制御してジェスチャを扱えるようにする

ジェスチャを扱えるようにするためにはどうしたら良いか。

1. トラックパッドの入力値を得る
2. 入力値からジェスチャを判別する
3. ワークスペースやブラウザのGUI操作に紐付け

これらの要素についてそれぞれ考えてみる。

## デバイスの入力を読む

トラックパッドの入力を読み込むためにはlibinput-toolsをインストールする。
https://wayland.freedesktop.org/libinput/doc/latest/tools.html

```sh
$ sudo apt-get install libinput-tools
```

`libinput debug-events`でデバイスの入力情報を標準出力に吐き出してくれる。

```console
-event2   DEVICE_ADDED     Sleep Button                      seat0 default group5  cap:k
-event17  DEVICE_ADDED     DLL075B:01 06CB:76AF Touchpad     seat0 default group7  cap:pg  size 101x57mm tap(dl off) left scroll-nat scroll-2fg-edge click-buttonareas-clickfinger dwt-on
-event4   DEVICE_ADDED     AT Translated Set 2 keyboard      seat0 default group13 cap:k
 event17  POINTER_MOTION    +1.17s        1.22/  0.00
 event17  POINTER_MOTION    +1.18s        1.22/  1.22
 event17  POINTER_MOTION    +1.18s        1.22/  0.00
-event4   KEYBOARD_KEY      +2.56s      *** (-1) pressed
 event4   KEYBOARD_KEY      +2.67s      *** (-1) released
```

デバイスの接続情報やトラックパッド上のポインタの移動の情報、キーボード入力(マスクされている)が取得できる。


また、3本指でスワイプしてみると以下の結果が取れる。

```console
-event17  GESTURE_SWIPE_BEGIN +296.35s  3
 event17  GESTURE_SWIPE_UPDATE +296.35s 3  6.14/-0.28 (24.06/-1.09 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.36s 3  8.06/-0.81 (21.87/-2.19 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.36s 3  7.71/-0.81 (20.78/-2.19 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.37s 3  6.09/-0.81 (16.40/-2.19 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.38s 3  4.06/-0.41 (10.94/-1.09 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.38s 3  6.49/-0.81 (17.50/-2.19 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.39s 3  4.06/-0.41 (10.94/-1.09 unaccelerated)
 event17  GESTURE_SWIPE_UPDATE +296.40s 3  2.84/ 0.00 ( 7.66/ 0.00 unaccelerated)
 event17  GESTURE_SWIPE_END +296.41s    3
```

`GESTURE_SWIPE_UPDATE +296.40s 3  2.84/ 0.00`の1行にはジェスチャのタイプと指の本数、移動距離が含まれている

libinput-debug-eventsを利用することでデバイスのイベントやスワイプのイベントやピンチズームなどのイベントを簡単に取得できる。
(libinputのCのAPIもあるが、こちらは今回は利用しない)

なお、Ubuntu 14.10以前のsynapticsでは"synclient -m"コマンドでトラックパッドのログを表示できるが、スワイプイベントなどの出力はなく、最新のsynclientでは-mオプションが削除されたためイベントの取得が出来なくなっている。


## libinput debug-deventsからジェスチャを抽出する

> `GESTURE_SWIPE_UPDATE +296.40s 3  2.84/ 0.00`

この1行をイベントとして扱い、複数行のイベントの移動距離の平均値を取得すれば、どちらに移動しているかを判別出来る。
GESTURE_SWIPE_END来るまでに移動距離の平均値を出してあげれば良さそうである。

ちなみに計算してみると、移動量の平均値はx=5.68125, y=-0.5425となり右方向に移動しているということになる

右方向に3本指のスワイプを行ったということが分かる。

## ジェスチャをGUI操作に割り当てる

スワイプをしたことがわかったら、次はジェスチャをGUIを操作しているように見せるためにはどうしたら良いか。
スワイプしたら、シャッと画面が動いて欲しい。 1番簡単そうなのは既存のショートカットキーを呼び出す事である。

ショートカットキーを呼び出すにはxdotoolが便利である。 xdotoolはキーボード入力とマウス操作をエミュレート出来る。
http://manpages.ubuntu.com/manpages/trusty/man1/xdotool.1.html

Workspaceの移動には大抵ショートカットキーがあるのでこれらをxdotoolのコマンドで表すと、
* 前のワークスペース移動 `$ xdotool key ctrl+alt+Up`
* 次のワークスペース移動 `$ xdotool key ctrl+alt+Down`

また、ブラウザの操作も同様に、
* ブラウザの戻る `$ xdotool key alt+Left`
* ブラウザの進む `$ xdotool key alt+Right`


このようにコマンドでワークスペース操作やブラウザのGUI操作をエミュレートできるようになる。
(本当はピクセル単位でジェスチャの移動距離と同期させてワークスペースを動かしたほうが直感的なフィードバックが得られるが、WMやアプリケーション側の実装に依存するため、今回はショートカットキーのエミュレートで済ませてしまう)

# イベント駆動なプログラムを書く

基本的には下記の内容を逐次ループしながら実行するだけである。

デバイスの入力の読み込み、ジェスチャ判別し、コマンド(xdotool)呼び出す。これらをループし続ければ良い。

libinputからジェスチャを認識させるRuby製のソフトウェアFusuma(https://github.com/iberianpig/fusuma) を作った。
以前にもsynaptic向けのperlスクリプト(https://github.com/iberianpig/xSwipe) も作っていた。それを含めるとこれが2作目。

図に起こしてみると下記のような形になる。

[![Image from Gyazo](https://i.gyazo.com/24225ead8dde3f0528372e1f55c448a0.png)](https://gyazo.com/24225ead8dde3f0528372e1f55c448a0)

イベント駆動プログラミングっぽい事をしていてユーザー操作に対して以下をループする。

 1. libinput-debug-eventsの生成イベントをループで処理
 2. スワイプ用、ピンチ用などのイベントディスパッチャが待機
 3. イベントがディスパッチ可能になるまでキューイングし、1に戻る
 4. イベントハンドラが設定ファイルよりジェスチャに対応したコマンドを取得
 5. 取得したコマンドを実行し、1に戻る

## Fusumaの実行デモ
[![Image from Gyazo](https://i.gyazo.com/3f8026aef136647b599d375dcd842c2d.gif)](https://gyazo.com/3f8026aef136647b599d375dcd842c2d)

実行中のスクリーンキャプチャなので手元が映っていない。
ぜひインストールして試してみて欲しい。

Rubyが書けるならディスパッチャやイベントハンドラの部分もGemで拡張可能なので、タップやスタイラス、キーボードのイベントも取得したり出来るので色々できるはず。[”キー押し+スワイプ”のイベントを認識するディスパッチャ](https://github.com/iberianpig/fusuma-plugin-keypress)を書いてみた。

# ジェスチャは手放せない

普段の生活で必須になってしまい、 ワークスペースの切替やブラウザの戻る、進むなどFusumaをオフにすると途端に困る程度に依存してしまっている。

今後はWaylandであれば、GnomeShellの拡張機能を書けばjavascriptでイベント受け取ることが出来るので割と敷居が低くなってきそう。

自由にデバイス入力を扱えると便利な機能が色々と作れそうなことに気づいた。
イベントを拾うことが出来れば今回のように様々なアクションに紐付け可能なので、
Webカメラでハンドジェスチャ、顔ジェスチャ?なども作れそうな気がしてくる。
姿勢が悪いと通知とか。いらないか。

これからも身近でマイナーな課題を見つけて地道に解決していきたい。