ほぼタイトルの通りなのだが、怪奇現象が起こって困っている。
- FILCO Majestouch Convertible 2を使っている
- 青軸最高〜〜
- Linux (OpenSuSE Tumbleweed)ユーザ
- 自分はスペースキーをShiftに割り当ててSandSを行うため、
xcape
を使っている(後述) - BluetoothモードでMajestouchを使っているとき、一定の確率で、スペースキーを押下して離すと機内モードに入る
- もちろん、Bluetoothが停止するのでキーボードも動かない
- 怒り
という現象が起こっている。
tl;dr
xcape
使うときはダミーとして255ではなく無難なキーコードを使うこと- 255は機内モードスイッチのキーコードになっている
- キーボードを差し替えたりすると
xmodmap
の効力が切れる- ワイヤレスイヤホンはキーボードとしても追加で認識されることがある
Majestouch Convertible 2
これ(の、英語配列)を使っている。普通に使うぶんには良いキーボードだが、Bluetooth面の品質が若干微妙というレビューが多く、実際自分も時々つながらなかったり、打鍵中にプチフリーズ的な挙動をして困ることがある。しかしいきなり機内モードに入るという怪現象が起こっているのは自分だけのはず。 USB接続だと全く問題ないという声が多く、実際自分もUSB接続にすると全くトラブルは起こらない。
SandS
SandS(Space and Shift)とは、スペースキーをカスタマイズする方法の一種である。スペースキーを押しっぱなしにしたときはシフトキーとして振舞い、そのまま離したときはスペースキーが押されたものとして、または別のキーが押下されたときはShift+そのキーが打鍵されたものとして扱うというもので、比較的ポピュラーなスペースキーのカスタム方法だ。
このカスタマイズは日本語入力方式であるSKK(シフトキーを非常に多用する)のユーザがよく取り入れている。自分もSKKのためにSandSを使っている。
そして、Linux環境(のX11環境)においてSandSを実現するために使われるソフトウェアがxcape
だ。
xcape
xcape
とは、キーボード入力を監視し、修飾キーを押したときと離したときとで別々のキーコードを発するようにできる。今回の場合は以下のような設定で起動している:
# sands.sh xmodmap -e 'keycode 255=space' xmodmap -e 'keycode 65=Shift_L' exec env xcape -d -e '#65=space'
xmodmap
はX11についてくるユーティリティで、特定のキーコードを別のキーにマップする。それぞれの行がやっていることは以下の通り:
- xmodmap
- キーコード
255
をスペースに割り当てる- キーボードは255を送信できないので、衝突することはない
- 送信できないが割り当てられてないとは言ってない(ここ伏線です)
- キーコード
65
(X11ではスペースバーに対応する)を左シフトに割り当てる- スペースが入力されなくなる
- キーコード
- xcape
- キーコード
65
を単独で使った場合はスペースに割り当てる- キーコード
65
の押下とリリースとを連続して検知した場合はキーコード255
を送信するという振舞いになる - キーコード
255
が受信されるので、X11は割り当てられたスペースキーが押されたと判定し、スペースが入力される
- キーコード
- xcapeは
-d
スイッチでフォアグラウンドで起動させ続け、systemd管理下に置く
- キーコード
これで、スペースバーを押下している間はシフトキーとして振る舞うようにできた。
前提知識として、スキャンコードとキーコードとキーシムとは別の概念。
怪現象
Bluetoothモードで使っているとき、どういうわけかスペースキーを押すと機内モードに入ってしまうという現象に悩まされることになった。上掲のスクリプトを再起動させるといったん収まるのでそれでごまかしてきたが、その度にUSB接続に戻したりsystemctl
を叩いたりしなければならないので億劫だ。
今回たまたまjournalctl -xe
を叩いてログを見る用事があったところ、この現象が起こり、xcape
がログを残していた。
10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 2, key code 65 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Key pressed! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 3, key code 65 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Key released! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Generating space! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Ignoring generated event. 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Ignoring generated event. 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 2, key code 65 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Key pressed! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 3, key code 65 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Key released! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Generating XF86RFKill! 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Ignoring generated event. 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Ignoring generated event. 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 2, key code 22 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 3, key code 22 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 2, key code 22 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 3, key code 22 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 2, key code 22 10月 06 22:52:35 localhost.localdomain sands-f.sh[25879]: Intercepted key event 3, key code 22 ...
XF86RFKill
という不穏な文字列が見える。こんな設定した覚えはない。たぶん、GNOMEはXF86RFKill
を受信すると、(RadioFrequencyKillというキー名から推測する限り)機内モードを起動するようだ。機内モードはsystemd-rfkill.service
というsystemdのサービスが担当しているらしく、このサービスが起動していることを確認した。
以下の文献によれば、event 2はキー押下に対応していて、event 3は離すことに対応している。
4と5はマウスボタンに対応しているっぽいことがわかる。
キーコードは/usr/share/X11/xkb/keycodes/xfree86
に定義されていて、65はスペースキーに対応している。
したがって、以下のことが分かった:
- key code 65を押して離すことをxcapeが認知している
- 一度目は正常にスペースに対応するkeysymを発出できている
- 二度目はなぜか同じキーコードの組み合わせに対して
XF86RFKill
を発出した
keysymを確認しよう。
/usr/include/X11/XF86keysym.h
によれば、
#define XF86XK_RFKill 0x1008FFB5 /* Toggle radios on/off */
と書いてある。ちなみに/usr/include/X11/keysymdef.h
によれば、普通のスペースは
#define XK_space 0x0020 /* U+0020 SPACE */
に対応している。正直ぜんぜん似ていない。xcapeのソースコードを読む限り、キーコードからKeySymへの対応を取っている関数はXKeysymToString (XkbKeycodeToKeysym (self->ctrl_conn, k->key, 0, 0))
なので、これを調べる。
3、4つめの引数はいつも0なので無視するとして、入力されているキーコードが狂っている可能性を考えて、ちょっと細工をした:
diff --git a/xcape.c b/xcape.c index 12f48c6..39cba03 100644 --- a/xcape.c +++ b/xcape.c @@ -327,9 +327,9 @@ void handle_key (XCape_t *self, KeyMap_t *key, { for (k = key->to_keys; k != NULL; k = k->next) { - if (self->debug) fprintf (stdout, "Generating %s!\n", + if (self->debug) fprintf (stdout, "Generating %s (from %d)!\n", XKeysymToString (XkbKeycodeToKeysym (self->ctrl_conn, - k->key, 0, 0))); + k->key, 0, 0)), k->key); XTestFakeKeyEvent (self->ctrl_conn, k->key, True, 0);
すると普段はちゃんと(xmodmap
が変換した)255
を受け取っていそう:
10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Intercepted key event 2, key code 65 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Key pressed! 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Intercepted key event 3, key code 52 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Intercepted key event 3, key code 65 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Key released! 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Generating space (from 255)! 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Ignoring generated event. 10月 07 01:22:14 localhost.localdomain sands-f.sh[23117]: Ignoring generated event.
このまましばらく使ってみて、怪現象が起こるまで待ってみる。
そういえばNothing earを接続したら、(タッチして再生したり停めたりするために)キーボードとしても認識している様子を確認した。嫌な予感がする。
10月 07 01:25:31 localhost.localdomain /usr/libexec/gdm/gdm-x-session[2030]: (II) XINPUT: Adding extended input device "Nothing ear (1) (AVRCP)" (type: KEYBOARD, id 12) 10月 07 01:25:31 localhost.localdomain /usr/libexec/gdm/gdm-x-session[2030]: (**) Option "xkb_layout" "us"
すると 見事完全に再現してしまった。
10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Intercepted key event 2, key code 65 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Key pressed! 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Intercepted key event 3, key code 65 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Key released! 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Generating XF86RFKill (from 255)! 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Ignoring generated event. 10月 07 01:29:19 localhost.localdomain sands-f.sh[23993]: Ignoring generated event.
再現手順が分かった。
- Majestouchを接続した状態でxcapeを起動する
- その状態でNothing earを接続する
- Majestouchでスペースキーを押下して離す
- すると機内モードになる
なんだよこれ!!!ワザップかよ!!! しかし、キーコードは255を受信している。
仮説
そこで、仮説を立ててみる:
- 事実として、xcapeはキーコード65の押下とリリースを検出している
- 事実として、xmodmapはスペースの押下をキーコード255に変換している
- なんかがミスって、X11の中ではキーコード255が
XF86RFKill
に割り当てられてしまっている??
もうちょっと調べてみる。 調べてみると、xmodmap -pk
するとキーコードに対応するKeySymが得られる:
スクリプトを実行してSandSが有効な状態では・・・
$ xmodmap -pk ... 255 0x0020 (space) 0x0000 (NoSymbol) 0x0020 (space)
Nothing earを接続した後は・・・
255 0x1008ffb5 (XF86RFKill) 0x0000 (NoSymbol) 0x1008ffb5 (XF86RFKill)
当たり…………………………………………………
修正
というわけで、以下のことがわかった:
- Nothing Earをつなぐとキーボードとしても認識される
- キーボードを差すと
xmodmap
の効力が切れる - キーコード255がそのままX11に送られる
- キーコード255は元々は
XF86RFKill
になる XF86RFKill
を受信したGnome(か誰か)が機内モードを起動する
この連鎖のどっかを破壊すればよい。とりあえずsystemd-rfkill
を停められないか試すが:
# systemctl stop systemd-rfkill.socket # systemctl disable systemd-rfkill.socket
これだとダメだった(勝手に動く)ので、キーコードを無難そうなやつに修正する。207(Hyper_L)
という修飾キーのためのコードが空いてそうだったのでこれを使うことにした:
diff --git a/bin/sands-f.sh b/bin/sands-f.sh index 3853e3e..fa913e0 100755 --- a/bin/sands-f.sh +++ b/bin/sands-f.sh @@ -4,7 +4,8 @@ set -eux if pgrep -x xcape ; then killall xcape # to prevent xcape daemon duplication fi -xmodmap -e 'keycode 255=space' +# 207 is for Hyper_L(unused keycode) +xmodmap -e 'keycode 207=space' xmodmap -e 'keycode 65=Shift_L' exec env xcape -d -e '#65=space'
これで、勝手に機内モードになるということはなくなった。すさまじい謎解きだった。