liuzhiyu123 发表于 2011-5-16 09:33:10

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

在OSG中使用CEGUI时中文输入是比较麻烦的,必须自己建立Hook,经过了一天的研究终于弄出来了,在这里与大家分享一下#include <Imm.h>
#include <stdio.h>

HHOOK Chinese_hHook = NULL;
HHOOK Input_hook = NULL;

void InitHook()
{
        Input_hook = SetWindowsHookEx(WH_CALLWNDPROC ,IMM_GetMsgProc,GetModuleHandle( NULL ),GetCurrentThreadId());
        Chinese_hHook = SetWindowsHookEx(WH_GETMESSAGE,ChineseCharHookProc,NULL,GetCurrentThreadId());
}

void ReleaseHook()
{
        if(Input_hook && Chinese_hHook)
        {
                UnhookWindowsHookEx(Input_hook);
                UnhookWindowsHookEx(Chinese_hHook);
        }

}

LRESULT CALLBACK IMM_GetMsgProc( int nCode, WPARAM wParam, LPARAM lParam )
{
        CWPSTRUCT* cw = (CWPSTRUCT*)lParam;
        if (nCode < 0)
        {
                return CallNextHookEx(Input_hook, nCode, wParam, lParam);
        }

        HIMC hIMC;
        HWND hWnd;
        DWORD dwSize;
        HGLOBAL hstr;
        LPSTR lpstr;

        if (nCode == HC_ACTION/*&& wParam != 0*/)
        {
                switch (cw->message)
                {

                case WM_IME_STARTCOMPOSITION:

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

                case WM_IME_ENDCOMPOSITION:

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

                        break;

                case WM_IME_COMPOSITION:

                        {

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

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

                        //int num = MultiByteToWideChar(CP_ACP, NULL, lpstr, dwSize, NULL, 0);
                        //WCHAR * pstr = new WCHAR;
                        //MultiByteToWideChar(CP_ACP, NULL, lpstr, dwSize, pstr, num);
                        //pstr = L'\0';

                        int utf8Num = WideCharToMultiByte(CP_UTF8, NULL, lpstr, dwSize, NULL, 0, NULL, NULL);
                        char * str = new char;
                        WideCharToMultiByte(CP_UTF8, NULL, lpstr, dwSize, str, utf8Num, NULL, NULL);
                        str = '\0';

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


                        //2.获得并设置候选框
                        CEGUI::Window * win = CEGUI::WindowManager::getSingleton().getWindow("Root/show");
                        LPCANDIDATELIST pList;
                        dwSize = ImmGetCandidateListA(hIMC,0,NULL,0);
                        pList = (LPCANDIDATELIST)GlobalAlloc( GPTR, dwSize);
                        ImmGetCandidateListA(hIMC,0,pList,dwSize);
                        //dwSize /= 2;
                        DWORD * dwOffset = &pList->dwOffset;
                        dwOffset += pList->dwPageStart;
                        char buf;
                        DWORD j = 0;
                        for (DWORD i=0;i<pList->dwCount;i++)
                        {
                                LPSTR s = (LPSTR)pList + *dwOffset++;                       
                                sprintf( buf + j,"%d.%s ",i+1,s);
                                j += strlen(s) + 3;
                        }
                        buf = '\0';
                        int num = MultiByteToWideChar(CP_ACP, NULL, buf, j + 1, NULL, 0);
                        WCHAR * pstr = new WCHAR;
                        MultiByteToWideChar(CP_ACP, NULL, buf, strlen(buf), pstr, num);
                        pstr = L'\0';
                        utf8Num = WideCharToMultiByte(CP_UTF8, NULL, pstr, wcslen(pstr), NULL, 0, NULL, NULL);
                        char * str2 = new char;
                        WideCharToMultiByte(CP_UTF8, NULL, pstr, wcslen(pstr), str2, utf8Num, NULL, NULL);
                        str2 = '\0';

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

                        }

                        break;
                }

        }//if (nCode == HC_ACTION)

        return 0;
}

