在远程桌面中,中文输入可以分为两个问题:终端如何显示中文,以及 图形程序如何连接输入法。本文记录一次实际排查:从桌面节点打开 Terminal,再通过 SSH 连接计算节点,并在远程节点启动 VS Code。

环境和目标

节点 用途
172.31.0.122 Xfce 桌面,运行 Terminal 和桌面 IBus
172.31.0.132 SSH 目标节点,运行远程 shell 和 VS Code

两台机器的用户家目录通过 NFS 共享。因此,在一台机器修改的 ~/.cshrc 和 ~/bin 脚本,在另一台机器也能看到;修改启动配置时必须考虑机器差异。

目标是:

  1. 在 122 的桌面 Terminal 输入、显示中文。
  2. SSH 到 132 后,在 shell 中输入、显示中文。
  3. 从 132 的 shell 启动 VS Code,在编辑器中输入、显示中文,并尽量让候选框靠近输入光标。

本文使用 IBus 和 Intelligent Pinyin(libpinyin),桌面为 X11 环境。下文的候选框位置是相对于文本插入光标,不是保证跟随鼠标指针。

一、先配置桌面的中文输入法

在 122 的桌面打开 IBus Preferences,进入 Input Method,点击 Add,添加中文输入法,例如 Chinese → Intelligent Pinyin。

使用桌面配置的输入法切换快捷键,切换到中文输入法后,在桌面应用中试输中文。具体快捷键可以在 IBus Preferences 中查看或调整。

二、解决本机 Terminal 中中文显示为问号

添加中文输入法后,最初在 Terminal 中输入中文仍显示为问号。将 Terminal 菜单中的 Encoding 设置为 UTF-8 后,中文输入和显示正常。

终端编码和输入法是不同的设置:输入法负责把拼音转换为汉字,终端编码决定终端如何解释和显示字符。应先确认:

  • Terminal 的 Encoding 为 UTF-8。
  • 桌面输入法已经切换为中文。
  • shell 的 locale 支持 UTF-8。可以运行 locale 查看;具体应使用系统实际安装的 UTF-8 locale。

三、SSH 后在远程 shell 中输入中文

从 122 的 Terminal 连接 132:

1
ssh 172.31.0.132

连接后可检查远端 locale,并试输中文:

1
2
locale
printf '中文测试\n'

这里的 Terminal 窗口仍运行在 122,中文输入也是在本地 Terminal 完成后,通过 SSH 传给 132 的 shell。远端 shell 不需要仅为了接收中文而单独启动一个 IBus;终端编码、远端 locale 和远端应用的字符处理仍需匹配 UTF-8。

四、远程启动 VS Code:能输入但候选框位置不理想

VS Code 是从 132 启动并显示在 122 桌面上的图形程序,输入法连接方式与终端不同。首先尝试过:

1
env GTK_IM_MODULE=ibus QT_IM_MODULE=ibus XMODIFIERS=@im=ibus code

在当时的环境中,这种方式无法输入中文。将输入法模块改为 XIM 后,中文输入得到验证,但候选框位置不理想,离编辑器内的文本光标较远:

1
env GTK_IM_MODULE=xim QT_IM_MODULE=xim XMODIFIERS=@im=ibus code

这种方式适合只要求能够输入中文、可以接受候选框位置的情况。它依赖可访问的 X11 显示和相应 IBus/XIM 服务;如果 VS Code 复用了已经运行的进程,新设置未必会被该进程采用。

五、让 VS Code 候选框靠近输入光标

进一步测试发现,在 132 上为 VS Code 使用 D-Bus 会话和 IBus,再以 IBus 模块启动 VS Code,可以让候选框出现在输入光标附近。

先确认从 132 可以打开图形程序,且 $DISPLAY 指向 122 的桌面。如果使用 SSH X11 转发,连接时可按站点配置使用 ssh -X 或 ssh -Y;不要在未确认显示连接可用时继续排查输入法。

手动测试示例:

