查看: 6829|回复: 21

OSG中使用CEGUI时的中文输入问题的解决方法

[复制链接]

该用户从未签到

发表于 2011-5-16 09:33:10 | 显示全部楼层 |阅读模式
在OSG中使用CEGUI时中文输入是比较麻烦的,必须自己建立Hook,经过了一天的研究终于弄出来了,在这里与大家分享一下
  1. #include <Imm.h>
  2. #include <stdio.h>

  3. HHOOK Chinese_hHook = NULL;
  4. HHOOK Input_hook = NULL;

  5. void InitHook()
  6. {
  7.         Input_hook = SetWindowsHookEx(WH_CALLWNDPROC ,IMM_GetMsgProc,GetModuleHandle( NULL ),GetCurrentThreadId());
  8.         Chinese_hHook = SetWindowsHookEx(WH_GETMESSAGE,ChineseCharHookProc,NULL,GetCurrentThreadId());
  9. }

  10. void ReleaseHook()
  11. {
  12.         if(Input_hook && Chinese_hHook)
  13.         {
  14.                 UnhookWindowsHookEx(Input_hook);
  15.                 UnhookWindowsHookEx(Chinese_hHook);
  16.         }

  17. }

  18. LRESULT CALLBACK IMM_GetMsgProc( int nCode, WPARAM wParam, LPARAM lParam )
  19. {
  20.         CWPSTRUCT* cw = (CWPSTRUCT*)lParam;
  21.         if (nCode < 0)
  22.         {
  23.                 return CallNextHookEx(Input_hook, nCode, wParam, lParam);
  24.         }

  25.         HIMC hIMC;
  26.         HWND hWnd;
  27.         DWORD dwSize;
  28.         HGLOBAL hstr;
  29.         LPSTR lpstr;

  30.         if (nCode == HC_ACTION  /*&& wParam != 0*/)
  31.         {
  32.                 switch (cw->message)
  33.                 {

  34.                 case WM_IME_STARTCOMPOSITION:

  35.                         {
  36.                                 CEGUI::Editbox * eb = (CEGUI::Editbox *)CEGUI::WindowManager::getSingleton().getWindow("IME/Composition");
  37.                                 eb->setVisible(true);
  38.                                 CEGUI::Editbox * win = (CEGUI::Editbox *)CEGUI::WindowManager::getSingleton().getWindow("Root/show");
  39.                                 win->setVisible(true);
  40.                                 //以上是现实输入法输入框
  41.                         }
  42.                        //可以自己建立一个CEGUI::EditBox来接收输入的数据
  43.                         break;

  44.                 case WM_IME_ENDCOMPOSITION:

  45.                         {
  46.                                 CEGUI::Editbox * eb = (CEGUI::Editbox *)CEGUI::WindowManager::getSingleton().getWindow("IME/Composition");
  47.                                 eb->setVisible(false);
  48.                                 CEGUI::Editbox * win = (CEGUI::Editbox *)CEGUI::WindowManager::getSingleton().getWindow("Root/show");
  49.                                 win->setVisible(false);
  50.                                 //以上是隐藏输入法输入框
  51.                         }

  52.                         break;

  53.                 case WM_IME_COMPOSITION:

  54.                         {

  55.                         //1.获得并设置输入框
  56.                         //先获取当前正在输入的窗口的输入法句柄
  57.                         HIMC hIMC = ImmGetContext(GetActiveWindow());
  58.                         // 先将ImmGetCompositionString的获取长度设为0来获取字符串大小.
  59.                         LONG dwSize = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
  60.                         WCHAR * lpstr = new WCHAR[dwSize + 1];

  61.                         // 再调用一次.ImmGetCompositionString获取字符串
  62.                         ImmGetCompositionStringW(hIMC, GCS_COMPSTR , lpstr, dwSize);
  63.                         lpstr[dwSize] = L'\0';

  64.                         //int num = MultiByteToWideChar(CP_ACP, NULL, lpstr, dwSize, NULL, 0);
  65.                         //WCHAR * pstr = new WCHAR[num + 1];
  66.                         //MultiByteToWideChar(CP_ACP, NULL, lpstr, dwSize, pstr, num);
  67.                         //pstr[num] = L'\0';

  68.                         int utf8Num = WideCharToMultiByte(CP_UTF8, NULL, lpstr, dwSize, NULL, 0, NULL, NULL);
  69.                         char * str = new char[utf8Num + 1];
  70.                         WideCharToMultiByte(CP_UTF8, NULL, lpstr, dwSize, str, utf8Num, NULL, NULL);
  71.                         str[utf8Num] = '\0';

  72.                         CEGUI::String string((CEGUI::utf8*)str);
  73.                         CEGUI::Editbox * eb = (CEGUI::Editbox *)CEGUI::WindowManager::getSingleton().getWindow("IME/Composition");
  74.                         float weight = eb->getFont()->getTextExtent(string);//getTextExtent(string.data());
  75.                         eb->setWidth(CEGUI::UDim(0.018f,weight));
  76.                         eb->setText(string.data());
  77.                         LONG cursorIndex = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
  78.                         eb->setCaratIndex(cursorIndex);


  79.                         //2.获得并设置候选框
  80.                         CEGUI::Window * win = CEGUI::WindowManager::getSingleton().getWindow("Root/show");
  81.                         LPCANDIDATELIST pList;
  82.                         dwSize = ImmGetCandidateListA(hIMC,0,NULL,0);
  83.                         pList = (LPCANDIDATELIST)GlobalAlloc( GPTR, dwSize);
  84.                         ImmGetCandidateListA(hIMC,0,pList,dwSize);
  85.                         //dwSize /= 2;
  86.                         DWORD * dwOffset = &pList->dwOffset[0];
  87.                         dwOffset += pList->dwPageStart;
  88.                         char buf[355];
  89.                         DWORD j = 0;
  90.                         for (DWORD i=0;i<pList->dwCount;i++)
  91.                         {
  92.                                 LPSTR s = (LPSTR)pList + *dwOffset++;                       
  93.                                 sprintf( buf + j,"%d.%s ",i+1,s);
  94.                                 j += strlen(s) + 3;
  95.                         }
  96.                         buf[j + 1] = '\0';
  97.                         int num = MultiByteToWideChar(CP_ACP, NULL, buf, j + 1, NULL, 0);
  98.                         WCHAR * pstr = new WCHAR[num + 1];
  99.                         MultiByteToWideChar(CP_ACP, NULL, buf, strlen(buf), pstr, num);
  100.                         pstr[num] = L'\0';
  101.                         utf8Num = WideCharToMultiByte(CP_UTF8, NULL, pstr, wcslen(pstr), NULL, 0, NULL, NULL);
  102.                         char * str2 = new char[utf8Num + 1];
  103.                         WideCharToMultiByte(CP_UTF8, NULL, pstr, wcslen(pstr), str2, utf8Num, NULL, NULL);
  104.                         str2[utf8Num] = '\0';

  105.                         CEGUI::String string2((CEGUI::utf8*)str2);
  106.                         weight = win->getFont()->getTextExtent(string2);
  107.                         win->setWidth(CEGUI::UDim(0.018f,weight));
  108.                         win->setText(string2.data());

  109.                         }

  110.                         break;
  111.                 }

  112.         }//if (nCode == HC_ACTION)

  113.         return 0;
  114. }

  115. LRESULT CALLBACK ChineseCharHookProc( int nCode, WPARAM wParam, LPARAM lParam )
  116. {
  117.         if(nCode < 0)
  118.                 return CallNextHookEx(Chinese_hHook,nCode,wParam,lParam);
  119.         //         if(!ImmIsIME(GetKeyboardLayout(0)))
  120.         //                 return CallNextHookEx(g_hHook,nCode,wParam,lParam);

  121.         MSG* msg = (MSG*)lParam;
  122.         switch(msg->message)
  123.         {
  124.         case WM_CHAR:
  125.                 {
  126.                         switch(msg->wParam)
  127.                         {
  128.                                 //输入状态下的特殊功能键位处理
  129.                         case VK_RETURN:
  130.                                 {
  131.                                         CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Return);
  132.                                         break;
  133.                                 }

  134.                         case VK_TAB:
  135.                                 {
  136.                                         CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Tab);
  137.                                         break;
  138.                                 }

  139.                         case VK_BACK:
  140.                                 {
  141.                                         CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Backspace);
  142.                                         break;
  143.                                 }

  144.                         case VK_SPACE:
  145.                                 {
  146.                                         CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Space);
  147.                                         CEGUI::System::getSingleton().injectChar((CEGUI::utf32)msg->wParam);
  148.                                         break;
  149.                                 }
  150.                         default:
  151.                                 {
  152.                                         //IMMFollow();//窗口跟随,暂时还没有实现
  153.                                         ChnInjectChar((CEGUI::utf32)msg->wParam);
  154.                                         return true;
  155.                                 }
  156.                                 break;
  157.                         }
  158.                 }
  159.         }

  160.         return CallNextHookEx(Chinese_hHook,nCode,wParam,lParam);
  161. }

  162. bool ChnInjectChar( CEGUI::utf32 code_point )
  163. {
  164. #ifndef UNICODE
  165.         static char s_tempChar[3] = "";
  166.         static wchar_t s_tempWchar[2] = L"";
  167.         static bool s_flag = false;
  168.         unsigned char uch = (unsigned char)code_point;
  169.         if( uch >= 0x80 )
  170.         {
  171.                 if( !s_flag )
  172.                 {
  173.                         s_tempChar[1] = (char)uch; //第二个字节
  174.                         s_flag = true;
  175.                         return true;
  176.                 }
  177.                 else if( uch >= 0x80 )
  178.                 {
  179.                         s_tempChar[0] = (char)uch; //第一个字节
  180.                         s_flag = false;
  181.                         MultiByteToWideChar( 0, 0, s_tempChar, 2, s_tempWchar, 1); //转成宽字节
  182.                         s_tempWchar[1] = L'\0';
  183.                         CEGUI::utf32 code = (CEGUI::utf32)s_tempWchar[0];
  184.                         return CEGUI::System::getSingleton().injectChar( code );
  185.                 }
  186.                 else
  187.                 {
  188.                         return CEGUI::System::getSingleton().injectChar(code_point);
  189.                 }
  190.         }
  191.         else
  192.         {
  193.                 s_flag = false;
  194.                 return CEGUI::System::getSingleton().injectChar(code_point);
  195.         }
  196. #else
  197.         return CEGUI::System::getSingleton().injectChar(code_point );
  198. #endif
  199. }
