Windows 11から Linuxデスクトップへ RDP接続したい場合、GNOMEには gnome-remote-desktop という標準のリモートデスクトップ機能がある。いわゆる xrdp ではなく、GNOME自身が持っている RDPサーバー機能を使う方法だ。
これを使って当社ではいわゆるVDIのようにして業務を行っている。VDI(Virtual Desktop Infrastructure)は、業務用のデスクトップ環境を手元のPCではなく、社内サーバーやクラウド上の仮想マシンで動かす仕組みで、利用者はノートPCやタブレットなどからリモートデスクトップで接続し、画面・キーボード・マウスだけを手元で操作する。実際のアプリケーションやデータ処理は接続先の仮想デスクトップ側で行われる。
この方式にすると、端末側に業務データをできるだけ残さずに済み、PCの入れ替えや故障時にも作業環境を復旧しやすい。また、Windows、Linux、macOSなど手元の端末が違っても、同じ業務環境へ接続できるため、小規模な会社でも運用をそろえやすいのが利点だ。各個人の使用する端末からはLinuxデスクトップへRDPでログインし、どこからでも統一された環境で作業ができる。

さて、これ自体は便利なのだが、Windows 11の日本語109キー配列キーボードから接続すると、いくつかのキー入力が期待通りに動かなかった。
手元で確認した環境は次の通り。
- クライアント: Windows 11、日本語109キー配列キーボード
- サーバー: Linux + GNOME Remote Desktop
- 接続方式: GNOME Remote Desktopのビルトイン RDPサーバー
- 関連Issue: GNOME/gnome-remote-desktop#234
この記事では、手元で踏んだ問題とローカルパッチでの回避をまとめておく。FreeRDPや GNOME Remote Desktop本体で正式に修正されれば不要になる内容なので、あくまで現時点の作業メモとして読んでほしい。
ここで紹介しているパッチは、手元の Gentoo環境での回避策です。ディストリビューションやパッケージバージョンによって事情が変わる可能性があります。
問題1: 円記号キーとパイプが入力できない
日本語キーボードの ¥ / \ キーを押しても何も入力されなかったり、Shift+¥ で | が入力できなかったりする。
ログには次のようなメッセージが出ることがある。
Unknown/invalid virtual device key 0x0 pressed
原因は大きく2層に分かれていた。
まず、古い gnome-remote-desktop ではクライアントのキーボード種別を IBM Enhanced、つまり type 4 として固定的に扱っていた。日本語キーボードは type 7 なので、スキャンコードの解釈がずれる。この点は gnome-remote-desktop 側で修正されており、Portage上では少なくとも gnome-remote-desktop-49.2 には含まれている。
ただし、それだけではまだ足りなかった。FreeRDP側の日本語キーボードテーブルで、円記号キーに相当する RDPスキャンコード 0x7D の仮想キーコードが VK_NONE になっていた。
#define KBD7_T7D VK_NONE
このため、RDPスキャンコード 0x7D が有効なキーとして変換されず、GNOME側に正しいキーイベントが届かない。
手元では、これを VK_OEM_8 に変えることで KEY_YEN として扱われ、日本語レイアウト上で ¥ / \ と Shift時の | が期待通り入力できるようになった。
--- a/winpr/include/winpr/input.h
+++ b/winpr/include/winpr/input.h
@@ -675,7 +675,7 @@
#define KBD7_T7A VK_NONE
#define KBD7_T7B VK_OEM_PA1
#define KBD7_T7C VK_TAB
-#define KBD7_T7D VK_NONE
+#define KBD7_T7D VK_OEM_8
#define KBD7_T7E VK_ABNT_C2
#define KBD7_T7F VK_OEM_PA2
Gentooでは、たとえば次の場所にパッチを置いて emerge net-misc/freerdp すれば、ビルド時に自動適用される。
/etc/portage/patches/net-misc/freerdp/japanese-kbd-yen-key.patch
問題2: 全角/半角キーで入力ソースが連続切り替えされる
GNOMEの「設定」から、入力ソースの切り替えに全角/半角キーを割り当てると、1回押しただけで入力ソースが連続的に切り替わり続けることがあった。環境によっては、全く反応しないように見える場合もある。
ローカル接続では問題なく、RDP経由の時だけ発生する。
調べてみると、Windows RDPでの全角/半角キーのイベント列が少し特殊だった。通常のキーは press と release を送るが、全角/半角キーでは次のような並びになる。
1. release-only
2. press
3. release
gnome-remote-desktop は、内部的に「押されているキー」の集合を持っている。そこに存在しないキーの release は無視する設計になっているため、この release-only を起点にする特殊な並びと相性が悪い。
手元では、release-only から始まるキーを一時的に toggle key として扱い、後続の press で press+release を1回だけ発行するようにして回避した。
ロジックとしては次のような状態を持たせる。
pending_toggle: release-only を受け取った後、press を待っているキーin_toggle_sequence: press を受け取り、最後の release を待っているキー
処理の流れはこうなる。
release-only受信
-> modifier系なら無視
-> toggle中なら最後のreleaseとして消費し、次回のpendingも予約
-> それ以外なら pending_toggle に登録
press受信
-> pending_toggle にあれば press+release を1回だけ発行
-> それ以外は通常キーとして処理
実際のデバッグでは、GNOME Remote Desktop本体ではなく handover側のユーザーサービスを見る。
G_MESSAGES_DEBUG=all journalctl --user -f -u gnome-remote-desktop-handover.service
Gentooでローカルパッチとして持つなら、たとえば次の場所に置く。
/etc/portage/patches/net-misc/gnome-remote-desktop/zenkaku-hankaku.patch
この回避は「押されていないキーの release が先に来る」という条件でだけ動くため、通常のキー入力にはほぼ影響しないはず。ただし upstream に提案するなら、日本語キーボードの時だけに限定するなど、もう少し明示的な条件を入れたほうがよさそうだ。
問題3: テンキーの数字が入力できない
もうひとつ困ったのがテンキーだった。NumLockの状態に関係なく、テンキーの 0 から 9 と小数点が数字として入力できない。
テンキーの /、*、-、+、Enter は動くが、数字キーはカーソル移動や Home / End などとして扱われてしまう。
原因は FreeRDPの日本語キーボード用テーブルにあった。KBD7、つまり日本語キーボード type 7 の定義で、テンキーのスキャンコードが VK_NUMPAD* ではなく VK_HOME や VK_UP などに直接割り当てられている。
#define KBD7_T47 VK_HOME /* テンキー7 */
#define KBD7_T48 VK_UP /* テンキー8 */
#define KBD7_T49 VK_PRIOR /* テンキー9 */
これだと NumLockの状態を見て数字にする余地がなく、常にナビゲーションキーとして処理される。
手元では、標準キーボード側の扱いと同じように VK_NUMPAD* へ戻すことで解消した。
--- a/winpr/include/winpr/input.h
+++ b/winpr/include/winpr/input.h
@@ -621,19 +621,19 @@
#define KBD7_T44 VK_F10
#define KBD7_T45 VK_NUMLOCK
#define KBD7_T46 VK_SCROLL
-#define KBD7_T47 VK_HOME
-#define KBD7_T48 VK_UP
-#define KBD7_T49 VK_PRIOR
+#define KBD7_T47 VK_NUMPAD7 /* VK_HOME */
+#define KBD7_T48 VK_NUMPAD8 /* VK_UP */
+#define KBD7_T49 VK_NUMPAD9 /* VK_PRIOR */
#define KBD7_T4A VK_SUBTRACT
-#define KBD7_T4B VK_LEFT
-#define KBD7_T4C VK_CLEAR
-#define KBD7_T4D VK_RIGHT
+#define KBD7_T4B VK_NUMPAD4 /* VK_LEFT */
+#define KBD7_T4C VK_NUMPAD5 /* VK_CLEAR */
+#define KBD7_T4D VK_NUMPAD6 /* VK_RIGHT */
#define KBD7_T4E VK_ADD
-#define KBD7_T4F VK_END
-#define KBD7_T50 VK_DOWN
-#define KBD7_T51 VK_NEXT
-#define KBD7_T52 VK_INSERT
-#define KBD7_T53 VK_DELETE
+#define KBD7_T4F VK_NUMPAD1 /* VK_END */
+#define KBD7_T50 VK_NUMPAD2 /* VK_DOWN */
+#define KBD7_T51 VK_NUMPAD3 /* VK_NEXT */
+#define KBD7_T52 VK_NUMPAD0 /* VK_INSERT */
+#define KBD7_T53 VK_DECIMAL /* VK_DELETE */
#define KBD7_T54 VK_SNAPSHOT
#define KBD7_T55 VK_NONE
#define KBD7_T56 VK_OEM_102
このパッチも FreeRDP側なので、Gentooでは次のような場所に置ける。
/etc/portage/patches/net-misc/freerdp/japanese-kbd-numpad.patch
円記号キーのパッチと同じ winpr/include/winpr/input.h を触るが、変更箇所は離れているので、少なくとも手元では同時に適用できた。
NumLockがローカルとリモートで逆に見える場合
RDP接続時に NumLockの ON/OFF がクライアント側とリモート側で逆に見える場合は、リモート側の GNOMEセッションで次の設定を入れると解消することがある。
gsettings set org.gnome.desktop.peripherals.keyboard remember-numlock-state false
これはユーザーごとの設定なので、必要なユーザーで実行する。
まとめ
手元で必要になったローカルパッチは次の3つ。
/etc/portage/patches/net-misc/freerdp/japanese-kbd-yen-key.patch
/etc/portage/patches/net-misc/freerdp/japanese-kbd-numpad.patch
/etc/portage/patches/net-misc/gnome-remote-desktop/zenkaku-hankaku.patch
整理すると、問題は次のようになる。
| 症状 | 対象 | 回避 |
|---|---|---|
¥ / \ / ` |
` が入力できない | FreeRDP |
| 全角/半角キーで入力ソースが連続切り替えされる | GNOME Remote Desktop | release-only から始まる toggle key を特別扱いする |
| テンキー数字がカーソルキー扱いになる | FreeRDP | KBD7のテンキー定義を VK_NUMPAD* に戻す |
日本語キーボードまわりは、RDPクライアント、FreeRDP、GNOME Remote Desktop、Linux側のキーコード変換が重なっていて、どこか一箇所だけ見ても原因が分かりにくい。今回の件も、GNOME側の修正でクライアント申告のキーボード種別を正しく見るようになった結果、今度は FreeRDP側の日本語キーボードテーブルの問題が表に出てきた、という形だった。
正式な修正が入るまでは、Gentooの /etc/portage/patches にローカルパッチとして置いておくのが扱いやすい。