Bug 反馈:Web 平台无法输入中文(IME 组合事件未到达 SDL)

06/0453 浏览BUG反馈

问题描述

我在开发一款 DND 风格的回合制策略 RPG,项目中有一个 F9 快捷键调出的 UI 编辑器(NanoVG 即时模式渲染),编辑器中有文本输入功能(备注字段、重命名等)。
遇到的问题是:在 Web 平台上,所有文本输入框(包括自定义 NanoVG 编辑器和引擎自带的 urhox-libs/UITextField 组件)都无法输入中文。英文字符输入正常,但切换到中文输入法后,无论使用何种 IME(微软拼音、搜狗等),都没有任何字符被输入。粘贴中文内容同样无效。
关键事实:此功能以前是正常工作的。在某次引擎构建更新后失效。
horizontal linehorizontal line

排查过程

第一步:排除编辑器代码问题

最初怀疑是 F9 编辑器代码拆分时引入的 bug(编辑器从单文件拆为 UIEditor.lua / UIEditorDraw.lua / UIEditorInput.lua / UIEditorData.lua 四文件)。
为了验证,编写了独立的 IME 测试脚本,仅使用引擎自带的 UI.TextField 组件,完全不涉及 F9 编辑器代码:
lua复制
lua
local UI = require("urhox-libs/UI") local textField = UI.TextField { placeholder = "在此输入中文测试...", onChange = function(self, value) print("onChange: " .. value) end, }
测试结果UI.TextField 同样无法输入中文。英文正常、粘贴英文正常、粘贴中文无效、中文输入法无效。
结论:不是编辑器代码的问题,是引擎平台层面的问题。

第二步:编写运行时诊断工具

由于 Web 平台无法同时打开 F12 DevTools 和游戏页面(打开 DevTools 会关闭游戏),编写了屏幕叠加诊断工具 scripts/tools/IMEDiag.lua,使用 NanoVG 在游戏画面上直接渲染输入事件日志。
诊断工具订阅了以下引擎事件:
  • KeyDown — 按键按下
  • KeyUp — 按键抬起
  • TextInput — 文本输入(已提交的字符)
  • TextEditing — IME 组合文本(拼音候选等)

第三步:采集诊断数据

通过诊断工具在运行时实际观察到以下行为:
horizontal linehorizontal line

诊断数据与发现

运行时环境状态

screenKeyboardSupport = false    (平台报告不支持屏幕键盘)
screenKeyboardVisible = true     (SDL 认为 TextInput 已激活)
GetPlatform() = "Web"

发现 1:英文输入完全正常

当系统输入法处于英文模式时,每次按键都会产生一对完整事件:
KeyDown: key=100 scancode=7 qual=0    (D键按下)
TextInput: "d" [64]                    (收到字符 'd')
英文字母、数字、符号、退格、回车均正常工作。

发现 2:Ctrl+Space IME 切换组合键被浏览器拦截

按下 Ctrl+Space 切换输入法时,诊断工具只收到 Ctrl 的 KeyDown 事件:
KeyDown: key=1073742048 scancode=224 qual=2 (左Ctrl按下)
Space 的 KeyDown 事件从未出现。浏览器在操作系统层面拦截了 Ctrl+Space 组合键用于 IME 切换,不会将 Space 传递给 Canvas/SDL。
但用户确认:系统任务栏的输入法图标确实发生了变化,说明 OS 层面的 IME 切换成功了。

发现 3:IME 激活后所有事件消失

切换到中文输入法后,在游戏中按任何键:
  • 没有 KeyDown 事件
  • 没有 TextInput 事件
  • 没有 TextEditing 事件(IME 组合事件)
完全静默。诊断日志不再有任何新条目。
即使切回英文模式(通过 Shift 或再次 Ctrl+Space),英文字符也不再出现,直到重新点击 Canvas 获取焦点。

发现 4:SetScreenKeyboardVisible 是空操作

lua复制
lua
input:SetScreenKeyboardVisible(true) -- 无效果 input:SetScreenKeyboardVisible(false) -- 无效果
由于 screenKeyboardSupport = false,这两个调用在 Web 平台上是完全空操作。SDL 层面的 SDL_StartTextInput() / SDL_StopTextInput() 实际上没有执行(或执行了但没有效果)。

