Ubuntu 中文输入法 Shift 切换故障排查与 GNOME 输入源机制剖析

八月 02, 2026 [桌面与效率] #Linux #Wayland

Ubuntu 中文输入法 Shift 切换故障排查与 GNOME 输入源机制剖析

问题现象

Ubuntu 26.04 + GNOME Wayland + ibus-libpinyin 环境下,按 Shift 切换中英文输入法时经常失效,且失效时伴随候选词窗口消失等异常。同时发现 GNOME 设置界面无法添加已删除的 xkb 输入源。

排查过程中一路深入,最终把 xkb、ibus、GNOME 输入源这三者的分层协作机制彻底理清。本文记录完整排查过程与概念剖析。

排查过程

第一步:确认环境

# 发行版 / 会话
cat /etc/os-release          # Ubuntu 26.04 LTS
echo $XDG_SESSION_TYPE       # wayland
echo $XDG_CURRENT_DESKTOP    # ubuntu:GNOME

# 输入法框架进程
ps aux | grep -iE "fcitx|ibus|scim"
# /usr/bin/ibus-daemon --panel disable
# /usr/libexec/ibus-engine-libpinyin --ibus

结论:GNOME Wayland 会话 + IBus + libpinyin 引擎,ibus-daemon 以 --panel disable 启动。

第二步:检查配置

gsettings get org.gnome.desktop.input-sources sources
# [('xkb', 'cn'), ('ibus', 'libpinyin')]   ← 两个输入源

gsettings get com.github.libpinyin.ibus-libpinyin.libpinyin main-switch
# '<Shift>'   ← Shift 切换已配置

gsettings get org.freedesktop.ibus.general use-global-engine   # true
gsettings get org.freedesktop.ibus.general embed-preedit-text  # true

配置本身正常,但发现几个高风险因素。

第三步:定位根因

1. 双输入源结构(xkb-cn + libpinyin)

输入源分两种:xkb 键盘布局(纯键盘映射,无引擎)和 ibus 引擎(真正的输入法)。用 Super+Space 切换输入源时,如果停在 xkb-cn(中文键盘布局)上,Shift 切换必然无效——切换逻辑在 libpinyin 引擎的状态机里,而 xkb 布局层根本没有"模式"概念。

2. Chromium/Electron 应用吞键

运行环境中有 VS Code 和 Chrome(均为 Chromium 系)。这类应用在 GNOME Wayland 下存在已知问题:Shift 修饰键事件可能被应用自身消费,未传给 ibus 引擎,导致切换失败——这与"间歇性失效"(而非永久失效)的特征吻合。

3. 候选词窗口消失的机理

embed-preedit-text = true 时,候选框嵌入应用窗口内渲染。Chromium 应用清理嵌入候选框的时机存在 bug:可能清掉候选框但未把 Shift 事件转发给引擎——视觉上"候选框消失",实际模式没切换。

4. 全局模式放大故障

use-global-engine = true 让中英模式在所有应用间共享,一次吞键的失败状态会传染到所有应用。

第四步:修复方案

修复 1:移除 xkb-cn,只保留 libpinyin 输入源

gsettings set org.gnome.desktop.input-sources sources "[('ibus', 'libpinyin')]"

修复 2:恢复 ibus 语言栏(解决状态不可见)

移除 xkb 后,GNOME 顶部栏在单一输入源时不显示指示器(keyboard.js 的 _syncShowIndicator 逻辑)。恢复语言栏的方法:ibus 由 systemd 管理,用 drop-in 覆盖掉 --panel disable:

mkdir -p ~/.config/systemd/user/org.freedesktop.IBus.session.GNOME.service.d/
cat > ~/.config/systemd/user/org.freedesktop.IBus.session.GNOME.service.d/panel.conf << 'EOF'
[Service]
ExecStart=
ExecStart=/usr/bin/ibus-daemon --replace
EOF
systemctl --user daemon-reload
systemctl --user restart org.freedesktop.IBus.session.GNOME.service

