关于imgui的鼠标的释放以及imgui中文输入相关的问题 - ShadowStrike

关于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)

  1. DLL 入口: DllMain 被触发,启动一个新线程运行 CreateThreadRoutine
  2. 寻找 Present 地址:
  • 创建一个临时的 D3D11 设备和交换链(Swap Chain)。
  • 从交换链的虚函数表(vtable)中找到第 9 个函数(索引 8),即 Present 的内存地址。
  1. 安装 Hook:
  • 使用 MinHook 将游戏的 Present 指向自定义的 my_present
  • 释放临时创建的资源。

第二阶段:初始化 (Initialization)

  1. 首次运行: 当游戏执行到 Present 时,实际会进入 my_present
  2. 环境搭建:
  • 获取游戏真实的 D3D 设备、上下文和窗口句柄 (HWND)。
  • 创建渲染目标视图 (RenderTargetView)。
  • 拦截消息: 替换游戏的 WndProc 为代码中的自定义 WndProc
  • 初始化 ImGui: 绑定 Win32 和 DX11 后端。

第三阶段:消息循环处理 (Message Handling)

在自定义的 WndProc 中:

  1. 快捷键检测: 监测 VK_INSERT 键,切换菜单状态并调用 UpdateCursorState 同步鼠标状态。
  2. 输入法拦截 (核心):
  • 当菜单打开且需要输入文字时,拦截 WM_IME_SETCONTEXTWM_IME_STARTCOMPOSITION屏蔽掉系统默认的输入法窗口,防止干扰。
  • WM_IME_COMPOSITION 消息中,提取用户选定的最终汉字,放入 g_imeQueue 队列中。
  1. 输入重定向: 如果菜单打开,大部分鼠标和键盘消息会被拦截,不再传递给游戏逻辑,从而保证操作菜单时游戏人物不动。

第四阶段:每帧渲染 (Rendering Loop)

在每次 my_present 执行时:

  1. 同步输入: 从 g_imeQueue 中取出待输入的字符,通过 AddInputCharactersUTF8 喂给 ImGui。
  2. 开启新帧: 调用 ImGui 的 NewFrame
  3. 绘制自定义 IME: 如果检测到正在打字,调用 ShowImeOverlay 在光标处画出候选框。
  4. 绘制菜单: 执行其他的菜单 UI 代码(用户可在此添加功能按钮)。
  5. 提交渲染: 渲染 ImGui 数据到游戏的缓冲区。
  6. 返回原函数: 调用原始的 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)

由于输入法消息是在窗口线程触发的,而渲染是在渲染线程,为了保证线程安全和不丢失字符,代码使用了一个生产者-消费者模型

  1. 生产者 (WndProc): 当你按下空格选定汉字时,WM_IME_COMPOSITIONGCS_RESULTSTR 分支被触发,将确定的汉字推入 g_imeQueue 队列。
  2. 消费者 (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)

发表你的看法