复制代码

该用户从未签到

 楼主| 发表于 2011-5-16 09:52:54 | 显示全部楼层

效果图

效果图

该用户从未签到

发表于 2011-5-17 08:26:40 | 显示全部楼层
Very cool~~

该用户从未签到

发表于 2011-5-18 10:11:26 | 显示全部楼层
非常棒的分享啊

该用户从未签到

发表于 2011-6-8 10:12:29 | 显示全部楼层
感谢 楼主 分享

该用户从未签到

发表于 2011-6-8 10:12:40 | 显示全部楼层
感谢 楼主 分享

该用户从未签到

发表于 2011-6-8 10:12:53 | 显示全部楼层
感谢 楼主 分享

该用户从未签到

发表于 2011-6-21 16:57:09 | 显示全部楼层
请问一下怎么切换到中文输入法呢。在osg的窗口中,按下Ctrl+Shift没有反应,输入法的窗口都没有。

该用户从未签到

 楼主| 发表于 2011-6-22 07:45:39 | 显示全部楼层
回复 8# zzq11105229

您开始打字,窗口中就会有显示了,只要切换输入法成功。

该用户从未签到

发表于 2011-6-22 10:35:56 | 显示全部楼层
回复 9# liuzhiyu123

    谢谢,现在就是不能成功切换输入法,
    在窗口里面只能输入英文字符呀,不能切换到汉字输入法。
    是不是要添加些什么代码,让输入法认为这个窗口是可输入的?
    一般的osg程序,也是不能切换输入法的,它没有可输入的地方,而记事本程序就可以,它们有什么不同啊?

