以下の内容はhttps://turgenev.hatenablog.com/entry/2025/10/02/001505より取得しました。


Moonlight+Sunshineの日本語キーボード由来の誤動作を(無理やり)修正する

MoonlightSunshineは、マウスキャプチャの機能があってゲームプレイなどに適したリモートデスクトップソフトウェア(Moonlightがクライアント側、Sunshineがサーバー側)で、以前の記事リモートデスクトップで視点移動ありのゲーム(マイクラ等)をプレイするには【Moonlight+Sunshineなど】 - turgenev’s blogで紹介しました。

今回は、日本語キーボードでMoonlight+Sunshineを使うにあたって発生した不具合を(かなり無理やり)修正する方法を解説します。

今回の環境は、クライアント(Moonlight)がWindows、サーバー(Sunshine)がLinuxLinux Mint, X11)です。キーボードの扱いはOSごとに結構異なるのでこれ以外の組み合わせではまた別の対処が必要になると思いますが、全く役に立たないこともないかもしれません。

具体的に発生していた不具合は以下の通りです。

  • 「半角全角」(1の左隣)キーが、押す・離すが逆になったような挙動をしていて、離した後も連続して入力メソッドが切り替わり続ける。
  • 「\ |」(Backspaceの左隣)キーが入力されない。
  • 「\ _」(右Shiftの左隣)キーで「< >」が入力される。

このうち1つ目と3つ目についてはちょうど最近https://github.com/moonlight-stream/moonlight-qt/pull/1589というプルリクエストが作成されていてこれが取り込まれれば修正される可能性もありますが、2つ目については治っていません。

詳しくはまだ理解できていないのですが、おそらくMoonlight+SunshineはUSキーボードを前提に設計されており、日本語キーボードからの入力であっても一旦USキーボードのスキャンコード(一言でいえば、キーボード上の「位置」を表すコード)に変換してから入力するような処理になっているように感じます。

US配列のキーボード

これがその標準的なUSキーボードの配列で、日本語キーボードはこのうちBackspaceとEnterと右Shiftがキー1個分ずつ短くなってそれぞれ「\ |」「] }」「\ _」になっています。そのかわり、EnterとBackspaceの間にあった「\ |」キーはEnterキーの一部になっています。

ここで、スキャンコード(位置)的な観点からは、日本語キーボードの「] }」は英語キーボードにおける「\ |」キーに対応すると考えることができます。一方で、あとの2つである「\ |」と「\ _」についてはUSキーボードには対応する位置が存在しません。この2キーで問題が発生するのはこれが原因です。とはいえ後者は入力できないのではなく「< >」になるわけで、そこまでは説明できないのですが…。

  • 追記: 「<>」に関しては、キーボードを日本語設定にしたときにデフォルトで生成されるxkbの設定に
    key <LSGT> {
            type= "FOUR_LEVEL",
            symbols[Group1]= [            less,         greater,             bar,       brokenbar ]
        };
    というようなものが含まれていてこのbarが反応してしまっているということがわかりました。

半角全角に関しては多分これらとは別で、Windowsの問題な気がします(理由は後述)。

ちなみに、Moonlight+Sunshineは比較的低レイヤ(uinput)でキーボード入力をエミュレートするのでこうした問題が発生しますが、より高レイヤであるXを直接使ってキーボード入力をする方法(x11vnc経由での接続など)では発生しません。基本的にはMoonlight+Sunshineはゲーム用がメインでWASDほか左側のキーしか使わない場合も多いはずで、その他作業はvncでやるというのも解決策の1つではあります。

修正の方法

では本題の修正方法に移ります。といっても実はそこまで完璧な解決策ではないです。

どうやるかというと、まずWindows側で問題が発生しているキーの入力を(日本語・英語キーボードにともに存在していて問題が発生しづらい)別のキーに置き換えます。そのあと、LinuxXorgのレイヤでそれらのキーボード入力を目的のものに変更します(udevのhwdbをいじるのも検討しましたがMSC_SCANみたいなスキャンコードのデータを含む行が送られていないので無理そうです)。

つまり、この「別のキー」というのが犠牲になります(という点で完璧ではないです)。自分の場合、テンキーレスキーボードを使っているため、テンキーを活用することにします。具体的には、「\ |」をテンキーの「+」、「全角半角」をテンキーの「-」に置き換えることにします。ちなみにF13-24とか日本語キーボードに存在しないようなキーを使おうとしてもうまくいきませんでした。