1
2
3
4
5
6
7
dbus-run-session -- sh -c '
    ibus-daemon -d
    sleep 1
    ibus engine libpinyin
    env GTK_IM_MODULE=ibus QT_IM_MODULE=ibus XMODIFIERS=@im=ibus \
        code --user-data-dir=/tmp/vscode-native-ibus --new-window --wait
'

在打开的编辑器中输入拼音,检查候选框是否靠近文本插入光标,以及汉字是否能正常提交。--user-data-dir 使用独立的 VS Code 用户数据目录,避免复用其他 VS Code 实例;因此这个实例的设置、扩展等可能与平时使用的实例不同。--wait 让启动命令等待窗口关闭。

综上,解决方案:Wrapper自动识别与启动

以下 Wrapper 是本环境使用的启动方式。它优先尝试使用当前环境中的 D-Bus 和 IBus;否则为 VS Code 创建一个新的 D-Bus 会话。

将其保存在与 code 命令相同的目录(本环境以 /tools/OSS/VSCode/1.129.1/bin 为例),命名为 code-ibus,并赋予执行权限,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/sh
# VSCode launcher with ibus/libpinyin. Reuses an existing dbus+ibus, else starts its own.
# wanlinwang, 20261030

export GTK_IM_MODULE=ibus QT_IM_MODULE=ibus XMODIFIERS=@im=ibus

# Real VSCode = sibling `code` next to this script (no PATH lookup, so no alias/recursion issues).
HERE=$(dirname "$(readlink -f "$0")")
CODE_BIN=$HERE/code
if [ ! -x "$CODE_BIN" ]; then
    echo "ERROR: $CODE_BIN not found. Load a VSCode version first, e.g.: ml VSCode/1.129.1" >&2
    exit 1
fi
if [ -z "$DISPLAY" ]; then
    echo "ERROR: DISPLAY is not set (use ssh -X/-Y, or bsub -XF / -Is with X forwarding)." >&2
    exit 1
fi

UDD=/tmp/vscode-ibus-$USER      # per-user, so users on a shared host don't collide
have() { pgrep -u "$USER" -x "$1" >/dev/null 2>&1; }

if [ -n "$DBUS_SESSION_BUS_ADDRESS" ] && have dbus-daemon; then
    # Reuse the existing session bus; start ibus only if it isn't running.
    have ibus-daemon || { ibus-daemon -drx; sleep 1; }
    ibus engine libpinyin
    exec "$CODE_BIN" --user-data-dir="$UDD" --new-window --wait "$@"
else
    # No usable bus: start a private one.
    exec dbus-run-session -- sh -c '
        CODE_BIN=$1; UDD=$2; shift 2
        ibus-daemon -drx
        sleep 1
        ibus engine libpinyin
        exec "$CODE_BIN" --user-data-dir="$UDD" --new-window --wait "$@"
    ' sh "$CODE_BIN" "$UDD" "$@"
fi

在 Modulefile 中设置 code 指向 code-ibus

1
2
3
4
5
6
7
8
[wanlin.wang@rd-172-31-0-132 ~]$ ml show VSCode/1.129.1
-------------------------------------------------------------------
/tools/modulefiles/OSS/VSCode/1.129.1:

module-whatis   {Modulefile for OSS/VSCode/1.129.1}
prepend-path    PATH /tools/OSS/VSCode/1.129.1/bin
set-alias       code code-ibus
-------------------------------------------------------------------

此后,无论是在桌面启动的 shell 环境还是在 从桌面环境 ssh 过去后的 shell 环境,加载该 VSCode 均能使用配置好的 中文输入法。

六、排查时的记录

场景 处理方式 本次观察
122 本机 Terminal 添加 IBus 中文输入法,Terminal 设为 UTF-8 能输入、显示中文
SSH 到 132 的 shell 保持 Terminal 和 locale 使用 UTF-8 能输入、显示中文
132 的 VS Code,XIM 设置 GTK_IM_MODULE=xim 等环境变量 能输入,但候选框离文本光标较远
132 的 VS Code,IBus 在远端 D-Bus 会话中启动 IBus 和 VS Code 测试时候选框靠近文本光标