发现 5:UI.TextField 组件同样受影响

引擎自带的 urhox-libs/UI/Widgets/TextField.lua 内部调用的也是 input:SetScreenKeyboardVisible(true),因此同样无法触发 IME。这排除了任何应用层代码的问题。
horizontal linehorizontal line

我尝试过的修复

尝试 1:移除所有 SetScreenKeyboardVisible(false) 调用

理论:可能某处代码调用了 SetScreenKeyboardVisible(false) 导致 SDL_StopTextInput 永久关闭了 IME。
结果:移除所有 false 调用后,问题不变。

尝试 2:在文本框获得焦点时仅调用一次 SetScreenKeyboardVisible(true)

理论:可能需要主动触发 SDL_StartTextInput 来聚焦隐藏的 textarea。
结果:问题不变。screenKeyboardSupport=false 导致此调用是空操作。

尝试 3:从 Lua 层寻找 JavaScript 互操作 API

搜索了 .emmylua/engine-docs/urhox-libs/ 所有目录,查找任何 JS 桥接、eval、emscripten 相关 API。
结果:引擎不暴露任何 JavaScript 互操作接口到 Lua 层。无法从 Lua 代码手动创建/聚焦 HTML input 元素。
horizontal linehorizontal line

问题根因分析

基于以上诊断数据,问题的根因是:
SDL/Emscripten 层的隐藏 textarea 没有正确接收浏览器的 IME 组合事件。
在 Web 平台上,IME 组合事件(compositionstart / compositionupdate / compositionend)只会发送给当前 focused 的 <input><textarea> 元素。SDL 的 Emscripten 后端通常会创建一个隐藏的 textarea 来接收这些事件,然后转化为 SDL 的 SDL_TEXTEDITINGSDL_TEXTINPUT 事件。
当前的表现说明以下情况之一:
  1. SDL 的隐藏 textarea 没有被创建
  2. 或创建了但没有获得浏览器 focus
  3. SDL_StartTextInput() 因为某种原因没有被调用(screenKeyboardSupport=false 可能导致跳过了 textarea 创建逻辑)
  4. 或浏览器/Emscripten SDK 更新后改变了事件传递行为
证据支持urhox-libs/UI/Widgets/TextField.lua 第 593-598 行有以下注释:
lua复制
lua
-- Web + touch: browser paste event via hidden input already injects TextInput, -- skip DoPaste to avoid double-paste.
这说明引擎设计上确实依赖一个隐藏 input 元素来桥接浏览器事件。但当前这个机制失效了。
horizontal linehorizontal line

这个问题的更广泛影响

对所有 Web 项目的影响:这不是个别项目的问题。任何在 Web 平台上需要中文输入的 UrhoX 项目都会受到影响,包括使用引擎自带 UI.TextField 组件的项目。
对开发者的影响
  • 需要中文输入的游戏功能(聊天、角色命名、编辑器文本字段等)在 Web 平台完全不可用
  • 此问题在 Lua 层面没有 workaround — 没有 JS 互操作接口可以手动修复
  • 排查成本高 — 需要编写自定义运行时诊断工具才能确认原因(Web 平台无法同时使用 DevTools)
对玩家的影响:任何需要输入中文的交互(角色命名、聊天等)对中文用户完全不可用。
horizontal linehorizontal line

期望的修复方向

  1. 确认 Emscripten 后端是否正确创建了隐藏 textarea 用于 IME 输入
  2. 确认 SDL_StartTextInput() 在 Web 平台是否正确聚焦该 textarea
  3. 检查 screenKeyboardSupport 返回 false 是否错误地跳过了 textarea 创建/聚焦逻辑
  4. 验证浏览器 compositionstart / compositionupdate / compositionend 事件是否能正确触发 SDL_TEXTEDITING / SDL_TEXTINPUT
horizontal linehorizontal line

诊断脚本说明

诊断工具位于 scripts/tools/IMEDiag.lua,在游戏运行时通过 NanoVG 在屏幕左下角渲染半透明日志面板,实时显示所有输入事件(KeyDown/KeyUp/TextInput/TextEditing)。进入 F9 编辑器时自动激活。
IME 功能验证测试脚本位于 scripts/tools/IMETest.lua,使用 UI.TextField 组件提供独立的输入测试环境。
5
2
1