この部分はAutoHotkeyを使います。以下のような常駐スクリプトを動かします。(AutoHotkey v2向けですがv1でも同様に書けるはず)

#HotIf WinActive("ahk_exe Moonlight.exe")
VKDC::NumpadAdd
*sc029::
{
SendInput "{NumpadSub}"
return
}
#HotIf

VKDC(仮想キーコード0xDC)というのが「\ |」を表します。「sc029」はスキャンコード29番ということです。「\ |」のほうはシンプルなキーの置き換えの構文で済む一方で、半角全角のほうはわざわざSendInputを使っています。これは半角全角のキーが他のキーとは違う特殊な動作をするためで、「\ |」と同じように書いても上手く動作しません(ただしここに書いた以外の方法がある可能性はあります)(このコードはAIに聞いたものです)。先ほど半角全角が「Windowsの問題な気がします」と書いたのはこのへんが理由です(Linux側では特別な扱いは要らなかった)。

これを起動してMoonlight+Sunshineで接続してみて、「半角全角」「\ |」がそれぞれ「-」「+」として入力されるかを確認しましょう。

では次にLinux側で設定を行います。無駄に副作用を起こしたくないのでSunshineが作成するキーボードデバイスだけに適用できるようにします。

まずxinputと打って出てくるデバイスの一覧から、Sunshineのものがどのidなのか特定します。基本的には「Keyboard passthrough」という名前のはずです。sudo evtestなどを用いて特定することも可能です。ここではidが11だったとします。

次にxkbcomp -i 11 $DISPLAY test.xkbと打つと、test.xkbというファイルが作成され、ここに現在のキーボードの設定が書き込まれています。このファイルをいじってxkbcomp -i 11 test.xkb $DISPLAYと(先ほどとは引数の順番を逆にして)実行すると設定が即時適用されるという流れです。ちなみに元の状態にリセットしたい場合はsetxkbmap -device 11 jp日本語配列で使っているなら多分これでいいはず)としてください。

で、この.xkbファイルをどう変えるかというと、先ほど入力されるよう設定されたテンキーの「-」「+」、および現在入力されてしまっている「< >」を、それぞれ目的のキーと入れ替えます。

.xkbファイルの最初のほうには

...
<ESC> = 9;
<AE01> = 10;
<AE02> = 11;
<AE03> = 12;
<AE04> = 13;
<AE05> = 14;
<AE06> = 15;
<AE07> = 16;
<AE08> = 17;
...

みたいなのがずらっと並んでいるのでこの部分を変えます。

対応するキー名は、多分

  • テンキー「-」…<KPSU>
  • テンキー「+」…<KPAD>
  • 「< >」…<LSGT>
  • 「半角全角」…<TLDE>
  • 「\ |」…<AE13>
  • 「\ _」…<AB11>

のはずです。ファイルの下のほうを見れば、これらの4文字のコードみたいなやつに、実際に「[backslash, bar]」などといった割り当てが書いてあるので、そこから特定することもできます。あるいはxevコマンドで実際に文字を打ってみて特定することも可能です。

これらが特定できたら、数字を入れ替えます。例えば<KPSU> = 82と<TLDE> = 49になっているなら(これも普通はそうなっている気がする)<KPSU> = 49と<TLDE> = 82に書き換えます。

書き換えが終わったらさっきのコマンドで設定を適用しましょう。

これで、正しく目的のキーが入力されるようになっているはずです。

永続化(追記)

xkbcompでの設定は再起動するとリセットされます。

自分の環境では以下のようなスクリプトGUIログイン時(rc.localのようなところから普通に実行するとうまくいかないので注意)に実行するようにしました。sunshine.xkbは、今まで作成したようなxkbファイルの名前です。

#!/bin/sh
sleep 15
cd $HOME
SUNSHINE_KB_ID=$(xinput | grep "Keyboard passthrough" | sed 's/^.*id=\([0-9]*\).*/\1/')
xkbcomp -i $SUNSHINE_KB_ID sunshine.xkb $DISPLAY

まとめ

根本的な原理を半分くらいしか理解していないので、とりあえずこうやったら動きました的な報告になってしまいました。他OSやWaylandなどでどうやるのかちょっとわかりませんが、何か参考になれば幸いです。




以上の内容はhttps://turgenev.hatenablog.com/entry/2025/10/02/001505より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14