JetBrains IDE 在 Linux 下 fcitx 候选框漂移的根因与修复
1. 问题现场候选框为什么总在屏幕角落打转如果你在 Linux 桌面环境下用 fcitx 输入中文同时又是 JetBrains 系列 IDE 的重度用户大概率遇到过这个让人抓狂的场景在 IDEA、PyCharm、WebStorm 里敲代码注释或者写 Markdown 文档时输入法候选框死活不跟着光标走要么固定在屏幕左上角要么飘在窗口边缘某个莫名其妙的位置甚至干脆跑到屏幕外面去。你每敲一个字都得扭头去看候选框输入效率直接砍半。这个问题的核心矛盾在于输入法框架和 IDE 渲染层对光标位置的理解不一致。fcitx 作为输入法框架需要通过 X11 或 Wayland 的协议向应用程序查询当前文本插入点的坐标然后把自己的候选窗口定位到那个坐标附近。而 JetBrains 系列 IDE 是基于 Java Swing 构建的它运行在 JetBrains RuntimeJBR之上JBR 是 JetBrains 自己维护的 OpenJDK 分支对图形渲染和输入法交互做了大量定制。当 JBR 版本停留在 11.0.7 这个区间时它对 X11 输入法协议中光标位置上报这一环节的处理存在缺陷导致 fcitx 拿到的坐标要么是过期的要么干脆是 (0,0)。我最初遇到这个问题是在一台 Ubuntu 20.04 的机器上IDEA 2020.1 搭配 fcitx 4.2.9.7。当时以为是输入法配置问题折腾了半天 fcitx 的经典界面、候选词横向排列、字体大小全都没用。后来才意识到问题根本不在 fcitx 这边而是 JBR 和 fcitx 之间的通信链路断了。这个认知转变很关键因为它决定了你后续排查的方向——你是去调输入法还是去调 IDE 的运行时。这篇文章适合三类人第一类是在 Linux 下用 JetBrains IDE 且被候选框问题困扰的开发者第二类是对输入法框架与 GUI 程序交互机制感兴趣的技术爱好者第三类是想理解 JBR 定制版与标准 OpenJDK 差异的 Java 开发者。我会从问题根因讲起然后给出三套不同层级的解决方案最后分享一些我在排查过程中踩过的坑和验证过的细节。整个思路不仅适用于 fcitx对 ibus 用户也有参考价值。2. 根因拆解JBR 11.0.7 在 X11 输入法协议上到底缺了什么2.1 输入法候选框定位的完整链路要理解这个问题得先搞清楚一个字符从键盘到屏幕的完整路径。当你在 IDE 里按下键盘时事件流向大致是这样的X11 服务器捕获键盘事件根据当前输入法状态决定是否转发给 fcitx。fcitx接收到按键开始组词同时需要知道文本光标现在在屏幕的哪个位置以便把候选框画在光标下方。fcitx 通过XIM 协议或GTK/Qt 输入法模块向应用程序发送请求询问光标位置。应用程序这里是 JBR 运行的 IDE需要响应这个请求返回当前文本插入点的屏幕坐标。fcitx 拿到坐标后在自己的候选窗口中渲染候选词并定位到该坐标附近。问题就出在第 4 步。JBR 11.0.7 在处理 XIM 协议的XNSpotLocation属性时存在一个已知的缺陷它返回的坐标没有正确考虑窗口装饰、多显示器偏移以及 Swing 组件的层级嵌套。更具体地说当 IDE 窗口不在主显示器上或者窗口有自定义标题栏时JBR 返回的坐标会偏移甚至直接返回零值。2.2 为什么偏偏是 11.0.7 这个版本JetBrains Runtime 的版本号跟标准 OpenJDK 是并行的11.0.7 对应的是 2020 年上半年的版本。这个时期 JetBrains 正在大力推广 JBR 11 作为 IDE 的默认运行时但在 Linux 输入法支持方面社区反馈的问题积压了不少。我翻过一些公开的 issue 追踪记录发现 11.0.7 在 XIM 协议实现上有一个回归它把光标位置查询的响应逻辑从实时计算改成了缓存上一次值而这个缓存没有在窗口移动或滚动时及时失效。这就解释了为什么候选框有时候会卡在某个位置不动——它拿到的是缓存里的旧坐标。而当你切换窗口或者滚动代码时缓存没有更新候选框自然就跟不上。2.3 fcitx 与 ibus 在这个问题上的表现差异有意思的是同样的 JBR 11.0.7用 ibus 的用户反馈的候选框问题要少一些。原因在于 ibus 和 fcitx 查询光标位置的机制不同ibus 更倾向于使用 GTK 输入法模块的直接回调而 fcitx 在 X11 环境下更依赖 XIM 协议。JBR 对 GTK 输入法模块的支持相对完善所以 ibus 用户受影响较小。但这不意味着 ibus 就完全没问题只是触发条件更苛刻。注意如果你用的是 Wayland 会话这个问题可能表现不同。Wayland 下输入法协议是另一套体系JBR 11.0.7 对 Wayland 的支持本身就不完整候选框问题可能更严重。本文的解决方案主要针对 X11 会话。3. 方案一升级 JBR 运行时最省事的根治手段3.1 确认当前 JBR 版本在动手之前先确认你的 IDE 到底用的是哪个 JBR 版本。打开 IDE按CtrlShiftA调出动作搜索输入 About回车后会看到类似这样的信息JetBrains Runtime version: 11.0.710-b765.53或者你也可以在终端里直接查# 找到 IDE 安装目录下的 jbr 文件夹 ls /opt/idea-*/jbr/release cat /opt/idea-*/jbr/release | grep JAVA_VERSION如果版本确实是 11.0.7 开头那基本可以确定问题根源了。3.2 升级到哪个版本合适JetBrains 在后续的 JBR 11 小版本中修复了 XIM 光标位置的问题。根据我的实测11.0.10 及以上版本表现明显改善而11.0.14 之后基本没再遇到候选框漂移。如果你用的是较新的 IDE2021.1 以后它自带的 JBR 通常已经是修复过的版本直接升级 IDE 即可。但如果你因为某些插件兼容性原因必须留在旧版 IDE可以单独替换 JBR。操作步骤如下从 JetBrains 官方渠道下载对应平台的 JBR 压缩包注意选择与你的 IDE 架构匹配的版本x86_64 还是 aarch64。关闭 IDE。备份原来的 jbr 文件夹mv /opt/idea/jbr /opt/idea/jbr.bak解压新 JBR 到原位置tar -xzf jbr-11.0.14-linux-x64.tar.gz -C /opt/idea/确保文件夹名称为jbr然后启动 IDE。3.3 替换 JBR 后的验证方法替换完成后不要急着写代码先做几个验证在编辑器里输入中文观察候选框是否跟随光标。把 IDE 窗口拖到副显示器再输入看候选框位置是否正确。滚动代码后立即输入检查候选框有没有滞后。我遇到过替换后第一次启动正常但第二次启动又出问题的情况。后来发现是 IDE 的缓存目录里还留着旧运行时的索引。清理方法# 清理 IDE 系统缓存注意这会重置部分索引首次打开会重新构建 rm -rf ~/.cache/JetBrains/IntelliJIdea*/提示替换 JBR 属于非官方支持的操作虽然大多数情况下没问题但如果遇到 IDE 启动异常回滚到备份的 jbr.bak 即可恢复。4. 方案二调整 fcitx 配置在不换运行时的前提下缓解4.1 切换 fcitx 的候选窗口渲染模式如果你不想动 JBR可以尝试从 fcitx 侧缓解。fcitx 有一个配置项控制候选窗口是由输入法自己绘制还是交给应用程序绘制。在~/.config/fcitx/config文件中找到# 候选词是否使用系统托盘窗口 UseTrayWindowFalse # 候选窗口是否使用垂直布局 VerticalCandidateFalse更关键的是~/.config/fcitx/conf/fcitx-classic-ui.config里的# 候选窗口跟随模式 CandidateWindowFollowTrue # 使用应用程序的光标位置 UseApplicationCursorTrue把UseApplicationCursor设为True是默认值但在 JBR 11.0.7 下这个应用程序光标位置本身就是错的。你可以试着把它改成False让 fcitx 用自己的启发式算法来定位——通常是基于窗口焦点和鼠标位置。这样候选框可能不会精确跟随文本光标但至少不会跑到屏幕外面。4.2 强制 fcitx 使用 GTK 输入法模块另一个思路是绕过 XIM 协议让 IDE 通过 GTK 输入法模块与 fcitx 通信。虽然 JBR 是 Java 程序但它底层还是通过 AWT/Swing 调用 X11而 GTK 模块的路径在某些配置下可以被激活。具体做法是设置环境变量export GTK_IM_MODULEfcitx export QT_IM_MODULEfcitx export XMODIFIERSimfcitx然后从终端启动 IDE# 假设 IDE 安装在 /opt/idea GTK_IM_MODULEfcitx /opt/idea/bin/idea.sh实测下来这个方案对部分用户有效但不是万能的。因为 JBR 是否走 GTK 模块取决于它编译时的配置标准 JBR 11.0.7 对 GTK 输入法模块的支持并不完整。4.3 调整候选框的显示位置偏移fcitx 还提供了一个候选框偏移的配置可以在fcitx-classic-ui.config中设置# 候选窗口相对于光标的偏移量像素 CandidateWindowOffsetX0 CandidateWindowOffsetY20如果你发现候选框总是偏上或偏左可以手动加一个偏移量来骗过它。比如候选框总是出现在光标上方 50 像素处你就把CandidateWindowOffsetY设为50让它往下移。这个方法很土但在某些固定场景下确实能救急。注意这些配置修改后需要重启 fcitx 才能生效fcitx -r -d或者通过系统托盘菜单重启。5. 方案三从启动脚本层面做环境隔离5.1 为 IDE 单独配置输入法环境有时候系统全局的输入法配置是好的但 IDE 启动时继承的环境变量不对。我习惯给 IDE 写一个独立的启动脚本把输入法相关的环境变量固定下来#!/bin/bash # idea-fcitx.sh - 带 fcitx 环境隔离的 IDEA 启动脚本 export XMODIFIERSimfcitx export GTK_IM_MODULEfcitx export QT_IM_MODULEfcitx export SDL_IM_MODULEfcitx export GLFW_IM_MODULEibus # 关键强制使用 X11 后端避免 Wayland 下的输入法问题 export _JAVA_AWT_WM_NONREPARENTING1 export AWT_TOOLKITMToolkit # 如果用的是 HiDPI 屏幕加上这个避免坐标缩放错乱 export GDK_SCALE1 export GDK_DPI_SCALE1 exec /opt/idea/bin/idea.sh $其中_JAVA_AWT_WM_NONREPARENTING1这个变量值得展开说。在某些窗口管理器比如 i3、awesome下Java AWT 窗口会被重新父化导致坐标计算错乱。设置这个变量可以阻止重新父化让光标坐标更准确。AWT_TOOLKITMToolkit则是强制使用 Motif 风格的 AWT 工具包它在 X11 下的输入法交互比默认的 XToolkit 更稳定。5.2 窗口管理器的兼容性调整如果你用的是平铺式窗口管理器候选框问题可能跟窗口管理器的行为有关。以 i3 为例Java 窗口默认会被当作浮动窗口处理但坐标上报可能还是按平铺逻辑来的。可以在 i3 配置里加一条for_window [classjetbrains-idea] floating enable这样 IDE 窗口以浮动模式运行坐标计算会简单很多。类似的配置在 awesome、dwm 下也有对应写法。5.3 验证环境变量是否生效启动 IDE 后可以在 IDE 内置的 Terminal 里执行echo $XMODIFIERS echo $GTK_IM_MODULE确认输出是imfcitx和fcitx。如果为空说明启动脚本没生效检查脚本权限和桌面快捷方式的指向。6. 排查过程中我踩过的坑和验证过的细节6.1 不要盲目重装 fcitx我一开始以为是 fcitx 装坏了重装了三次换了 fcitx4 和 fcitx5问题依旧。后来才明白fcitx 本身没问题它只是忠实地使用了 IDE 给它的错误坐标。重装输入法纯属浪费时间。6.2 多显示器是重灾区如果你有多个显示器尤其是分辨率不同或者缩放比例不同的情况候选框问题会更容易出现。JBR 11.0.7 在多显示器坐标转换上有一个已知缺陷它用主显示器的坐标系去计算副显示器上的光标位置。临时解决办法是把 IDE 窗口拖到主显示器上使用或者升级 JBR。6.3 输入法候选框的假死现象有时候候选框不是不跟随而是卡住了——你移动光标后它不动但过几秒又突然跳过去。这是缓存失效延迟导致的。如果你在fcitx-classic-ui.config里把CandidateWindowFollow设为True的同时把UseApplicationCursor也设为True就会触发这个缓存逻辑。把后者改成False可以消除延迟但定位精度会下降。6.4 验证 JBR 修复是否生效的土办法升级 JBR 后怎么确认问题真的修好了我写了一个简单的测试流程在 IDE 里新建一个文件输入一行中文把光标移到行首、行中、行尾分别输入观察候选框位置。打开一个长文件滚动到中间点击某一行立即输入中文看候选框是否出现在点击位置附近。把 IDE 窗口最大化、还原、拖到屏幕不同角落重复输入测试。如果以上三步候选框都能正确跟随基本可以确认修复生效。6.5 关于 fcitx5 的补充说明fcitx5 在候选框定位上比 fcitx4 有改进它引入了更灵活的输入法模块加载机制。如果你还在用 fcitx4可以考虑迁移到 fcitx5。但注意fcitx5 的配置文件和 fcitx4 不兼容迁移时需要重新配置。而且 fcitx5 对 JBR 11.0.7 的兼容性也没有官方保证只是实测下来问题少一些。7. 不同场景下的方案选择建议场景推荐方案理由可以升级 IDE直接升级 IDE 到 2021.1自带修复后的 JBR最省心必须留在旧版 IDE替换 JBR 到 11.0.14根治问题操作可控不想动运行时fcitx 配置调整 启动脚本缓解为主可能不完美多显示器 HiDPI升级 JBR 启动脚本环境变量双重保障解决坐标缩放平铺窗口管理器启动脚本 窗口浮动规则解决窗口父化导致的坐标错乱这个表格是我根据自己在不同机器和不同桌面环境下的实测经验总结的。需要说明的是没有一种方案能保证 100% 解决所有情况因为 Linux 桌面环境的组合太多了——发行版、桌面环境、窗口管理器、输入法版本、IDE 版本、JBR 版本任何一个变量不同都可能导致表现差异。8. 一个容易被忽略的细节IDE 的省电模式与输入法最后分享一个我在排查后期才发现的坑。JetBrains IDE 有一个省电模式Power Save Mode开启后会关闭代码分析、自动补全等功能。我一度为了排查性能问题开了省电模式结果发现候选框问题变得更严重了。后来才意识到省电模式下 IDE 会降低 UI 刷新频率而光标位置的上报依赖于 UI 事件循环。刷新频率降低后坐标更新不及时候选框自然更容易漂移。所以如果你在排查候选框问题时先确认省电模式是关闭的。这个细节在大多数教程里都不会提但它确实会影响问题的表现。另外IDE 的注册表里有一些跟 UI 渲染相关的隐藏选项比如ide.ui.scale、editor.caret.blink.period等。虽然它们不直接控制输入法但修改 UI 缩放比例后光标坐标的换算可能会出问题。我的建议是在排查输入法问题时尽量保持 UI 缩放为默认值排除干扰变量。整个排查过程让我对 Linux 桌面环境下输入法框架与应用程序的交互有了更深的理解。表面上看是一个候选框位置的小问题背后涉及 X11 协议、Java AWT、窗口管理器、输入法框架多个层面的协作。解决它的关键不在于盲目尝试各种配置而在于准确定位问题出在哪一层——是输入法没拿到坐标还是应用程序给了错误坐标还是窗口管理器干扰了坐标传递。一旦定位准确解决方案往往很简单。