日本語入力中、タッチパッドに手が触れて変換が確定してしまう問題に悩まされていました。原因はシンプルで、外部マウスを使っている間もタッチパッドが有効なままになっていたことです。
KDE Plasmaには「外部マウス接続時にタッチパッドを無効化する」というチェックボックスが標準で用意されています(システム設定 → 入力デバイス → タッチパッド)。ところが、このチェックボックスがグレーアウトされていて押せません。せっかく機能があるのに使えないという、地味にモヤモヤするやつです。
原因調査
環境はPlasma 6.7.4、Wayland、タッチパッドはELAN製のI2C接続デバイス(ELAN1201:00 04F3:3098)です。
KWinはDBus経由で各入力デバイスの詳細なプロパティを公開しています。
qdbus6 org.kde.KWin /org/kde/KWin/InputDevice/event15
ここに supportsDisableEventsOnExternalMouse というプロパティがあり、確認すると true が返ってきました。つまりドライバレベルでは機能をサポートしているのに、なぜかKCM(設定画面)側ではグレーアウトしています。動くはずのものが動かないのは何とも言えない気持ちになりますが、KDE側のUIバグと判断し、GUIに頼らず自力で実装することにしました。
KWinのDBus APIで直接制御する
同じDBusオブジェクトには enabled という読み書き可能なプロパティもあります。これを直接叩けば、コンフィグファイルを書き換えて再読み込みさせるより確実かつ即座に効きます。
# 無効化
qdbus6 org.kde.KWin /org/kde/KWin/InputDevice/event15 \
org.freedesktop.DBus.Properties.Set org.kde.KWin.InputDevice enabled false
# 有効化
qdbus6 org.kde.KWin /org/kde/KWin/InputDevice/event15 \
org.freedesktop.DBus.Properties.Set org.kde.KWin.InputDevice enabled true
実行した瞬間にタッチパッドの反応が変わることを確認できました。あとはこれを「マウス使用中かどうか」に応じて自動で呼び出す仕組みを作るだけです。
「マウス接続」ではなく「マウス使用中」で判定する
当初はUSBレシーバーの抜き差しを検知する方式で作りましたが、実際の使い方は「レシーバーは挿しっぱなしで、マウス本体の電源だけを切る」というものでした。レシーバーが挿さっているかどうかでは判定できません。
そこで方針を変更し、マウスの生入力(/dev/input/eventN)を直接監視する方式にしました。
- マウスから入力イベントが来ている間 → タッチパッド無効
- 一定時間(120秒)入力が無い → タッチパッド有効に戻す
- レシーバー自体が抜かれた → 即座にタッチパッド有効に戻す
つまずいたポイント
誤検出されるダミーデバイス
DBusで touchpad プロパティが true のデバイスを検索したところ、本物のELANタッチパッド(event15)だけでなく、Logitechレシーバー(event8)まで touchpad=true と誤って報告されていました。プロパティでの判定をやめ、デバイス名を直接指定するよう修正しました。
head -c 1 がEINVALで失敗する
生入力デバイスからマウスの活動を検知するために head -c 1 /dev/input/eventN を試しましたが、無効な引数です(EINVAL)で失敗しました。evdevの文字デバイスは struct input_event 未満のサイズでの読み込みを受け付けない仕様だったようです。dd bs=24 count=1 に変更してあっさり解決しました(64bit環境でのstruct input_eventのサイズは24バイトです)。
inputグループの反映タイミング
/dev/input/eventN を一般ユーザーが読むには input グループへの追加が必要です(sudo usermod -aG input $USER)。ところがGUIの再ログインだけではsystemdのユーザーセッション([email protected])自体が再生成されず、グループ変更が反映されませんでした。結局、完全な再起動が必要でした。
最終的な仕組み
~/.local/bin/touchpad-mouse-toggle.sh としてシェルスクリプトを作成し、systemdのユーザーサービスとして常駐させました。
- マウスのUSBレシーバーが挿さっているか(
lsusb)を確認 - 挿さっていれば、マウスの生入力デバイスを
ddで監視 - 入力があるたびにタッチパッドを無効化
- 120秒間入力が無い、またはレシーバーが抜かれたらタッチパッドを有効化
スクリプトの中身はこんな感じです。環境固有の値(デバイス名・VID:PID・DBusのパス)は自分の環境に合わせて書き換えてください。
#!/usr/bin/env bash
# ~/.local/bin/touchpad-mouse-toggle.sh
set -u
TOUCHPAD_DBUS_PATH="/org/kde/KWin/InputDevice/event15"
MOUSE_VID_PID="046d:c52b" # lsusb で確認したレシーバーのVID:PID
MOUSE_EVENT_DEVICE="/dev/input/event8"
IDLE_TIMEOUT=120 # 秒
set_touchpad() {
local enabled="$1"
qdbus6 org.kde.KWin "$TOUCHPAD_DBUS_PATH" \
org.freedesktop.DBus.Properties.Set \
org.kde.KWin.InputDevice enabled "$enabled"
}
mouse_connected() {
lsusb | grep -qi "$MOUSE_VID_PID"
}
while true; do
if ! mouse_connected; then
set_touchpad true
sleep 5
continue
fi
last_active=$(date +%s)
set_touchpad false
while mouse_connected; do
# struct input_event は 24 byte 単位でしか読み込めない
if timeout 1 dd if="$MOUSE_EVENT_DEVICE" bs=24 count=1 status=none 2>/dev/null | grep -q .; then
last_active=$(date +%s)
set_touchpad false
fi
now=$(date +%s)
if (( now - last_active > IDLE_TIMEOUT )); then
set_touchpad true
fi
done
set_touchpad true
done
実行権限を付けておきます。
chmod +x ~/.local/bin/touchpad-mouse-toggle.sh
このスクリプトをsystemdのユーザーサービスとして常駐させます。~/.config/systemd/user/touchpad-mouse-toggle.service の中身は以下の通りです。
# ~/.config/systemd/user/touchpad-mouse-toggle.service
[Unit]
Description=Disable touchpad while external mouse is in use
After=graphical-session.target
[Service]
Type=simple
ExecStart=%h/.local/bin/touchpad-mouse-toggle.sh
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
設定を反映して有効化します。
systemctl --user daemon-reload
systemctl --user enable --now touchpad-mouse-toggle.service
ログイン時に自動的に立ち上がるようになりました。journalctl --user -u touchpad-mouse-toggle.service -f でログを眺めながら動作確認すると安心です。
KDEの標準機能がハードウェアの組み合わせによっては使えないことがありますが、KWinはかなり詳細なDBus APIを公開しているので、GUIを介さずに同等以上のことが自力で実現できます。チェックボックスがグレーアウトしていても、裏側のプロパティが実際には機能する場合があるので、qdbus6 org.kde.KWin で該当デバイスのプロパティ一覧を眺めてみる価値はありそうです。