重启后语言栏窗口出现,中英状态可见。

修复 3(可选):两条 gsettings 优化

# 候选框改为 ibus 独立弹出窗口,规避 Chromium 清理候选框的 bug
gsettings set org.freedesktop.ibus.general embed-preedit-text false
# 各应用独立记忆中英模式,防止一次失败全局传染
gsettings set org.freedesktop.ibus.general use-global-engine false

概念剖析:xkb 与输入法的分层协作

按键处理完整链路

物理键盘 ──内核 input 子系统──> 扫描码(scancode) ──> compositor(Mutter) ──> keysym ──> ibus 引擎 ──> 字符上屏
             内核只做这件事                  ┌────────────────────────────┘
                                           libxkbcommon (用户态 C 库)

xkb 是内核实现还是用户态实现?

用户态,但在比 libpinyin 更底层的位置:

层实现职责
内核 inputevdev上报扫描码
compositorlibxkbcommon扫描码 → keysym 映射
输入法框架ibus-daemon + libpinyin输入法逻辑(状态机)

xkb 是 X11 时代(X Keyboard Extension)的遗留技术,被 libxkbcommon 库继承,在合成器内部运行。ibus/libpinyin 是合成器外的独立进程,通过 Wayland 的 text-input 协议与合成器通信。Wayland 本身不实现任何输入处理,只规定协议。

GNOME 的"输入源"抽象

GNOME 把 xkb 布局和 ibus 引擎统一抽象为"输入源"(input source),这是一个用户视角的"配置集合",而不是技术层的概念:

输入源(智能拼音) = { xkb布局: default(us) }  +  { ibus引擎: libpinyin }
输入源(中文中国) = { xkb布局: cn }           +  { 无引擎 }

切换输入源时,GNOME 同时通知两个层:

切换输入源 (面板/Super+Space)
        │
        ├──> Mutter 层:切换 xkb 布局(用 libxkbcommon 重新加载映射)
        │
        └──> ibus 层:激活/停用对应引擎

所以面板上的互斥是"输入源选择器"的互斥(同一时刻只能激活一个输入源对象),不是 xkb 层和引擎层的互斥。激活的输入源内部,xkb 层和引擎层始终同时工作——没有 xkb 映射,连 a 键都映射不出 a 字符。

每个引擎都绑定一个 xkb 布局

验证 libpinyin 引擎的注册表声明:

python3 -c "
import gi
gi.require_version('IBus','1.0')
from gi.repository import IBus
bus = IBus.Bus()
for e in bus.get_engines_by_names(['libpinyin']):
    print(e.get_name(), e.get_layout(), e.get_language())
"
# libpinyin default zh_CN

default 即美式 QWERTY——拼音引擎必须依赖标准字母映射才能输入拼音。这也是引擎无法脱离输入源体系单独存在的原因。

sources 配置格式详解

gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('ibus', 'libpinyin')]"

为什么设置界面找不到 xkb-cn?

GNOME 设置"添加输入源"对话框受 show-all-sources 键控制(默认 false):

gsettings set org.gnome.desktop.input-sources show-all-sources true

而命令行 gsettings set 直接写入 dconf 配置库,绕过 UI 过滤逻辑,所以始终有效。evdev.xml 里的 Chinese 布局一直存在——删除输入源只是从 dconf 列表移除引用,布局本身从未消失。

总结

  1. Shift 切换失效排查要点:检查输入源结构(是否停在无引擎的 xkb 源上)、main-switch 配置、运行中的 Chromium 应用、embed-preedit-text 与 use-global-engine 设置
  2. xkb 是用户态,位于 compositor 层,提供 keysym 映射;ibus 引擎是合成器外的进程,两者通过 Wayland 协议协作
  3. GNOME 输入源 = xkb 布局 + 引擎的打包组合,面板互斥的是输入源对象,xkb 层永远在工作
  4. 每个引擎输入源都绑定一个 xkb 布局(libpinyin 为 default/us),这是引擎正确工作的基础