关于imgui的鼠标的释放以及imgui中文输入相关的问题
一、 核心组件与功能模块
1. 状态管理 (States)
- Menu::menu_open: 控制 ImGui 菜单是否显示的全局开关。
- CustomIMEState: 存储 IME 的实时数据,包括当前的拼音串(Composition String)、候选词列表(Candidates)、当前选中的索引以及是否处于打开状态。
2. 渲染劫持 (Hooking)
- MinHook: 用于劫持
IDXGISwapChain::Present函数。这是所有 DX11 游戏每一帧必经的函数,劫持它就能在游戏画面渲染完之后、显示到屏幕之前,“强行”画上自己的菜单。 - WndProc Hook: 通过
SetWindowLongPtr替换游戏的窗口过程函数,捕获键盘(如 Insert 键)、鼠标和输入法消息。
3. 游戏引擎交互 (SDL3 & InputSystem)
- UpdateCursorState: 这部分针对使用 SDL3 引擎的游戏。它通过动态获取
sdl3.dll的导出函数,在菜单打开时释放鼠标抓取(让鼠标能动),在菜单关闭时将鼠标控制权交还给游戏(通常是锁定在中心或隐藏)。
4. 自定义 IME 逻辑 (Custom IME)
- UpdateIMEData: 调用 Windows 原生 API (
ImmGetContext等) 获取系统输入法的后台数据。 - ShowImeOverlay: 用 ImGui 绘制一个浮动窗口,根据游戏内输入框的光标位置,显示拼音和候选词,外观类似现代输入法。
二、 详细执行流程
第一阶段:注入与挂钩 (Injection & Hooking)
- DLL 入口:
DllMain被触发,启动一个新线程运行CreateThreadRoutine。 - 寻找 Present 地址:
- 创建一个临时的 D3D11 设备和交换链(Swap Chain)。
- 从交换链的虚函数表(vtable)中找到第 9 个函数(索引 8),即
Present的内存地址。
- 安装 Hook:
- 使用 MinHook 将游戏的
Present指向自定义的my_present。 - 释放临时创建的资源。
第二阶段:初始化 (Initialization)
- 首次运行: 当游戏执行到
Present时,实际会进入my_present。 - 环境搭建:
- 获取游戏真实的 D3D 设备、上下文和窗口句柄 (
HWND)。 - 创建渲染目标视图 (
RenderTargetView)。 - 拦截消息: 替换游戏的
WndProc为代码中的自定义WndProc。 - 初始化 ImGui: 绑定 Win32 和 DX11 后端。
第三阶段:消息循环处理 (Message Handling)
在自定义的 WndProc 中:
- 快捷键检测: 监测
VK_INSERT键,切换菜单状态并调用UpdateCursorState同步鼠标状态。 - 输入法拦截 (核心):
- 当菜单打开且需要输入文字时,拦截
WM_IME_SETCONTEXT和WM_IME_STARTCOMPOSITION,屏蔽掉系统默认的输入法窗口,防止干扰。 - 在
WM_IME_COMPOSITION消息中,提取用户选定的最终汉字,放入g_imeQueue队列中。
- 输入重定向: 如果菜单打开,大部分鼠标和键盘消息会被拦截,不再传递给游戏逻辑,从而保证操作菜单时游戏人物不动。
第四阶段:每帧渲染 (Rendering Loop)
在每次 my_present 执行时:
- 同步输入: 从
g_imeQueue中取出待输入的字符,通过AddInputCharactersUTF8喂给 ImGui。 - 开启新帧: 调用 ImGui 的
NewFrame。 - 绘制自定义 IME: 如果检测到正在打字,调用
ShowImeOverlay在光标处画出候选框。 - 绘制菜单: 执行其他的菜单 UI 代码(用户可在此添加功能按钮)。
- 提交渲染: 渲染 ImGui 数据到游戏的缓冲区。
- 返回原函数: 调用原始的
Present函数,完成最终画面的呈现。
以下是针对中文输入流程的详细整理:
1. 消息拦截层 (The Shield)
在 WndProc 窗口过程函数中,代码通过拦截 Windows IME 消息,解除了系统输入法窗口与游戏窗口的显示关联。
- 屏蔽系统界面:
- 拦截
WM_IME_SETCONTEXT: 修改lParam参数,移除ISC_SHOWUICOMPOSITIONWINDOW等标志位。这告诉 Windows:“别在我的窗口上画那个丑丑的系统候选框”。 - 拦截
WM_IME_STARTCOMPOSITION: 返回 0。这阻止了系统默认的输入开始动作。 - 重定向输入上下文:
- 当 ImGui 的
WantTextInput为真(即你点击了输入框)时,通过ImmAssociateContext激活自定义的 IME 上下文,确保按键被输入法截获而非被游戏当做指令。
2. 数据抓取层 (The Brain)
当你在键盘上敲击拼音时,UpdateIMEData 函数负责通过 Windows IMM32 API 提取输入法内部的状态。
- 提取拼音 (Composition String): 使用
ImmGetCompositionStringW获取你正在输入的字母串(如 "ceshi")。 - 提取候选词 (Candidate List):
- 通过
ImmGetCandidateListW获取候选词列表。 - 代码逻辑中只取了前 9 个候选词,并记录了当前选中的索引(
dwSelection)。 - 字符转换: 将获取到的宽字符(WideChar)通过
WideToUTF8转换为 ImGui 支持的 UTF-8 格式。
3. 数据中转层 (The Bridge)
由于输入法消息是在窗口线程触发的,而渲染是在渲染线程,为了保证线程安全和不丢失字符,代码使用了一个生产者-消费者模型:
- 生产者 (
WndProc): 当你按下空格选定汉字时,WM_IME_COMPOSITION的GCS_RESULTSTR分支被触发,将确定的汉字推入g_imeQueue队列。 - 消费者 (
my_present): 在每一帧渲染开始前,程序会锁住互斥锁 (g_imeMutex),从队列中取出所有汉字,通过ImGui::GetIO().AddInputCharactersUTF8()喂给 ImGui。
4. 界面渲染层 (The Face)
这是最终呈现在屏幕上的部分,由 ShowImeOverlay 负责。
- 精确定位: 通过
GImGui->PlatformImeData.InputPos获取当前 ImGui 输入框光标的确切坐标。这保证了候选框能像在 Word 或微信里一样,跟随光标移动。 - 自定义样式:
- 使用
ImGuiWindowFlags_Tooltip等标志创建一个无边框、不置焦的浮动窗口。 - 高亮显示: 遍历候选词列表,如果该项是当前选中的(
is_selected),则使用黄色 (1.0f, 0.8f, 0.0f) 高亮显示。 - 拼音展示: 在候选词上方显示当前的拼音串,并用分割线分隔。
步骤动作目的1. 屏蔽拦截 WM_IME_SETCONTEXT隐藏系统自带的候选框,防止画面闪烁
2. 获取调用 ImmGetCandidateListW从系统内存读取拼音和候选词数据
3. 同步压入 g_imeQueue 队列确保多线程环境下输入字符不丢失
4. 注入AddInputCharactersUTF8将选定的汉字真正输入到 ImGui 的 InputText 控件
5. 绘制ShowImeOverlay在光标位置画出漂亮的自定义候选窗口
附件下载: (请点赞解锁下载)
🔒 mouse.cpp (点赞可见)
💬 评论 (0)