前言
最近我在開發 XVoice——一個跨平台的語音輸入系統。這個專案的核心功能很簡單:用麥克風錄音,透過 Whisper 辨識成文字,然後自動輸入到當前視窗。在 Windows 上一切順利,但當我切換到 Linux 的 KDE Plasma Wayland 環境時,噩夢開始了。
問題浮現
語音辨識的部分運作正常,Whisper 成功地把我說的「你好」轉換成文字。但是,文字就是打不出來。終端機顯示「已輸入: 你好」,可是目標視窗什麼都沒有。
一開始我以為是程式碼的問題,花了不少時間 debug,後來才發現——這是 Wayland 的「特色」。
Wayland 的安全模型:福還是禍?
Wayland 為了安全性,移除了 X11 時代大部分的輸入模擬 API。在 X11 上,xdotool 可以輕鬆模擬鍵盤輸入,但 Wayland 認為這是安全漏洞——惡意程式可能會偷偷打字或竊取輸入。
理念很好,但對我這種需要模擬輸入的開發者來說,簡直是災難。
嘗試一:wtype
我首先嘗試了 wtype,這是專門為 Wayland 設計的輸入工具:
wtype "測試"
結果:
Compositor does not support the virtual keyboard protocol
原來 wtype 依賴 virtual-keyboard 協議,但 KDE 的 KWin 不支援這個協議。GG。
嘗試二:ydotool
接著我試了 ydotool,它透過 Linux 核心的 uinput 模擬輸入裝置:
sudo usermod -aG input $USER # 加入 input 群組
# 重新登入
ydotool type "測試"
結果:指令執行了,但中文字沒出現。後來查到 GitHub Issue 才知道,ydotool 只能模擬鍵盤按鍵,無法直接輸入 Unicode 字元。它會把「測」拆成鍵盤按鍵,但中文根本不在鍵盤上啊!
嘗試三:dotool
dotool 是另一個類似的工具:
echo "type 測試中文" | dotool
結果:
dotool: WARNING: impossible character for layout: 測
dotool: WARNING: impossible character for layout: 試
一樣的問題——這些工具都是在鍵盤層級運作,不是字元層級。
嘗試四:剪貼簿模式
既然直接輸入不行,那就用剪貼簿吧:
echo "你好" | wl-copy # 複製到剪貼簿
echo "key ctrl+v" | dotool # 模擬 Ctrl+V
wl-copy 成功了,但 dotool 的 Ctrl+V 似乎沒有效果。我懷疑是 KDE 的問題,但沒有深究。
這個方案勉強可用——文字會複製到剪貼簿,使用者手動按 Ctrl+V 貼上。但這不是我想要的體驗。
深入研究:Fcitx5 D-Bus
我開始研究 Fcitx5 輸入法的 D-Bus 介面,想說或許可以直接透過輸入法提交文字:
busctl --user introspect org.fcitx.Fcitx5 /controller
結果發現 Fcitx5 的標準 D-Bus 介面沒有 CommitString 方法。有人為舊版 Fcitx 寫過 fcitx-dbus-commit-string 插件,但 Fcitx5 沒有對應的版本。
轉機:發現 KWtype
就在我快要放棄的時候,搜尋到了 KWtype——一個專門為 KDE Wayland 設計的虛擬鍵盤工具。
它使用 KWin 的 Fake Input 協議,這是 KDE 專屬的特權協議。編譯安裝:
git clone https://github.com/Sporif/KWtype.git
cd KWtype
meson setup --buildtype=release build
sudo ninja -C build install
測試:
kwtype "測試中文輸入"
成功了!文字直接出現在游標位置,包括中文!
最終解決方案
我更新了 XVoice 的 Wayland 輸出模組,優先使用 KWtype:
def _detect_tool(self) -> str | None:
# 優先使用 kwtype(KDE Wayland 專用,支援中文)
if self._kwtype_path:
return "kwtype"
# 備選方案...
if self._ydotool_path:
return "ydotool"
if self._wtype_path:
return "wtype"
return None
對於中文輸入:
def type_text(self, text: str) -> None:
# kwtype 支援中文,優先使用
if self._tool == "kwtype":
subprocess.run([self._kwtype_path, text], check=True)
return
# 其他工具不支援中文,使用剪貼簿模式
has_non_ascii = any(ord(c) > 127 for c in text)
if has_non_ascii:
self.type_with_clipboard(text)
return
心得總結
這次偵錯經歷讓我深刻體會到:
Wayland 生態還不成熟:雖然 Wayland 的安全模型很好,但配套工具還跟不上。不同桌面環境各自為政,沒有統一的輸入模擬方案。
KDE 有自己的路:KDE 不支援
virtual-keyboard協議,但有自己的 Fake Input。這種分裂讓跨平台開發很痛苦。中文輸入是另一個維度:大部分輸入模擬工具只考慮英文鍵盤,對於 CJK 字元完全無能為力。
小眾工具是救星:KWtype 這種小眾專案往往能解決大問題,感謝開放原始碼社群。
記錄很重要:我把整個調查過程記錄在
docs/WAYLAND_INPUT_ISSUE.md,下次遇到類似問題可以快速回顧。
工具總結
| 工具 | 平台 | 中文支援 | KDE 支援 | 備註 |
|---|---|---|---|---|
| xdotool | X11 | ✓ | N/A | X11 限定 |
| wtype | Wayland | ✓ | ✗ | 需要 virtual-keyboard 協議 |
| ydotool | 通用 | ✗ | ✓ | 只支援鍵盤按鍵 |
| dotool | 通用 | ✗ | ✓ | 同上 |
| KWtype | KDE Wayland | ✓ | ✓ | KDE 專用,推薦 |
後記
如果你也在 KDE Wayland 上開發需要模擬輸入的應用,希望這篇文章能幫你少走一些彎路。記住:先找 KWtype!
專案連結:XVoice on GitHub
寫於解決問題的那個深夜,帶著如釋重負的心情。