LinuxでAndroidを動かすWaydroidの中で、テンキーの数字が打てなくなった。キーボードをGboardの「日本語」にすると打てて、「English (US)」にすると打てない。上段の数字キーは、どちらでも打てる。ホスト(Arch Linux、sway)でテンキーを打つと、数字が出る。
swayの設定に input * xkb_numlock enabled を足したが、直らなかった。なぜ効かないのかを、ソースと実測で調べた。
先に結論
- ホストのNumLockの状態は、Waydroidの中のAndroidに渡らない。swayの設定は、Waydroidには効かない。
- AndroidのNumLockは、Waydroidの中で、入った直後に消されている可能性が高い。ソースの読みと実測が合っているが、消す判定の値そのものは、見ていない。
- Androidのアプリで、テンキーを数字に割り当てるキーレイアウトを足したら、英語のままテンキーが打てた。
ソースで分かったこと
読んだのは、WaydroidのHWC(android_hardware_waydroidのhwcomposer/wayland-hwc.cpp)と、Android 13(android13-release)の入力まわり、Waydroidが当てるパッチ(android_vendor_waydroid)。
1. ホストのキーは、キーコードのまま渡される。修飾キーの状態は渡されない。
HWCは、Waydroidのウィンドウに入ったキー入力を、キーコードの形で、Android側のパイプ(/dev/input/wl_keyboard_events)に書く。Waylandの修飾キーの状態を受け取る関数 keyboard_handle_modifiers は、中身が空になっている。ホストのNumLockがオンでも、Androidはそれを知らない。
2. テンキーの数字は、AndroidのNumLockがオンのときだけ出る。
Android 13の Generic.kcm では、NUMPAD_1 は、base: fallback MOVE_END、numlock: '1' と定義されている。NumLockがオフなら、Endキーになる。
3. NumLockの状態は、キーを離したときに切り替わる。
Keyboard.cpp の toggleLockedMetaState は、キーを押したときは何もせず、離したときに、状態を反転する。
4. Waydroidが作る入力機器は、出せるキーを2つしか登録していない。
WaydroidのEventHubのパッチ(Android 13用の 0004-EventHub-Add-wayland-inputs-support.patch)は、wayland_keyboard の keyBitmask に、BTN_MISC と KEY_OK だけを登録する。
AndroidのEventHubの markSupportedKeyCodes は、キーレイアウトにそのキーがあっても、この登録に無いキーは、「機器に無いキー」と判定する。KeyboardInputMapper::updateLedState は、NumLockのキーが無い機器について、NumLockの状態を消す。
NumLock(コード69)は、この登録に入っていない。
実測
dumpsys input で、入力機器の状態を取った。キー入力の履歴を含まない部分だけを、次のように取り出せる。
sudo waydroid shell dumpsys input | sed -n '/Input Reader State/,/Input Dispatcher State/p' > out-1.txt
wayland_keyboard の項目の、MetaState(修飾キーとロックの状態)と、DownTime(最後にキーが押された時刻)を、見た。
| 操作 | DownTime | MetaState |
|---|---|---|
| 最初 | 77639155471000 | 0x0 |
a を押す | 78176874436000 | 0x0 |
| NumLockを押して離す | 78198740555000 | 0x0 |
NumLockを押すと、DownTime は変わる。キー入力は、wayland_keyboard に届いている。それなのに、MetaState は 0x0 のまま。NumLockがオンになるなら、0x200000 のビットが立つはずで、立たなかった。キー入力は届いて、状態だけが残らない。これが、上の4の読みと合う。
機器の一覧(Event Hub State)には、wayland_keyboard が Generic.kl と Generic.kcm を使っていると出ていた。キーレイアウトに、NUM_LOCKの定義はある。
確認できていないこと
- 4の判定(NUM_LOCKが登録に無いので、NumLockの状態が消える)が、直接の原因かどうか。コードの読みと、
MetaStateが0x0のままという結果からの推論で、判定の値は見ていない。 - 日本語のときに、テンキーが打てる理由。Gboardの日本語入力が、テンキーのキーコードを自分で数字として扱っているのだと思うが、Gboardは非公開で、確認できない。
- 入れたAndroidイメージのパッチが、読んだパッチと同じ内容か。Androidのバージョンが13であることだけを、確認した。
同じ症状は、Waydroidの Issue #1944(2025年6月、未解決)にも出ている。
直し方
Waydroidのパッチを直すには、Androidのイメージを作り直す必要がある。代わりに、Androidのアプリで、キーレイアウトを足した。android-numpad-klが、テンキーのキー(スキャンコード71〜73、75〜77、79〜83)を、NumLockに関係なく、数字のキーに割り当てるレイアウトを提供している。別のIssueのコメントで見つけた。
入れる前に、ソースを全部読んだ。Javaは4ファイル、約500行で、権限は RECEIVE_BOOT_COMPLETED だけ。通信とコマンド実行は、見つからなかった。
ビルドは、配布のAPKを使わず、ソースからやった。元のビルド設定はGradleとJDK 17を要求するが、手元にはAndroid SDK(platform 36)とJDK 27しか無い。aapt2、javac、d8、zipalign、apksigner を順に呼ぶ、短いスクリプトで組んだ。なお、/opt/android-sdk/build-tools の実行ファイルは、rootしか実行できない権限だったので、コピーして使った。
入れるときに詰まったこと
waydroid app installは、結果を返さない。 ソースを読むと、Android側にインストールを始めるよう伝えるだけで、結果を受け取る処理が無い。入らなくても、何も表示されない。失敗の理由は、sudo waydroid shell -- logcat -dで見た(--が要る)。python3がpyenvの版だと、waydroidが動かない。dbusを読み込めない。/usr/bin/python3 /bin/waydroid ...と、システムのPythonを指定した。- Play プロテクトが止めた。 元の設定の
targetSdkを 31 にすると、「安全ではないアプリをブロックしました(Androidの以前のバージョンを対象としている)」と出て、「OK」しか選べない。35にすると、「アプリのスキャンをおすすめします」に変わった。 - ダイアログが、一瞬で消えた。 マウスのフォーカスをWaydroidのウィンドウに移すと、消える。HWCの
keyboard_handle_enterを読むと、ウィンドウにフォーカスが入るたびに、Androidの画面を切り替えている。そのためだと思うが、これは確認していない。先にフォーカスをWaydroidに置いてから、sleep 8; waydroid app install ...を実行し、ダイアログに答えた。
最後に、Androidの設定の「物理キーボード」で、wayland_keyboard に、「Full keyboard with numpad, US QWERTY」を選ぶと、英語のままテンキーで数字が打てた。
手元に残したもの
ビルドのスクリプトと、できたAPK、手順は、~/repos/waydroid-numpad-kl に置いた。ホストの xkb_numlock enabled は、Waydroidには効かないと分かったが、ほかのアプリには効いている可能性があるので、消していない。