该用户从未签到

发表于 2011-6-23 10:54:47 | 显示全部楼层
谢谢,问题解决了。可以切换输入法,只是消息处理不及时,看起来就没反应了。

该用户从未签到

发表于 2011-9-21 20:33:18 | 显示全部楼层
呵呵,楼主好强大啊,你研究的东西我现在也正在弄,像ODE做碰撞检测,cegui做界面。我用cegui做个树状的界面用来显示节点,也用到了中文显示,自己摸索了半天。单独的界面是可以了但与OSG结合就有点问题了。

该用户从未签到

 楼主| 发表于 2012-7-11 15:55:09 | 显示全部楼层

该用户从未签到

发表于 2012-7-19 17:13:54 | 显示全部楼层
马克一下!好东西,不过现在有些地方还看不懂!继续学习

该用户从未签到

发表于 2012-11-2 17:11:59 | 显示全部楼层
太好了  真心有用  只是我想问问 MultiEditBox这个如何显示中文呢 动态显示 在代码里用setText(字符串)该如何显示呢?  字符串是未知大小的 从数据库里读取

该用户从未签到

发表于 2013-5-17 16:15:37 | 显示全部楼层
亲 Imm.h需要什么库啊?我在控制台下有这个错误 error LNK2019: 无法解析的外部符号 _ImmGetCandidateListA@16,该符号在函数 "long __stdcall IMM_GetMsgProc(int,unsigned int,long)" (?IMM_GetMsgProc@@YGJHIJ@Z) 中被引用
1>main.obj : error LNK2019: 无法解析的外部符号 _ImmGetCompositionStringW@16,该符号在函数 "long __stdcall IMM_GetMsgProc(int,unsigned int,long)" (?IMM_GetMsgProc@@YGJHIJ@Z) 中被引用
1>main.obj : error LNK2019: 无法解析的外部符号 _ImmGetContext@4,该符号在函数 "long __stdcall IMM_GetMsgProc(int,unsigned int,long)" (?IMM_GetMsgProc@@YGJHIJ@Z) 中被引用

该用户从未签到

发表于 2013-5-17 16:20:00 | 显示全部楼层
已经找到 谢谢 #pragma comment(lib,"imm32.lib")

该用户从未签到

 楼主| 发表于 2013-5-17 16:27:23 | 显示全部楼层
w910916 发表于 2013-5-17 16:15
亲 Imm.h需要什么库啊?我在控制台下有这个错误 error LNK2019: 无法解析的外部符号 _ImmGetCandidateListA ...

输入法的接口    imm32.lib

该用户从未签到

发表于 2013-5-18 21:42:52 | 显示全部楼层
亲 您这完善了么?有很多bug

该用户从未签到

发表于 2013-5-18 21:43:11 | 显示全部楼层
由于没有用过输入法的这个库

该用户从未签到

 楼主| 发表于 2013-5-20 07:57:19 | 显示全部楼层
w910916 发表于 2013-5-18 21:42
亲 您这完善了么?有很多bug

bug 可以自己解决  这个只是一个解决方案

该用户从未签到

 楼主| 发表于 2013-5-20 07:57:52 | 显示全部楼层
w910916 发表于 2013-5-18 21:43
由于没有用过输入法的这个库

输入法 编程 网上的资料很多 您可以自己查找
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表