阅读视图

发现新文章,点击刷新页面。
🔲 ☆

fcitx 启动后键盘输入卡顿的排查

* 背景 =fcitx= 是 Linux 下常用的输入法框架,用于在英文环境中输入中文、日文、韩文等语言。它的工作方式是"拦截"应用程序的键盘输入,在用户敲拼音时弹出候选词窗口,选中后把中文字符提交给应用程序。 这个"拦截"机制意味着 =fcitx= 会插入到键盘事件的传递链路中。如果这条链路的某个环节出了问题,键盘输入就会受影响——而我遇到的就是这种情况。 * 故障现象 某天,我发现启动 =fcitx= 后,整个键盘输入变得 *非常卡* ——敲一个键要等半秒甚至更久才有反应。但鼠标完全正常,丝毫不受影响。不启动 =fcitx= 就一切如常。 键盘卡而鼠标没事,这个现象很诡异。直觉告诉我:一定是 =fcitx= 做了什么只影响键盘、不影响鼠标的事情。 * 排查过程 ** 第一步:收集基础信息 先搞清楚环境。我的系统是 Arch Linux,窗口管理器是 =awesome= ,显示服务器是 =Xorg= , =fcitx= 版本是 4.2.9.9(fcitx4)。 #+begin_src shell uname -r # 6.19.12-arch1-1 fcitx --version # fcitx version: 4.2.9.9 #+end_src 然后运行 =fcitx= 自带的诊断工具: #+begin_src shell DISPLAY=:0 fcitx-diagnose #+end_src 诊断结果暴露了大量问题,下面列出关键部分并逐一说明。 *** Fcitx 状态:DBus 通信断裂 #+begin_example # Fcitx State: 4. `fcitx-remote`: **Cannot connect to fcitx correctly.** 5. DBus interface: **Cannot find DBus name `org.fcitx.Fcitx` owner.** **Cannot find pid of DBus name `org.fcitx.Fcitx` owner.** #+end_example =fcitx-remote= 无法连接 =fcitx= ,DBus 总线上也找不到 =org.fcitx.Fcitx= 这个服务名。这意味着 =fcitx= 虽然进程在跑,但它 *没有成功注册到会话总线* 。 #+BEGIN_QUOTE *小知识:什么是 DBus?* DBus 是 Linux 桌面环境中的 *进程间通信* (IPC)机制。你可以把它想象成一个"内部公告栏":各个程序在上面发布自己的服务,其他程序通过公告栏找到并调用这些服务。 对 =fcitx= 来说,DBus 的作用包括: - 输入法切换通知(从中文切到英文) - 输入法状态查询(当前是中文还是英文模式?) - 配置变更同步(修改了快捷键后立即生效) 如果 DBus 通信断了,这些操作要么失败,要么阻塞等待超时。 #+END_QUOTE 更进一步,检查进程列表发现 =fcitx= 启动了一个 *私有的 dbus-daemon= : #+begin_example lujun9972 27481 /usr/bin/dbus-daemon --syslog --fork ... \ --config-file /usr/share/fcitx/dbus/daemon.conf #+end_example 正常情况下, =fcitx= 应该使用桌面会话的 DBus( =/run/user/1000/bus= ),但它退而求其次启动了自己的私有 DBus 实例。这说明 =fcitx= 启动时会话总线不可用(或者地址不对)。私有总线意味着其他应用无法通过标准 DBus 接口与 =fcitx= 通信,输入法切换、状态同步等功能全部受影响。 *** 前端模块:环境变量缺失 #+begin_example # Frontends setup: ## Xim: 1. `${XMODIFIERS}`: **XMODIFIERS is not set** ## Qt: 1. qt4 - `${QT4_IM_MODULE}`: **Please set environment variable QT4_IM_MODULE ...** 2. qt5 - `${QT_IM_MODULE}`: **Please set environment variable QT_IM_MODULE ...** ## Gtk: 1. gtk - `${GTK_IM_MODULE}`: **Please set environment variable GTK_IM_MODULE ...** #+end_example 三个关键环境变量( =XMODIFIERS= 、 =QT_IM_MODULE= 、 =GTK_IM_MODULE= )全部未设置。不过这一条有 *误导性* ——我检查了 =~/.xinitrc= ,里面确实有设置这些变量,只是 =fcitx-diagnose= 运行所在的 shell 环境没继承到 X 会话的环境变量。所以这个问题的严重程度需要打个问号。 #+BEGIN_QUOTE *小知识:为什么需要三个环境变量?* - =XMODIFIERS=@im=fcitx= :告诉 *所有 X11 应用* 通过 XIM 协议连接输入法服务器。这是最底层的兼容方案,几乎所有 X 应用都支持。 - =GTK_IM_MODULE=fcitx= :让 *GTK 应用* 使用专门的 fcitx IM 模块,比 XIM 性能更好、体验更流畅(支持光标跟随、预编辑等)。 - =QT_IM_MODULE=fcitx= :同理,让 *Qt 应用* 使用专门的 fcitx IM 模块。 三个变量的作用范围不同但互补: =XMODIFIERS= 是兜底方案, =GTK_IM_MODULE= 和 =QT_IM_MODULE= 是针对特定框架的优化方案。 #+END_QUOTE *** crash.log:BadWindow 错误 #+begin_example 3. `~/.config/fcitx/log/crash.log`: fcitx: BadWindow (invalid Window parameter) #+end_example =crash.log= 中记录了一次 =BadWindow= 错误。这是 X11 协议中的经典错误,表示 =fcitx= 尝试操作一个不存在或已销毁的窗口。这通常发生在窗口关闭时 =fcitx= 还没来得及清理对该窗口的引用。虽然未必直接导致卡顿,但说明 =fcitx= 的窗口管理与 X 服务器的状态存在不同步。 *** 配置工具:GTK2 模块缺失 #+begin_example # Fcitx Configure UI: 2. Config GUI for gtk2: **Config GUI for gtk2 not found.** ... ## Gtk: 1. gtk - gtk-query-immodules: **Failed to find fcitx in the output of `/usr/bin/gtk-query-immodules-2.0`** **Cannot find fcitx im module for gtk 2.** #+end_example GTK2 的 fcitx 输入法模块缺失。这在 2026 年影响不大——GTK2 应用已经很少见了,而且如果 =XMODIFIERS= 设置正确,GTK2 应用仍可通过 XIM 协议使用输入法。 *** Locale 警告 #+begin_example 3. XIM for Emacs: **Your LC_CTYPE is set to en_US.UTF-8 instead of one of zh, ja, ko. You may not be able to use input method in emacs because of an really old emacs bug that upstream refuse to fix for years.** #+end_example 这是一个 Emacs 的古老 bug :当 =LC_CTYPE= 不是中日韩 locale 时,Emacs 的 XIM 输入可能不工作。对我来说影响不大,因为我用的是 Emacs 内置的输入法支持,不走 XIM 。 ** 第二步:发现键盘设备被禁用 =fcitx-diagnose= 虽然暴露了很多问题,但没有一个能直接解释"为什么键盘卡"。我需要换个角度思考: - *键盘卡,鼠标没事* → 两种设备的处理路径不同 - 在 X11 中,键盘和鼠标是 *独立的设备* ,各有各的事件传递管道 - 如果 =fcitx= 只干扰了键盘的管道而没有干扰鼠标的,那问题一定出在键盘设备的 *X 层面配置* 上 所以我用 =xinput= (X Input 扩展的命令行工具)来检查 X11 是怎么看待键盘设备的: #+begin_src shell DISPLAY=:0 xinput list-props "AT Translated Set 2 keyboard" #+end_src 输出中有一行: #+begin_example Device Enabled (176): 0 #+end_example =Device Enabled= 是 *0* !物理键盘在 X 层面被 *禁用* 了! 再看设备列表: #+begin_src shell DISPLAY=:0 xinput list #+end_src #+begin_example ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ∼ AT Translated Set 2 keyboard id=10 [floating slave] ∼ ThinkPad Extra Buttons id=13 [floating slave] ... #+end_example 物理键盘前面的符号是 =~= (tilde),表示它是 *floating slave* ——一个浮动的、没有挂载到任何 master 设备上的从设备。正常情况下,物理键盘应该是 =↳= (attached slave),挂载在 master keyboard 下面。 #+BEGIN_QUOTE *小知识:X11 的输入设备层级* X11 的输入设备分为三层: 1. *Master 设备* :虚拟的核心设备(master keyboard + master pointer),是应用程序看到的"键盘"和"鼠标" 2. *Slave 设备* :物理设备,attach 到 master 上,通过 master 将事件传递给应用程序 3. *Floating slave* :物理设备但 *没有* attach 到任何 master,事件走另一条路径 当物理键盘变成 floating slave 且被禁用时,键盘事件只能通过更慢的回退路径(如 XIM 或 XKB 扩展)传递,这就是输入卡顿的原因。 #+END_QUOTE ** 第三步:临时修复——重新启用键盘 我尝试了两个操作: #+begin_src shell # 将键盘重新挂载到 master keyboard DISPLAY=:0 xinput reattach 10 3 # 重新启用键盘设备 DISPLAY=:0 xinput enable "AT Translated Set 2 keyboard" #+end_src 执行后确认: #+begin_example Device Enabled (176): 1 #+end_example 键盘恢复了正常。这证实了: *键盘被禁用和分离就是卡顿的直接原因* 。 ** 第四步:定位根因——fcitx-xkb 插件 为什么键盘会被禁用?回到 =fcitx-diagnose= 的输出,我注意到 =fcitx= 加载了大量插件: #+begin_example Found 36 enabled addons: fcitx-xkb fcitx-xkbdbus fcitx-x11 fcitx-xim ... #+end_example 其中 =fcitx-xkb= 和 =fcitx-xkbdbus= 这两个插件引起了我的注意。 =fcitx-xkb= 的功能是接管 X 的键盘布局管理(XKB),当它启动时会: 1. 接管 XKB 布局控制权 2. 通过自己的管道处理键盘事件 它是怎么把物理键盘搞成 disabled + floating 的?我没有在源码中逐行确认,但推理链条很清楚: - =fcitx-xkb= 启动前:键盘正常( =Device Enabled = 1= ,attached slave ) - =fcitx-xkb= 启动后:键盘被禁用且分离 - 停止 =fcitx= 后手动 =xinput enable= + =reattach= :键盘恢复正常 - 鼠标设备完全不受影响( =fcitx-xkb= 只管键盘布局,不管鼠标) 三个事实指向同一个结论: *=fcitx-xkb= 在接管键盘布局时,错误地禁用并分离了物理键盘设备* 。 而鼠标走的是完全独立的 XInput 路径,不受 XKB 插件影响。这完美解释了" *键盘卡而鼠标没事* "的现象。 同时,诊断还发现了 DBus 通信问题: #+begin_example Cannot connect to fcitx correctly. Cannot find DBus name org.fcitx.Fcitx owner. #+end_example =fcitx-remote= 也无法连接 =fcitx= ,说明 DBus 通信链路是断的。 =fcitx-xkbdbus= 插件需要通过 DBus 传递键盘状态,如果 DBus 通信断了,每次按键都可能在等待一个永远不会到来的 DBus 响应,进一步加剧延迟。 *** 根因链条 #+begin_example fcitx4 启动 → fcitx-xkb 插件接管 X 键盘布局 → 禁用/分离物理键盘设备 → 键盘事件走慢速回退路径 → 再叠加 DBus 通信断裂 → 每次按键都卡 #+end_example * 解决方案 我选择了升级到 =fcitx5= ,因为 =fcitx4= 太老了(4.2.9.9),它的 XKB 集成有 bug。 =fcitx5= 已经完全重写了 XKB 管理模块,不会有这个问题。 不过,如果你暂时不想升级,也有两个轻量的替代方案: 1. *禁用 fcitx-xkb 插件* :在 =~/.config/fcitx/conf/= 中创建 =fcitx-xkb.conf= ,设置 =Enabled=False= ,然后重启 =fcitx= 。代价是失去通过 =fcitx= 管理键盘布局的能力(如果你只用英文布局,无影响)。 2. *在 .xinitrc 中加修复命令* :在 =fcitx= 启动之后加一行 =xinput enable "AT Translated Set 2 keyboard"= ,强制重新启用键盘。 ** 升级到 fcitx5 #+begin_src shell # 移除 fcitx4 所有包 sudo pacman -Rns fcitx fcitx-configtool fcitx-googlepinyin \ fcitx-mozc fcitx-qt4 fcitx-qt5 fcitx-qt6 fcitx-rime \ fcitx-sogoupinyin fcitx-sunpinyin #+end_src #+begin_src shell # 安装 fcitx5 全家桶 sudo pacman -S fcitx5 fcitx5-configtool fcitx5-gtk fcitx5-qt \ fcitx5-chinese-addons fcitx5-mozc fcitx5-rime fcitx5-pinyin-zhwiki #+end_src ** 更新环境变量 =fcitx4= 和 =fcitx5= 的环境变量值不同,需要修改 =~/.xinitrc= : #+begin_example # 旧的 (fcitx4) export GTK_IM_MODULE=fcitx export QT_IM_MODULE=fcitx export XMODIFIERS="@im=fcitx" # 新的 (fcitx5) export GTK_IM_MODULE=fcitx5 export QT_IM_MODULE=fcitx5 export XMODIFIERS="@im=fcitx5" #+end_example #+BEGIN_QUOTE *小知识:为什么环境变量值要改?* =GTK_IM_MODULE= 和 =QT_IM_MODULE= 告诉 GTK/Qt 应用加载哪个输入法模块。 =fcitx= 和 =fcitx5= 是两套独立的 IM 模块共享库,名字不同。如果环境变量还指向 =fcitx= ,应用会尝试加载一个已经不存在的 =im-fcitx.so= ,结果就是找不到输入法。 #+END_QUOTE 升级后重新登录 X 会话,一切正常,键盘输入丝般顺滑。 * 输入法对照表 升级过程中,输入法引擎也需要对应替换: | fcitx4 包 | fcitx5 包 | 说明 | |-----------------------+--------------------------+------------------| | =fcitx= | =fcitx5= | 核心框架 | | =fcitx-configtool= | =fcitx5-configtool= | 图形配置工具 | | =fcitx-sunpinyin= | =fcitx5-chinese-addons= | 拼音(内置更优) | | =fcitx-sogoupinyin= | =fcitx5-chinese-addons= | 拼音(内置更优) | | =fcitx-googlepinyin= | =fcitx5-chinese-addons= | 拼音(内置更优) | | =fcitx-mozc= | =fcitx5-mozc= | 日文输入 | | =fcitx-rime= | =fcitx5-rime= | Rime 输入 | | =fcitx-qt5/6= | =fcitx5-qt= | Qt IM 模块 | | (无) | =fcitx5-gtk= | GTK IM 模块 | | (无) | =fcitx5-pinyin-zhwiki= | 维基百科词库 | 值得一提的是, =fcitx-sogoupinyin= (搜狗拼音)没有 fcitx5 版本,但 =fcitx5-chinese-addons= 内置的拼音引擎带云拼音功能,体验不逊于搜狗。 * 复盘 ** 排查思路 这次排查的关键转折点是用 =xinput list-props= 检查物理键盘的状态。如果只停留在" =fcitx= 导致卡顿"的表面,可能会一直猜测是 =fcitx= 性能问题,而不会发现真正的原因是 =X11= 层面的设备被禁用了。 整个排查过程遵循了一个核心原则: *先找根因,再动手修* 。 #+begin_example 现象:键盘输入卡顿 │ ├─ 1. fcitx-diagnose 收集信息 │ → 发现 DBus 断裂、环境变量缺失等多个异常 │ ├─ 2. xinput list-props 检查键盘设备 │ → Device Enabled = 0,键盘被禁用! │ ├─ 3. xinput list 看设备拓扑 │ → 物理键盘是 floating slave,未挂载到 master │ ├─ 4. xinput enable + reattach 临时修复 │ → 卡顿消失,确认根因 │ └─ 5. 定位 fcitx-xkb 插件为罪魁祸首 → 升级到 fcitx5 彻底解决 #+end_example ** 经验教训 1. *=xinput= 是排查 X11 键盘/鼠标问题的利器* : =xinput list= 看设备拓扑, =xinput list-props= 看设备属性, =xinput enable/disable= 控制设备开关 2. *输入法问题不一定在输入法层面* :这次表面是 =fcitx= 的锅,实际上是 =fcitx= 的 XKB 插件把 X11 的设备搞乱了,根因在 X 输入子系统 3. *老软件就该升级* : =fcitx4= 已经停止维护, =fcitx5= 不仅修复了这类 bug ,架构也更现代。在 Arch Linux 这种滚动更新的发行版上,抱着旧版本不放只会积累技术债
❌