修复 Shift-Tab 映射失败问题(Fcitx5-Rime / Noctalia 为例)
起因
最近我遇到两个问题:fcitx5-rime 里和 noctalia 的 launcher 按 Shift+Tab 候选不上翻。 这两问题的共同点都在 Shift+Tab 这一个组合键上。我先怀疑是不是两个软件各自有 bug,但又觉得这俩可能是同一个原因。于是从 rime 下手,借助 AI 帮我 Debug。
从 rime 下手排查
先复现现象:
- 按
Tab:候选下翻正常。 - 按
Shift+Tab:候选不上翻。如果是在 Kitty 里,那么它会吃掉这个按键,执行对应操作。 default.custom.yaml里key_binder/selector都写了Shift+Tab的绑定。
排查核心过程的几步:
- 跑
wev然后按Shift+Tab,可以看到输出的 keysym 是ISO_Left_Tab(65056),修饰位 Shift。 - fcitx5 的
key_trace日志显示Key(Shift+Tab)。(一开始我也被这个骗了,但实际上这是Key::toString()的显示层归一化——不等于传下去的就是Tab。) Rime receive key日志打印Shift+ISO_Left_Tab,说明按键确实进了 librime,只是没匹配上。- 读源码确认传参。 fcitx5-rime 的
src/rimestate.cpp里keyEvent()用的是event.rawKey().sym(),未归一化,所以交给 librime 的是{ sym=65056 (ISO_Left_Tab), modifier=Shift }。 - 确认 librime 的匹配语义。
KeyBindingProcessor::ProcessKeyEvent用std::map<KeyEvent>::find()做精确 (keycode, modifier) 匹配,不把ISO_Left_Tab当Tab归一化。
后面几步(读 librime 源码、确认匹配语义)是借助 AI 完成的。
根因
| 配置项 | 解析结果 (keycode, modifier) | 与 (65056, Shift) 比较 |
|---|---|---|
Shift+Tab |
(65289, Shift) | 不同 keycode |
ISO_Left_Tab |
(65056, 无修饰) | 修饰不匹配 |
Shift+ISO_Left_Tab |
(65056, Shift) | 精确相等 |
Tab 正常是因为它的事件 (65289, 0) 和配置里的 Tab 解析结果完全一致;而 Shift+Tab 失败,是因为 rime-prelude 的惯用配置只覆盖了 Shift+Tab(65289, Shift) 与 ISO_Left_Tab(65056, 无修饰) 两条,唯独漏掉了表格里第三种形态 Shift+ISO_Left_Tab(65056, Shift),恰好就是实际事件。
简单解释 ISO_Left_Tab
Tab 键对应的标准 keysym 是 XK_Tab (0xFF09 / 65289)。当按下 Shift+Tab 时,在 XKB/libxkbcommon 层通常会生成一个特殊 keysym:ISO_Left_Tab (0xFE20 / 65056),同时保留 Shift 修饰位。ISO_Left_Tab 表示反向 Tab(backtab),属于 X11 keysym 中 ISO 系列符号。
Wayland 的 wl_keyboard 协议本身不负责 keysym 转换,而是由 compositor 通常通过 libxkbcommon 根据 XKB keymap 将物理按键转换为 keysym。某些程序如果直接用 keysym + modifier 精确匹配,而没有将 ISO_Left_Tab 归一化为 Tab,就会导致配置中的 Shift+Tab 无法匹配,此时需要显式绑定 Shift+ISO_Left_Tab。
解决方法
最终方案很简单, 其实就是用 Shift+ISO_Left_Tab 表示 Shift+Tab。rime 的话在 ~/.local/share/fcitx5/rime/default.custom.yaml 加:
1 | |
然后我尝试修改 noctalia 的快捷键,发现也一样有效。在 ~/.local/state/noctalia/settings.toml 里如下配置:
1 | |
参考资料
- rime-ice #1156: https://github.com/iDvel/rime-ice/issues/1156
- librime #609: https://github.com/rime/librime/issues/609
- rime-prelude key_bindings.yaml: https://github.com/rime/rime-prelude/blob/master/key_bindings.yaml