Mac の音声入力ソフトで口述した内容を、RDP セッション内のリモート PC へそのまま入力できるようにする。
- リモート側にソフトを入れない(Windows / Linux どちらの接続先でも、増えても、そのまま使える)
- Mac 側にも常駐アプリを増やさない(Karabiner-Elements + macOS 標準ツール + 自前ヘルパ 1 本で完結)
- 音声入力ツールは Aqua Voice で検証したが、「クリップボードに載せて合成 ⌘V を送る」方式のツール(SuperWhisper / Wispr Flow / MacWhisper など Whisper 系全般)なら同じ仕組みが有効なはず
そのままでは RDP セッションに向かって口述すると「v」が 1 文字入るだけで使い物にならない。本 Gist はその原因調査(CGEvent ダンプによる特定)と解決策一式。
- Windows App (旧 Microsoft Remote Desktop) のセッションにフォーカスした状態で音声入力すると、文字起こしの代わりに
vが 1 文字入るだけ - osascript (
System Events keystroke "v" using command down) で ⌘V を送っても同じくvになる - クリップボードにも文字起こしが残っていない
Aqua Voice は通常アプリには合成タイピングで直接挿入するが、RDP クライアントが相手のときだけ「クリップボードに文字起こしを載せて合成 ⌘V を送り、約0.8秒後にクリップボードを復元する」方式に切り替わる。復元が速いため、手動で ⌘V を押しても間に合わない。
実キーボードのイベントと合成イベントを CGEvent タップでダンプして比較した結果、Windows App が修飾キーを受理する条件は:
- 修飾キーが
keyDownではなくflagsChangedイベントとして届くこと - flags に 修飾キーのデバイス固有ビット(左⌘ =
0x8)と NonCoalesced (0x100) が含まれること- ⌘押下中:
0x20100108/ 解放後:0x20000100
- ⌘押下中:
単なる maskCommand (0x100000) だけの合成イベントは(HID tap / session tap / postToPid いずれでも)修飾が剥がされ、v 単体としてリモートへ転送される。Windows App は右⌘/左⌘を Windows キーへ区別してマップするため、デバイスビットの無い修飾を無視するのだと思われる。
新規常駐アプリなし。Karabiner-Elements + macOS 標準ツール + ローカルビルドの Swift 20行で構成。
右⌘ / F10 で口述 (Windows App フォーカス中のみ)
→ Karabiner の shell_command が aqua-rdp-relay を起動 (短命 watcher、右⌘=60秒 / F10=10分)
→ クリップボードの「変化 → 即復元」ペア署名を検出して文字起こしを捕獲
→ 混入した "v" を Backspace で消去 → クリップボード再セット → HID レベル ⌘V で貼り付け
トリガーは 2 モード:
- 右⌘ (push-to-talk): 1 チャンクごとに即貼り付け
- F10 (ハンズフリー): 1 度目で開始してチャンクを溜め、2 度目で全文を一括貼り付け(溜めた分の
vはまとめて消す)
aqua-sendkeys.swift— 上記 3 条件を満たす CGEvent を投げるミニヘルパaqua-rdp-relay— クリップボード監視 bash スクリプト(Karabiner から起動)karabiner-aqua-rdp-relay.json— Karabiner complex modification(右⌘ / F10 トリガー)
-
ヘルパをビルド(Xcode Command Line Tools が必要)
xcrun swiftc -O -o ~/bin/aqua-sendkeys aqua-sendkeys.swift -
aqua-rdp-relayを~/bin/に置いてchmod +x -
karabiner-aqua-rdp-relay.json内のYOUR_USERNAMEを自分のユーザー名に置換し、~/.config/karabiner/assets/complex_modifications/へ配置 → Karabiner-Elements の Complex Modifications で 2 ルールを有効化 -
アクセシビリティ権限: システム設定 → プライバシーとセキュリティ → アクセシビリティに以下を追加
/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_console_user_server -
Windows App 側でクリップボードリダイレクトを有効化(Devices & Audio → Clipboard: Bidirectional)
AQUA_SYNC_WAIT(秒、既定 0.1)で RDP へのクリップボード同期待ちを調整できる。短いほど
貼り付けが速いが、音声入力ツールの壊れた ⌘V が入れた v の到着を backspace が追い越して
消し残すことがある(0.1 秒で 4 回中 1 回程度)。気になる場合は 0.25〜0.5 秒へ上げる。
なお v が一瞬表示されること自体は避けられない。あれは音声入力ツールが RDP へ直接
送っているキー入力で、Karabiner からは見えない合成イベントのため事前に抑止できない。
本スクリプトにできるのは「速やかに消す」ことだけ。
shell_command 経由だと
CGEventSource.flagsState が常に maskCommand を報告し続け(0x20100000)、しかも 1 回の
呼び出しに ~100ms かかるため、待ちループが必ず上限まで回って 1〜5 秒の遅延になる。
- トリガーキー(右⌘ / F10)は Aqua Voice の起動キー設定に合わせて JSON を書き換える
- リモートが Linux の場合は xrdp のクリップボードリダイレクト (chansrv) が動いていること
- RDP のクリップボード同期 (CLIPRDR) 自体が時々沈黙する持病がある。効かなくなったらリモート側で
rdpclip.exeを再起動(Linux はxrdp-chansrv)するか、Windows App を再起動 - デバッグは
/tmp/aqua-rdp-relay.logを見る
- macOS (Apple Silicon) / Windows App v11 系 / Aqua Voice v0.18.8
- リモート: Windows 11 / Linux (xrdp) — 2026-08-03 検証