fcitx5 下 Emacs 无法切换输入法的排查
目录
背景
我之前从 fcitx4 升级到了 fcitx5,解决了键盘输入卡顿的问题(详见fcitx 启动后键盘输入卡顿的排查)。升级后,普通应用(浏览器、终端等)都能正常切换输入法,唯独 Emacs 不行——按切换快捷键毫无反应,只能输入英文。
故障现象
- fcitx5 在其他应用(Firefox、终端等)中正常工作
- 在 Emacs 中无法切换到中文输入法
- Emacs 是通过
systemd user service以 daemon 模式启动的 - 系统为 Arch Linux,Emacs 版本 31.0.50,使用 Lucid(Xaw)工具包
排查过程
第一步:检查环境变量
输入法框架依赖三个关键环境变量来告诉应用程序"该用哪个输入法"。我先检查了当前 shell 中的值:
echo "GTK_IM_MODULE=$GTK_IM_MODULE" echo "QT_IM_MODULE=$QT_IM_MODULE" echo "XMODIFIERS=$XMODIFIERS"
GTK_IM_MODULE=fcitx5 QT_IM_MODULE=fcitx5 XMODIFIERS=@im=fcitx5
三个变量都有值,看起来没问题。
小知识:这三个环境变量是干什么的?
Linux 下的应用程序通过三种不同的协议连接输入法:
XMODIFIERS=@im=xxx:告诉 所有 X11 应用 ,通过 XIM 协议连接名为xxx的输入法服务器。这是最古老的方案,几乎所有 X 应用都支持,但功能最基础。GTK_IM_MODULE=xxx:让 GTK 应用 加载专门的输入法模块,比 XIM 体验更好(支持光标跟随、预编辑文字等)。QT_IM_MODULE=xxx:同理,让 Qt 应用 加载专门的输入法模块。简单来说:=XMODIFIERS= 是万能兜底方案,=GTK_IM_MODULE= 和
QT_IM_MODULE是各自框架的增强方案。
第二步:运行 fcitx5-diagnose
fcitx5 自带了一个诊断工具,可以自动检查各种常见配置问题:
fcitx5-diagnose
诊断结果里出现了多处警告:
环境变量 XMODIFIERS 的值被设为了"@im=fcitx5"而不是"@im=fcitx"。 请检查您是否在某个初始化文件中错误的设置了它的值。 环境变量 GTK_IM_MODULE 的值被设为了"fcitx5"而不是"fcitx"。 环境变量 QT_IM_MODULE 的值被设为了"fcitx5"而不是"fcitx"。
诊断工具建议把所有值从 fcitx5 改成 fcitx 。看起来很合理——虽然软件叫 fcitx5 ,但输入法模块注册的名字可能不带版本号。
于是我按建议修改了 ~/.xinitrc :
# 修改前 export GTK_IM_MODULE=fcitx5 export QT_IM_MODULE=fcitx5 export XMODIFIERS="@im=fcitx5" # 修改后 export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx export XMODIFIERS="@im=fcitx"
但问题没有解决——Emacs 还是无法切换输入法。
小知识:=.xinitrc= 是什么?
当你用
startx命令启动图形界面时,=X 服务器= 会读取~/.xinitrc这个文件,执行里面的命令来初始化桌面环境。通常在里面设置环境变量、启动输入法、启动窗口管理器等。
第三步:发现 Emacs 是 systemd 服务启动的
我意识到一个关键问题:我的 Emacs 不是从 .xinitrc 启动的,而是通过 systemd user service 以 daemon 模式在后台运行的。
小知识:Emacs daemon 模式
Emacs 可以以"守护进程"方式运行(=emacs --fg-daemon= 或
emacs --daemon=),在后台常驻。之后用 =emacsclient连接到这个后台进程来打开窗口。好处是:
- 启动速度极快(不用每次都重新加载配置)
- 多个客户端共享同一个 Emacs 进程(状态、缓冲区等)
这意味着 Emacs 的环境变量 不来自 .xinitrc ,而是来自 systemd service 文件中的 Environment 配置。查看服务文件:
cat ~/.config/systemd/user/emacs.service
[Service] Type=simple ExecStart=/usr/bin/emacs --fg-daemon Environment=... GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS="@im=fcitx" ...
果然!Emacs 的环境变量是独立的:
GTK_IM_MODULE=fcitxQT_IM_MODULE=fcitxXMODIFIERS"@im=fcitx"
看起来都是正确的
第四步:发现根因——XIM 服务器名称不匹配
既然 .xinitrc 和 emacs.service 都设了 XMODIFIERS ,那问题出在哪里?我检查了 fcitx5 实际注册到 X 服务器的 XIM 服务名称:
xprop -root XIM_SERVERS
XIM_SERVERS(ATOM) = @server=fcitx5
然后检查 Emacs 进程实际的 XMODIFIERS 值:
cat /proc/$(pgrep -x emacs)/environ | tr '\0' '\n' | grep XMODIFIERS
XMODIFIERS=@im=fcitx
找到根因了!
- fcitx5 注册的 XIM 服务器名:
@server=fcitx5 - Emacs 的
XMODIFIERS值:@im=fcitx
两者不匹配!XIM 协议要求 XMODIFIERS 的值必须和 XIM 服务器的注册名一致。Emacs 拿着 @im=fcitx 去找名为 fcitx 的 XIM 服务器,但实际注册的名字是 fcitx5 ,当然找不到。
小知识:XIM 协议的连接机制
XIM(X Input Method)是 X11 的输入法协议,工作流程是:
- 输入法框架(如 fcitx5)启动后,在 X 服务器上注册一个 XIM 服务器,名字形如
@server=fcitx5- 应用程序读取
XMODIFIERS环境变量(如 =@im=fcitx5=)- 应用程序拿着这个名字去 X 服务器上查找对应的 XIM 服务器
- 找到后建立连接,输入法就可以工作了
如果
XMODIFIERS的名字和 XIM 服务器注册的名字对不上,应用程序就找不到输入法服务器,输入法自然无法使用。
这也说明 fcitx5-diagnose 的建议是 误导 的:它建议把 XMODIFIERS 改成 @im=fcitx ,但 fcitx5 的 XIM 服务器偏偏注册为 fcitx5 。对于通过 XIM 协议连接输入法的应用(比如使用 Lucid 工具包的 Emacs),这个建议反而是错的。
第五步:确认 Emacs 走的是 XIM 而非 GTK 模块
为了确认我的 Emacs 确实走 XIM 协议,我检查了 Emacs 的构建特性:
emacs --batch --eval '(princ (format "%s\n" system-configuration-features))'
... X11 XDBE XIM XINPUT2 ... LUCID ...
关键信息:
XIM——Emacs 支持 XIM 协议LUCID——Emacs 使用 Lucid(Xaw)工具包,不是 GTK
Lucid 工具包不使用 GTK_IM_MODULE ,只走 XIM 协议。所以对 Emacs 来说,唯一重要的环境变量就是 XMODIFIERS ,而且它的值必须和 XIM 服务器注册名一致。
解决方案
明确了根因后,修复很简单:
修改 emacs.service 中的 XMODIFIERS
将 XMODIFIERS"@im=fcitx"= 改为 XMODIFIERS"@im=fcitx5"= :
# 修改前 Environment=... XMODIFIERS="@im=fcitx" ... # 修改后 Environment=... XMODIFIERS="@im=fcitx5" ...
重启 Emacs 服务
systemctl --user daemon-reload systemctl --user restart emacs
修改后确认 Emacs 进程的环境变量已更新:
cat /proc/$(pgrep -x emacs)/environ | tr '\0' '\n' | grep XMODIFIERS # XMODIFIERS=@im=fcitx5
重启后 Emacs 中顺利切换到中文输入法,问题解决。
恢复 .xinitrc
由于我的桌面环境也使用 fcitx5 ,且 fcitx5 的 XIM 服务器注册名为 fcitx5 ,所以 ~/.xinitrc 中的值保持为 fcitx5 是正确的:
export GTK_IM_MODULE=fcitx5 export QT_IM_MODULE=fcitx5 export XMODIFIERS="@im=fcitx5"
复盘
根因链条
fcitx5 注册 XIM 服务器名为 "@server=fcitx5"
→ emacs.service 中 XMODIFIERS="@im=fcitx"
→ 名称不匹配
→ Emacs 通过 XIM 找不到输入法服务器
→ 无法切换输入法
走过的弯路
这次排查最大的弯路是 轻信了 fcitx5-diagnose 的建议 。它建议把所有环境变量从 fcitx5 改成 fcitx ,我照做了,但这反而让 XMODIFIERS 和 XIM 服务器名不匹配的问题更加恶化了。
关键经验
xprop -root XIM_SERVERS可以查看 XIM 服务器注册名 :排查 XIM 问题时,先确认服务器叫什么名字,再和XMODIFIERS对比- systemd 服务的环境变量是独立的 :不继承
.xinitrc或 shell 的环境,需要在 service 文件中单独设置。遇到服务启动的程序出问题时,用cat /proc/$PID/environ | tr '\0' '\n' | grep XXX检查实际的环境变量 - 诊断工具的建议不一定全对 :=fcitx5-diagnose= 的建议在大部分场景下是对的,但对于 XIM 这种特殊场景会给出错误建议。理解原理比盲目执行建议更重要
- Emacs 的工具包决定了它用什么输入法协议 :Lucid 走 XIM(看
XMODIFIERS),GTK 走 GTK IM Module(看GTK_IM_MODULE)。排查前先搞清楚 Emacs 用的什么工具包