LRESULT CALLBACK ChineseCharHookProc( int nCode, WPARAM wParam, LPARAM lParam )
{
        if(nCode < 0)
                return CallNextHookEx(Chinese_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;
                                }

                        case VK_SPACE:
                                {
                                        CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Space);
                                        CEGUI::System::getSingleton().injectChar((CEGUI::utf32)msg->wParam);
                                        break;
                                }
                        default:
                                {
                                        //IMMFollow();//窗口跟随,暂时还没有实现
                                        ChnInjectChar((CEGUI::utf32)msg->wParam);
                                        return true;
                                }
                                break;
                        }
                }
        }

        return CallNextHookEx(Chinese_hHook,nCode,wParam,lParam);
}

bool ChnInjectChar( CEGUI::utf32 code_point )
{
#ifndef UNICODE
        static char s_tempChar = "";
        static wchar_t s_tempWchar = L"";
        static bool s_flag = false;
        unsigned char uch = (unsigned char)code_point;
        if( uch >= 0x80 )
        {
                if( !s_flag )
                {
                        s_tempChar = (char)uch; //第二个字节
                        s_flag = true;
                        return true;
                }
                else if( uch >= 0x80 )
                {
                        s_tempChar = (char)uch; //第一个字节
                        s_flag = false;
                        MultiByteToWideChar( 0, 0, s_tempChar, 2, s_tempWchar, 1); //转成宽字节
                        s_tempWchar = L'\0';
                        CEGUI::utf32 code = (CEGUI::utf32)s_tempWchar;
                        return CEGUI::System::getSingleton().injectChar( code );
                }
                else
                {
                        return CEGUI::System::getSingleton().injectChar(code_point);
                }
        }
        else
        {
                s_flag = false;
                return CEGUI::System::getSingleton().injectChar(code_point);
        }
#else
        return CEGUI::System::getSingleton().injectChar(code_point );
#endif
}

liuzhiyu123 发表于 2011-5-16 09:52:54

array 发表于 2011-5-17 08:26:40

Very cool~~

tianxiao888 发表于 2011-5-18 10:11:26

非常棒的分享啊

wangxiao358 发表于 2011-6-8 10:12:29

感谢 楼主 分享:lol

wangxiao358 发表于 2011-6-8 10:12:40

感谢 楼主 分享:lol

wangxiao358 发表于 2011-6-8 10:12:53

感谢 楼主 分享:lol

zzq11105229 发表于 2011-6-21 16:57:09

请问一下怎么切换到中文输入法呢。在osg的窗口中,按下Ctrl+Shift没有反应,输入法的窗口都没有。

liuzhiyu123 发表于 2011-6-22 07:45:39

回复 8# zzq11105229

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

zzq11105229 发表于 2011-6-22 10:35:56

回复 9# liuzhiyu123

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

zzq11105229 发表于 2011-6-23 10:54:47

谢谢,问题解决了。可以切换输入法,只是消息处理不及时,看起来就没反应了。

WLLoveOSG 发表于 2011-9-21 20:33:18

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

liuzhiyu123 发表于 2012-7-11 15:55:09

:lol

木子匕 发表于 2012-7-19 17:13:54

马克一下!好东西,不过现在有些地方还看不懂!继续学习

w910916 发表于 2012-11-2 17:11:59

太好了真心有用只是我想问问 MultiEditBox这个如何显示中文呢 动态显示 在代码里用setText(字符串)该如何显示呢?字符串是未知大小的 从数据库里读取

w910916 发表于 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) 中被引用

w910916 发表于 2013-5-17 16:20:00

已经找到 谢谢 #pragma comment(lib,"imm32.lib")

liuzhiyu123 发表于 2013-5-17 16:27:23

w910916 发表于 2013-5-17 16:15 static/image/common/back.gif
亲 Imm.h需要什么库啊?我在控制台下有这个错误 error LNK2019: 无法解析的外部符号 _ImmGetCandidateListA ...

输入法的接口    imm32.lib

w910916 发表于 2013-5-18 21:42:52

亲 您这完善了么?有很多bug

w910916 发表于 2013-5-18 21:43:11

由于没有用过输入法的这个库 :'(

liuzhiyu123 发表于 2013-5-20 07:57:19

w910916 发表于 2013-5-18 21:42 static/image/common/back.gif
亲 您这完善了么?有很多bug

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

liuzhiyu123 发表于 2013-5-20 07:57:52

w910916 发表于 2013-5-18 21:43 static/image/common/back.gif
由于没有用过输入法的这个库

输入法 编程 网上的资料很多 您可以自己查找
页: [1]
查看完整版本: OSG中使用CEGUI时的中文输入问题的解决方法