CEGUI是OGRE默认推荐的一个界面库.虽然Ogre官方表示CEGUI不是唯一的选择,但是我想很多人会因为Ogre的关系而选择CEGUI库.但是CEGUI内部使用的是UNICODE,使得在支持中文上有一些问题.
目前网上解决方案大多是不使用Ogre的默认的窗口,而是自己使用windows API创建自己的窗口,处理WM_CHAR消息,实现中文输入.但我觉得这个方案似乎动作太大了点.自己管理窗口意味着自己还要处理一些诸如窗口移动等许多消息.我个人觉得麻烦.一个程序员,要节省自己的时间,唯一的办法就是不要自己发明轮子.
我花了大概一天的时间在网上查找资料.整个中文输入的流程大概是这样:当客户输入中文的时候,windows系统的ime库拦截到响应的输入信息,做出一系列处理后,将输入转换成中文,把结果用WM_CHAR消息发送给应用程序,然后应用程序处理WM_CHAR消息,在相应的输入窗口显示中文.整个流程看起来是这样:
输入的按键消息 -> IME转换为中文 -> IME发送WM_CHAR消息 -> 应用程序窗口响应并显示消息
Ogre之所以存在中文输入的问题在于Ogre的默认窗口是不能处理WM_CHAR消息的.据我所知国内解决这个问题除了上面说的办法外,还有个办法是修改Ogre的源代码.但是同样的,这个手术太大了.既然问题在于应用程序的窗口不能响应消息,那我们就想个办法自己处理WM_CHAR消息好了.毕竟IME的工作是很正常的,即便我们使用的是Ogre默认窗口,WM_CHAR仍然在发送.
使用消息钩子HOOK可以做到这一点.因为整个程序的源代码都是我自己写,所以我可以使用一个局部钩子,直接添加个函数进源代码.这个函数看上去是这样:
- //局部钩子函数 截取WM_CHAR消息实现中文输入
- LRESULT CALLBACK ChineseCharHookProc(int nCode, WPARAM wParam, LPARAM lParam)
- {
- if(nCode < 0)
- return CallNextHookEx(g_hHook,nCode,wParam,lParam);
- if(!ImmIsIME(GetKeyboardLayout(0)))
- return CallNextHookEx(g_hHook,nCode,wParam,lParam);
- MSG* msg = (MSG*)lParam;
- switch(msg->message)
- {
- case WM_CHAR:
- {
- switch(msg->wParam)
- {
- //输入状态下的特殊功能键位处理
- case VK_RETURN:
- {
- CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Return);
- break;
- }
- case VK_TAB:
- {
- CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Tab);
- break;
- }
- case VK_BACK:
- {
- CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Backspace);
- break;
- }
- default:
- {
- CEGUI::utf32 utf = (CEGUI::utf32)(msg->wParam);
- CEGUI::System::getSingleton().injectChar(utf);
- // CEGUI::Window* w = CEGUI::WindowManager::getSingleton().getWindow("Root/Input");
- // w->setText(w->getText() + utf);
- return true;
- }
- break;
- }
- }
- }
- return CallNextHookEx(g_hHook,nCode,wParam,lParam);
- }
ImmIsIME(GetKeyboardLayout(0))这个函数检查输入法状态是否打开.所以这个函数的作用就是检查输入法,当打开输入法的时候把输入转换成CEGUI可以识别的模式.
另外在构造和析构函数要分别添加:
g_hHook =SetWindowsHookEx(WH_GETMESSAGE,ChineseCharHookProc,NULL,GetCurrentThreadId())
和
UnhookWindowsHookEx(g_hHook)
语句.OIS的capture代码段要加入对输入法状态的判断,否则会造成双重输入.