cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_windowskeyboard.c (51537B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#if SDL_VIDEO_DRIVER_WINDOWS
     24
     25#include "SDL_windowsvideo.h"
     26
     27#include "../../events/SDL_keyboard_c.h"
     28#include "../../events/scancodes_windows.h"
     29
     30#include <imm.h>
     31#include <oleauto.h>
     32
     33#ifndef SDL_DISABLE_WINDOWS_IME
     34static void IME_Init(SDL_VideoData *videodata, HWND hwnd);
     35static void IME_Enable(SDL_VideoData *videodata, HWND hwnd);
     36static void IME_Disable(SDL_VideoData *videodata, HWND hwnd);
     37static void IME_Quit(SDL_VideoData *videodata);
     38#endif /* !SDL_DISABLE_WINDOWS_IME */
     39
     40#ifndef MAPVK_VK_TO_VSC
     41#define MAPVK_VK_TO_VSC     0
     42#endif
     43#ifndef MAPVK_VSC_TO_VK
     44#define MAPVK_VSC_TO_VK     1
     45#endif
     46#ifndef MAPVK_VK_TO_CHAR
     47#define MAPVK_VK_TO_CHAR    2
     48#endif
     49
     50/* Alphabetic scancodes for PC keyboards */
     51void
     52WIN_InitKeyboard(_THIS)
     53{
     54    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     55
     56    data->ime_com_initialized = SDL_FALSE;
     57    data->ime_threadmgr = 0;
     58    data->ime_initialized = SDL_FALSE;
     59    data->ime_enabled = SDL_FALSE;
     60    data->ime_available = SDL_FALSE;
     61    data->ime_hwnd_main = 0;
     62    data->ime_hwnd_current = 0;
     63    data->ime_himc = 0;
     64    data->ime_composition[0] = 0;
     65    data->ime_readingstring[0] = 0;
     66    data->ime_cursor = 0;
     67
     68    data->ime_candlist = SDL_FALSE;
     69    SDL_memset(data->ime_candidates, 0, sizeof(data->ime_candidates));
     70    data->ime_candcount = 0;
     71    data->ime_candref = 0;
     72    data->ime_candsel = 0;
     73    data->ime_candpgsize = 0;
     74    data->ime_candlistindexbase = 0;
     75    data->ime_candvertical = SDL_TRUE;
     76
     77    data->ime_dirty = SDL_FALSE;
     78    SDL_memset(&data->ime_rect, 0, sizeof(data->ime_rect));
     79    SDL_memset(&data->ime_candlistrect, 0, sizeof(data->ime_candlistrect));
     80    data->ime_winwidth = 0;
     81    data->ime_winheight = 0;
     82
     83    data->ime_hkl = 0;
     84    data->ime_himm32 = 0;
     85    data->GetReadingString = 0;
     86    data->ShowReadingWindow = 0;
     87    data->ImmLockIMC = 0;
     88    data->ImmUnlockIMC = 0;
     89    data->ImmLockIMCC = 0;
     90    data->ImmUnlockIMCC = 0;
     91    data->ime_uiless = SDL_FALSE;
     92    data->ime_threadmgrex = 0;
     93    data->ime_uielemsinkcookie = TF_INVALID_COOKIE;
     94    data->ime_alpnsinkcookie = TF_INVALID_COOKIE;
     95    data->ime_openmodesinkcookie = TF_INVALID_COOKIE;
     96    data->ime_convmodesinkcookie = TF_INVALID_COOKIE;
     97    data->ime_uielemsink = 0;
     98    data->ime_ippasink = 0;
     99
    100    WIN_UpdateKeymap();
    101
    102    SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
    103    SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
    104    SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows");
    105}
    106
    107void
    108WIN_UpdateKeymap()
    109{
    110    int i;
    111    SDL_Scancode scancode;
    112    SDL_Keycode keymap[SDL_NUM_SCANCODES];
    113
    114    SDL_GetDefaultKeymap(keymap);
    115
    116    for (i = 0; i < SDL_arraysize(windows_scancode_table); i++) {
    117        int vk;
    118        /* Make sure this scancode is a valid character scancode */
    119        scancode = windows_scancode_table[i];
    120        if (scancode == SDL_SCANCODE_UNKNOWN ) {
    121            continue;
    122        }
    123
    124        /* If this key is one of the non-mappable keys, ignore it */
    125        /* Don't allow the number keys right above the qwerty row to translate or the top left key (grave/backquote) */
    126        /* Not mapping numbers fixes the French layout, giving numeric keycodes for the number keys, which is the expected behavior */
    127        if ((keymap[scancode] & SDLK_SCANCODE_MASK) ||
    128            scancode == SDL_SCANCODE_GRAVE ||
    129            (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) ) {
    130            continue;
    131        }
    132
    133        vk =  MapVirtualKey(i, MAPVK_VSC_TO_VK);
    134        if ( vk ) {
    135            int ch = (MapVirtualKey( vk, MAPVK_VK_TO_CHAR ) & 0x7FFF);
    136            if ( ch ) {
    137                if ( ch >= 'A' && ch <= 'Z' ) {
    138                    keymap[scancode] =  SDLK_a + ( ch - 'A' );
    139                } else {
    140                    keymap[scancode] = ch;
    141                }
    142            }
    143        }
    144    }
    145
    146    SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
    147}
    148
    149void
    150WIN_QuitKeyboard(_THIS)
    151{
    152#ifndef SDL_DISABLE_WINDOWS_IME
    153    IME_Quit((SDL_VideoData *)_this->driverdata);
    154#endif
    155}
    156
    157void
    158WIN_StartTextInput(_THIS)
    159{
    160#ifndef SDL_DISABLE_WINDOWS_IME
    161    SDL_Window *window = SDL_GetKeyboardFocus();
    162    if (window) {
    163        HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    164        SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
    165        SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
    166        IME_Init(videodata, hwnd);
    167        IME_Enable(videodata, hwnd);
    168    }
    169#endif /* !SDL_DISABLE_WINDOWS_IME */
    170}
    171
    172void
    173WIN_StopTextInput(_THIS)
    174{
    175#ifndef SDL_DISABLE_WINDOWS_IME
    176    SDL_Window *window = SDL_GetKeyboardFocus();
    177    if (window) {
    178        HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    179        SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
    180        IME_Init(videodata, hwnd);
    181        IME_Disable(videodata, hwnd);
    182    }
    183#endif /* !SDL_DISABLE_WINDOWS_IME */
    184}
    185
    186void
    187WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
    188{
    189    SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
    190
    191    if (!rect) {
    192        SDL_InvalidParamError("rect");
    193        return;
    194    }
    195
    196    videodata->ime_rect = *rect;
    197}
    198
    199#ifdef SDL_DISABLE_WINDOWS_IME
    200
    201
    202SDL_bool
    203IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
    204{
    205    return SDL_FALSE;
    206}
    207
    208void IME_Present(SDL_VideoData *videodata)
    209{
    210}
    211
    212#else
    213
    214#ifdef __GNUC__
    215#undef DEFINE_GUID
    216#define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
    217DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink,        0x71C6E74E,0x0F28,0x11D8,0xA8,0x2A,0x00,0x06,0x5B,0x84,0x43,0x5C);
    218DEFINE_GUID(IID_ITfUIElementSink,                              0xEA1EA136,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    219DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,                           0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
    220DEFINE_GUID(IID_ITfSource,                                     0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
    221DEFINE_GUID(IID_ITfUIElementMgr,                               0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    222DEFINE_GUID(IID_ITfCandidateListUIElement,                     0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    223DEFINE_GUID(IID_ITfReadingInformationUIElement,                0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    224DEFINE_GUID(IID_ITfThreadMgr,                                  0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
    225DEFINE_GUID(CLSID_TF_ThreadMgr,                                0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
    226DEFINE_GUID(IID_ITfThreadMgrEx,                                0x3E90ADE3,0x7594,0x4CB0,0xBB,0x58,0x69,0x62,0x8F,0x5F,0x45,0x8C);
    227#endif
    228
    229#define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
    230#define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
    231
    232#define MAKEIMEVERSION(major,minor) ((DWORD) (((BYTE)(major) << 24) | ((BYTE)(minor) << 16) ))
    233#define IMEID_VER(id) ((id) & 0xffff0000)
    234#define IMEID_LANG(id) ((id) & 0x0000ffff)
    235
    236#define CHT_HKL_DAYI            ((HKL)0xE0060404)
    237#define CHT_HKL_NEW_PHONETIC    ((HKL)0xE0080404)
    238#define CHT_HKL_NEW_CHANG_JIE   ((HKL)0xE0090404)
    239#define CHT_HKL_NEW_QUICK       ((HKL)0xE00A0404)
    240#define CHT_HKL_HK_CANTONESE    ((HKL)0xE00B0404)
    241#define CHT_IMEFILENAME1        "TINTLGNT.IME"
    242#define CHT_IMEFILENAME2        "CINTLGNT.IME"
    243#define CHT_IMEFILENAME3        "MSTCIPHA.IME"
    244#define IMEID_CHT_VER42         (LANG_CHT | MAKEIMEVERSION(4, 2))
    245#define IMEID_CHT_VER43         (LANG_CHT | MAKEIMEVERSION(4, 3))
    246#define IMEID_CHT_VER44         (LANG_CHT | MAKEIMEVERSION(4, 4))
    247#define IMEID_CHT_VER50         (LANG_CHT | MAKEIMEVERSION(5, 0))
    248#define IMEID_CHT_VER51         (LANG_CHT | MAKEIMEVERSION(5, 1))
    249#define IMEID_CHT_VER52         (LANG_CHT | MAKEIMEVERSION(5, 2))
    250#define IMEID_CHT_VER60         (LANG_CHT | MAKEIMEVERSION(6, 0))
    251#define IMEID_CHT_VER_VISTA     (LANG_CHT | MAKEIMEVERSION(7, 0))
    252
    253#define CHS_HKL                 ((HKL)0xE00E0804)
    254#define CHS_IMEFILENAME1        "PINTLGNT.IME"
    255#define CHS_IMEFILENAME2        "MSSCIPYA.IME"
    256#define IMEID_CHS_VER41         (LANG_CHS | MAKEIMEVERSION(4, 1))
    257#define IMEID_CHS_VER42         (LANG_CHS | MAKEIMEVERSION(4, 2))
    258#define IMEID_CHS_VER53         (LANG_CHS | MAKEIMEVERSION(5, 3))
    259
    260#define LANG() LOWORD((videodata->ime_hkl))
    261#define PRIMLANG() ((WORD)PRIMARYLANGID(LANG()))
    262#define SUBLANG() SUBLANGID(LANG())
    263
    264static void IME_UpdateInputLocale(SDL_VideoData *videodata);
    265static void IME_ClearComposition(SDL_VideoData *videodata);
    266static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd);
    267static void IME_SetupAPI(SDL_VideoData *videodata);
    268static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
    269static void IME_SendEditingEvent(SDL_VideoData *videodata);
    270static void IME_DestroyTextures(SDL_VideoData *videodata);
    271
    272#define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
    273#define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
    274
    275static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
    276static void UILess_ReleaseSinks(SDL_VideoData *videodata);
    277static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
    278static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
    279
    280static void
    281IME_Init(SDL_VideoData *videodata, HWND hwnd)
    282{
    283    if (videodata->ime_initialized)
    284        return;
    285
    286    videodata->ime_hwnd_main = hwnd;
    287    if (SUCCEEDED(WIN_CoInitialize())) {
    288        videodata->ime_com_initialized = SDL_TRUE;
    289        CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
    290    }
    291    videodata->ime_initialized = SDL_TRUE;
    292    videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
    293    if (!videodata->ime_himm32) {
    294        videodata->ime_available = SDL_FALSE;
    295        return;
    296    }
    297    videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC");
    298    videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC");
    299    videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC");
    300    videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC");
    301
    302    IME_SetWindow(videodata, hwnd);
    303    videodata->ime_himc = ImmGetContext(hwnd);
    304    ImmReleaseContext(hwnd, videodata->ime_himc);
    305    if (!videodata->ime_himc) {
    306        videodata->ime_available = SDL_FALSE;
    307        IME_Disable(videodata, hwnd);
    308        return;
    309    }
    310    videodata->ime_available = SDL_TRUE;
    311    IME_UpdateInputLocale(videodata);
    312    IME_SetupAPI(videodata);
    313    videodata->ime_uiless = UILess_SetupSinks(videodata);
    314    IME_UpdateInputLocale(videodata);
    315    IME_Disable(videodata, hwnd);
    316}
    317
    318static void
    319IME_Enable(SDL_VideoData *videodata, HWND hwnd)
    320{
    321    if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
    322        return;
    323
    324    if (!videodata->ime_available) {
    325        IME_Disable(videodata, hwnd);
    326        return;
    327    }
    328    if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
    329        ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc);
    330
    331    videodata->ime_enabled = SDL_TRUE;
    332    IME_UpdateInputLocale(videodata);
    333    UILess_EnableUIUpdates(videodata);
    334}
    335
    336static void
    337IME_Disable(SDL_VideoData *videodata, HWND hwnd)
    338{
    339    if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
    340        return;
    341
    342    IME_ClearComposition(videodata);
    343    if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
    344        ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0);
    345
    346    videodata->ime_enabled = SDL_FALSE;
    347    UILess_DisableUIUpdates(videodata);
    348}
    349
    350static void
    351IME_Quit(SDL_VideoData *videodata)
    352{
    353    if (!videodata->ime_initialized)
    354        return;
    355
    356    UILess_ReleaseSinks(videodata);
    357    if (videodata->ime_hwnd_main)
    358        ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
    359
    360    videodata->ime_hwnd_main = 0;
    361    videodata->ime_himc = 0;
    362    if (videodata->ime_himm32) {
    363        SDL_UnloadObject(videodata->ime_himm32);
    364        videodata->ime_himm32 = 0;
    365    }
    366    if (videodata->ime_threadmgr) {
    367        videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
    368        videodata->ime_threadmgr = 0;
    369    }
    370    if (videodata->ime_com_initialized) {
    371        WIN_CoUninitialize();
    372        videodata->ime_com_initialized = SDL_FALSE;
    373    }
    374    IME_DestroyTextures(videodata);
    375    videodata->ime_initialized = SDL_FALSE;
    376}
    377
    378static void
    379IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
    380{
    381    DWORD id = 0;
    382    HIMC himc = 0;
    383    WCHAR buffer[16];
    384    WCHAR *s = buffer;
    385    DWORD len = 0;
    386    INT err = 0;
    387    BOOL vertical = FALSE;
    388    UINT maxuilen = 0;
    389	static OSVERSIONINFOA osversion;
    390
    391    if (videodata->ime_uiless)
    392        return;
    393
    394    videodata->ime_readingstring[0] = 0;
    395    if (!osversion.dwOSVersionInfoSize) {
    396        osversion.dwOSVersionInfoSize = sizeof(osversion);
    397        GetVersionExA(&osversion);
    398    }
    399    id = IME_GetId(videodata, 0);
    400    if (!id)
    401        return;
    402
    403    himc = ImmGetContext(hwnd);
    404    if (!himc)
    405        return;
    406
    407    if (videodata->GetReadingString) {
    408        len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen);
    409        if (len) {
    410            if (len > SDL_arraysize(buffer))
    411                len = SDL_arraysize(buffer);
    412
    413            len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen);
    414        }
    415        SDL_wcslcpy(videodata->ime_readingstring, s, len);
    416    }
    417    else {
    418        LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc);
    419        LPBYTE p = 0;
    420        s = 0;
    421        switch (id)
    422        {
    423        case IMEID_CHT_VER42:
    424        case IMEID_CHT_VER43:
    425        case IMEID_CHT_VER44:
    426            p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24);
    427            if (!p)
    428                break;
    429
    430            len = *(DWORD *)(p + 7*4 + 32*4);
    431            s = (WCHAR *)(p + 56);
    432            break;
    433        case IMEID_CHT_VER51:
    434        case IMEID_CHT_VER52:
    435        case IMEID_CHS_VER53:
    436            p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4);
    437            if (!p)
    438                break;
    439
    440            p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
    441            if (!p)
    442                break;
    443
    444            len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
    445            s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
    446            break;
    447        case IMEID_CHS_VER41:
    448            {
    449                int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7;
    450                p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4);
    451                if (!p)
    452                    break;
    453
    454                len = *(DWORD *)(p + 7*4 + 16*2*4);
    455                s = (WCHAR *)(p + 6*4 + 16*2*1);
    456            }
    457            break;
    458        case IMEID_CHS_VER42:
    459            if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT)
    460                break;
    461
    462            p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4);
    463            if (!p)
    464                break;
    465
    466            len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
    467            s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
    468            break;
    469        }
    470        if (s) {
    471            size_t size = SDL_min((size_t)(len + 1), SDL_arraysize(videodata->ime_readingstring));
    472            SDL_wcslcpy(videodata->ime_readingstring, s, size);
    473        }
    474
    475        videodata->ImmUnlockIMCC(lpimc->hPrivate);
    476        videodata->ImmUnlockIMC(himc);
    477    }
    478    ImmReleaseContext(hwnd, himc);
    479    IME_SendEditingEvent(videodata);
    480}
    481
    482static void
    483IME_InputLangChanged(SDL_VideoData *videodata)
    484{
    485    UINT lang = PRIMLANG();
    486    IME_UpdateInputLocale(videodata);
    487    if (!videodata->ime_uiless)
    488        videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
    489
    490    IME_SetupAPI(videodata);
    491    if (lang != PRIMLANG()) {
    492        IME_ClearComposition(videodata);
    493    }
    494}
    495
    496static DWORD
    497IME_GetId(SDL_VideoData *videodata, UINT uIndex)
    498{
    499    static HKL hklprev = 0;
    500    static DWORD dwRet[2] = {0};
    501    DWORD dwVerSize = 0;
    502    DWORD dwVerHandle = 0;
    503    LPVOID lpVerBuffer = 0;
    504    LPVOID lpVerData = 0;
    505    UINT cbVerData = 0;
    506    char szTemp[256];
    507    HKL hkl = 0;
    508    DWORD dwLang = 0;
    509    if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0]))
    510        return 0;
    511
    512    hkl = videodata->ime_hkl;
    513    if (hklprev == hkl)
    514        return dwRet[uIndex];
    515
    516    hklprev = hkl;
    517    dwLang = ((DWORD_PTR)hkl & 0xffff);
    518    if (videodata->ime_uiless && LANG() == LANG_CHT) {
    519        dwRet[0] = IMEID_CHT_VER_VISTA;
    520        dwRet[1] = 0;
    521        return dwRet[0];
    522    }
    523    if (hkl != CHT_HKL_NEW_PHONETIC
    524        && hkl != CHT_HKL_NEW_CHANG_JIE
    525        && hkl != CHT_HKL_NEW_QUICK
    526        && hkl != CHT_HKL_HK_CANTONESE
    527        && hkl != CHS_HKL) {
    528        dwRet[0] = dwRet[1] = 0;
    529        return dwRet[uIndex];
    530    }
    531    if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) {
    532        dwRet[0] = dwRet[1] = 0;
    533        return dwRet[uIndex];
    534    }
    535    if (!videodata->GetReadingString) {
    536        #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
    537        if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2
    538            && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2
    539            && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2
    540            && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2
    541            && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) {
    542            dwRet[0] = dwRet[1] = 0;
    543            return dwRet[uIndex];
    544        }
    545        #undef LCID_INVARIANT
    546        dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle);
    547        if (dwVerSize) {
    548            lpVerBuffer = SDL_malloc(dwVerSize);
    549            if (lpVerBuffer) {
    550                if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) {
    551                    if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) {
    552                        #define pVerFixedInfo   ((VS_FIXEDFILEINFO FAR*)lpVerData)
    553                        DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
    554                        dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
    555                        if ((videodata->GetReadingString) ||
    556                            ((dwLang == LANG_CHT) && (
    557                            dwVer == MAKEIMEVERSION(4, 2) ||
    558                            dwVer == MAKEIMEVERSION(4, 3) ||
    559                            dwVer == MAKEIMEVERSION(4, 4) ||
    560                            dwVer == MAKEIMEVERSION(5, 0) ||
    561                            dwVer == MAKEIMEVERSION(5, 1) ||
    562                            dwVer == MAKEIMEVERSION(5, 2) ||
    563                            dwVer == MAKEIMEVERSION(6, 0)))
    564                            ||
    565                            ((dwLang == LANG_CHS) && (
    566                            dwVer == MAKEIMEVERSION(4, 1) ||
    567                            dwVer == MAKEIMEVERSION(4, 2) ||
    568                            dwVer == MAKEIMEVERSION(5, 3)))) {
    569                            dwRet[0] = dwVer | dwLang;
    570                            dwRet[1] = pVerFixedInfo->dwFileVersionLS;
    571                            SDL_free(lpVerBuffer);
    572                            return dwRet[0];
    573                        }
    574                        #undef pVerFixedInfo
    575                    }
    576                }
    577            }
    578            SDL_free(lpVerBuffer);
    579        }
    580    }
    581    dwRet[0] = dwRet[1] = 0;
    582    return dwRet[uIndex];
    583}
    584
    585static void
    586IME_SetupAPI(SDL_VideoData *videodata)
    587{
    588    char ime_file[MAX_PATH + 1];
    589    void* hime = 0;
    590    HKL hkl = 0;
    591    videodata->GetReadingString = 0;
    592    videodata->ShowReadingWindow = 0;
    593    if (videodata->ime_uiless)
    594        return;
    595
    596    hkl = videodata->ime_hkl;
    597    if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0)
    598        return;
    599
    600    hime = SDL_LoadObject(ime_file);
    601    if (!hime)
    602        return;
    603
    604    videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
    605        SDL_LoadFunction(hime, "GetReadingString");
    606    videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL))
    607        SDL_LoadFunction(hime, "ShowReadingWindow");
    608
    609    if (videodata->ShowReadingWindow) {
    610        HIMC himc = ImmGetContext(videodata->ime_hwnd_current);
    611        if (himc) {
    612            videodata->ShowReadingWindow(himc, FALSE);
    613            ImmReleaseContext(videodata->ime_hwnd_current, himc);
    614        }
    615    }
    616}
    617
    618static void
    619IME_SetWindow(SDL_VideoData* videodata, HWND hwnd)
    620{
    621    videodata->ime_hwnd_current = hwnd;
    622    if (videodata->ime_threadmgr) {
    623        struct ITfDocumentMgr *document_mgr = 0;
    624        if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
    625            if (document_mgr)
    626                document_mgr->lpVtbl->Release(document_mgr);
    627        }
    628    }
    629}
    630
    631static void
    632IME_UpdateInputLocale(SDL_VideoData *videodata)
    633{
    634    static HKL hklprev = 0;
    635    videodata->ime_hkl = GetKeyboardLayout(0);
    636    if (hklprev == videodata->ime_hkl)
    637        return;
    638
    639    hklprev = videodata->ime_hkl;
    640    switch (PRIMLANG()) {
    641    case LANG_CHINESE:
    642        videodata->ime_candvertical = SDL_TRUE;
    643        if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
    644            videodata->ime_candvertical = SDL_FALSE;
    645
    646        break;
    647    case LANG_JAPANESE:
    648        videodata->ime_candvertical = SDL_TRUE;
    649        break;
    650    case LANG_KOREAN:
    651        videodata->ime_candvertical = SDL_FALSE;
    652        break;
    653    }
    654}
    655
    656static void
    657IME_ClearComposition(SDL_VideoData *videodata)
    658{
    659    HIMC himc = 0;
    660    if (!videodata->ime_initialized)
    661        return;
    662
    663    himc = ImmGetContext(videodata->ime_hwnd_current);
    664    if (!himc)
    665        return;
    666
    667    ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
    668    if (videodata->ime_uiless)
    669        ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
    670
    671    ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
    672    ImmReleaseContext(videodata->ime_hwnd_current, himc);
    673    SDL_SendEditingText("", 0, 0);
    674}
    675
    676static void
    677IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
    678{
    679    LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition) - sizeof(videodata->ime_composition[0]));
    680    if (length < 0)
    681        length = 0;
    682
    683    length /= sizeof(videodata->ime_composition[0]);
    684    videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0));
    685    if (videodata->ime_cursor < SDL_arraysize(videodata->ime_composition) && videodata->ime_composition[videodata->ime_cursor] == 0x3000) {
    686        int i;
    687        for (i = videodata->ime_cursor + 1; i < length; ++i)
    688            videodata->ime_composition[i - 1] = videodata->ime_composition[i];
    689
    690        --length;
    691    }
    692    videodata->ime_composition[length] = 0;
    693}
    694
    695static void
    696IME_SendInputEvent(SDL_VideoData *videodata)
    697{
    698    char *s = 0;
    699    s = WIN_StringToUTF8(videodata->ime_composition);
    700    SDL_SendKeyboardText(s);
    701    SDL_free(s);
    702
    703    videodata->ime_composition[0] = 0;
    704    videodata->ime_readingstring[0] = 0;
    705    videodata->ime_cursor = 0;
    706}
    707
    708static void
    709IME_SendEditingEvent(SDL_VideoData *videodata)
    710{
    711    char *s = 0;
    712    WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
    713    const size_t size = SDL_arraysize(buffer);
    714    buffer[0] = 0;
    715    if (videodata->ime_readingstring[0]) {
    716        size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor);
    717        SDL_wcslcpy(buffer, videodata->ime_composition, len + 1);
    718        SDL_wcslcat(buffer, videodata->ime_readingstring, size);
    719        SDL_wcslcat(buffer, &videodata->ime_composition[len], size);
    720    }
    721    else {
    722        SDL_wcslcpy(buffer, videodata->ime_composition, size);
    723    }
    724    s = WIN_StringToUTF8(buffer);
    725    SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0);
    726    SDL_free(s);
    727}
    728
    729static void
    730IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
    731{
    732    LPWSTR dst = videodata->ime_candidates[i];
    733    *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
    734    if (videodata->ime_candvertical)
    735        *dst++ = TEXT(' ');
    736
    737    while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
    738        *dst++ = *candidate++;
    739
    740    *dst = (WCHAR)'\0';
    741}
    742
    743static void
    744IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
    745{
    746    LPCANDIDATELIST cand_list = 0;
    747    DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
    748    if (size) {
    749        cand_list = (LPCANDIDATELIST)SDL_malloc(size);
    750        if (cand_list) {
    751            size = ImmGetCandidateListW(himc, 0, cand_list, size);
    752            if (size) {
    753                UINT i, j;
    754                UINT page_start = 0;
    755                videodata->ime_candsel = cand_list->dwSelection;
    756                videodata->ime_candcount = cand_list->dwCount;
    757
    758                if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
    759                    const UINT maxcandchar = 18;
    760                    size_t cchars = 0;
    761
    762                    for (i = 0; i < videodata->ime_candcount; ++i) {
    763                        size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1;
    764                        if (len + cchars > maxcandchar) {
    765                            if (i > cand_list->dwSelection)
    766                                break;
    767
    768                            page_start = i;
    769                            cchars = len;
    770                        }
    771                        else {
    772                            cchars += len;
    773                        }
    774                    }
    775                    videodata->ime_candpgsize = i - page_start;
    776                } else {
    777                    videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
    778                    page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
    779                }
    780                SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
    781                for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++) {
    782                    LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
    783                    IME_AddCandidate(videodata, j, candidate);
    784                }
    785                if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
    786                    videodata->ime_candsel = -1;
    787
    788            }
    789            SDL_free(cand_list);
    790        }
    791    }
    792}
    793
    794static void
    795IME_ShowCandidateList(SDL_VideoData *videodata)
    796{
    797    videodata->ime_dirty = SDL_TRUE;
    798    videodata->ime_candlist = SDL_TRUE;
    799    IME_DestroyTextures(videodata);
    800    IME_SendEditingEvent(videodata);
    801}
    802
    803static void
    804IME_HideCandidateList(SDL_VideoData *videodata)
    805{
    806    videodata->ime_dirty = SDL_FALSE;
    807    videodata->ime_candlist = SDL_FALSE;
    808    IME_DestroyTextures(videodata);
    809    IME_SendEditingEvent(videodata);
    810}
    811
    812SDL_bool
    813IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
    814{
    815    SDL_bool trap = SDL_FALSE;
    816    HIMC himc = 0;
    817    if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled)
    818        return SDL_FALSE;
    819
    820    switch (msg) {
    821    case WM_INPUTLANGCHANGE:
    822        IME_InputLangChanged(videodata);
    823        break;
    824    case WM_IME_SETCONTEXT:
    825        *lParam = 0;
    826        break;
    827    case WM_IME_STARTCOMPOSITION:
    828        trap = SDL_TRUE;
    829        break;
    830    case WM_IME_COMPOSITION:
    831        trap = SDL_TRUE;
    832        himc = ImmGetContext(hwnd);
    833        if (*lParam & GCS_RESULTSTR) {
    834            IME_GetCompositionString(videodata, himc, GCS_RESULTSTR);
    835            IME_SendInputEvent(videodata);
    836        }
    837        if (*lParam & GCS_COMPSTR) {
    838            if (!videodata->ime_uiless)
    839                videodata->ime_readingstring[0] = 0;
    840
    841            IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
    842            IME_SendEditingEvent(videodata);
    843        }
    844        ImmReleaseContext(hwnd, himc);
    845        break;
    846    case WM_IME_ENDCOMPOSITION:
    847        videodata->ime_composition[0] = 0;
    848        videodata->ime_readingstring[0] = 0;
    849        videodata->ime_cursor = 0;
    850        SDL_SendEditingText("", 0, 0);
    851        break;
    852    case WM_IME_NOTIFY:
    853        switch (wParam) {
    854        case IMN_SETCONVERSIONMODE:
    855        case IMN_SETOPENSTATUS:
    856            IME_UpdateInputLocale(videodata);
    857            break;
    858        case IMN_OPENCANDIDATE:
    859        case IMN_CHANGECANDIDATE:
    860            if (videodata->ime_uiless)
    861                break;
    862
    863            trap = SDL_TRUE;
    864            IME_ShowCandidateList(videodata);
    865            himc = ImmGetContext(hwnd);
    866            if (!himc)
    867                break;
    868
    869            IME_GetCandidateList(himc, videodata);
    870            ImmReleaseContext(hwnd, himc);
    871            break;
    872        case IMN_CLOSECANDIDATE:
    873            trap = SDL_TRUE;
    874            IME_HideCandidateList(videodata);
    875            break;
    876        case IMN_PRIVATE:
    877            {
    878                DWORD dwId = IME_GetId(videodata, 0);
    879                IME_GetReadingString(videodata, hwnd);
    880                switch (dwId)
    881                {
    882                case IMEID_CHT_VER42:
    883                case IMEID_CHT_VER43:
    884                case IMEID_CHT_VER44:
    885                case IMEID_CHS_VER41:
    886                case IMEID_CHS_VER42:
    887                    if (*lParam == 1 || *lParam == 2)
    888                        trap = SDL_TRUE;
    889
    890                    break;
    891                case IMEID_CHT_VER50:
    892                case IMEID_CHT_VER51:
    893                case IMEID_CHT_VER52:
    894                case IMEID_CHT_VER60:
    895                case IMEID_CHS_VER53:
    896                    if (*lParam == 16
    897                        || *lParam == 17
    898                        || *lParam == 26
    899                        || *lParam == 27
    900                        || *lParam == 28)
    901                        trap = SDL_TRUE;
    902                    break;
    903                }
    904            }
    905            break;
    906        default:
    907            trap = SDL_TRUE;
    908            break;
    909        }
    910        break;
    911    }
    912    return trap;
    913}
    914
    915static void
    916IME_CloseCandidateList(SDL_VideoData *videodata)
    917{
    918    IME_HideCandidateList(videodata);
    919    videodata->ime_candcount = 0;
    920    SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
    921}
    922
    923static void
    924UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
    925{
    926    UINT selection = 0;
    927    UINT count = 0;
    928    UINT page = 0;
    929    UINT pgcount = 0;
    930    DWORD pgstart = 0;
    931    DWORD pgsize = 0;
    932    UINT i, j;
    933    pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
    934    pcandlist->lpVtbl->GetCount(pcandlist, &count);
    935    pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
    936
    937    videodata->ime_candsel = selection;
    938    videodata->ime_candcount = count;
    939    IME_ShowCandidateList(videodata);
    940
    941    pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
    942    if (pgcount > 0) {
    943        UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
    944        if (idxlist) {
    945            pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
    946            pgstart = idxlist[page];
    947            if (page < pgcount - 1)
    948                pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
    949            else
    950                pgsize = count - pgstart;
    951
    952            SDL_free(idxlist);
    953        }
    954    }
    955    videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
    956    videodata->ime_candsel = videodata->ime_candsel - pgstart;
    957
    958    SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
    959    for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++) {
    960        BSTR bstr;
    961        if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
    962            if (bstr) {
    963                IME_AddCandidate(videodata, j, bstr);
    964                SysFreeString(bstr);
    965            }
    966        }
    967    }
    968    if (PRIMLANG() == LANG_KOREAN)
    969        videodata->ime_candsel = -1;
    970}
    971
    972STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
    973{
    974    return ++sink->refcount;
    975}
    976
    977STDMETHODIMP_(ULONG)TSFSink_Release(TSFSink *sink)
    978{
    979    --sink->refcount;
    980    if (sink->refcount == 0) {
    981        SDL_free(sink);
    982        return 0;
    983    }
    984    return sink->refcount;
    985}
    986
    987STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
    988{
    989    if (!ppv)
    990        return E_INVALIDARG;
    991
    992    *ppv = 0;
    993    if (SDL_IsEqualIID(riid, &IID_IUnknown))
    994        *ppv = (IUnknown *)sink;
    995    else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink))
    996        *ppv = (ITfUIElementSink *)sink;
    997
    998    if (*ppv) {
    999        TSFSink_AddRef(sink);
   1000        return S_OK;
   1001    }
   1002    return E_NOINTERFACE;
   1003}
   1004
   1005ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
   1006{
   1007    ITfUIElementMgr *puiem = 0;
   1008    ITfUIElement *pelem = 0;
   1009    ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
   1010
   1011    if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
   1012        puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
   1013        puiem->lpVtbl->Release(puiem);
   1014    }
   1015    return pelem;
   1016}
   1017
   1018STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
   1019{
   1020    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   1021    ITfReadingInformationUIElement *preading = 0;
   1022    ITfCandidateListUIElement *pcandlist = 0;
   1023    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   1024    if (!element)
   1025        return E_INVALIDARG;
   1026
   1027    *pbShow = FALSE;
   1028    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
   1029        BSTR bstr;
   1030        if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
   1031            SysFreeString(bstr);
   1032        }
   1033        preading->lpVtbl->Release(preading);
   1034    }
   1035    else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
   1036        videodata->ime_candref++;
   1037        UILess_GetCandidateList(videodata, pcandlist);
   1038        pcandlist->lpVtbl->Release(pcandlist);
   1039    }
   1040    return S_OK;
   1041}
   1042
   1043STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
   1044{
   1045    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   1046    ITfReadingInformationUIElement *preading = 0;
   1047    ITfCandidateListUIElement *pcandlist = 0;
   1048    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   1049    if (!element)
   1050        return E_INVALIDARG;
   1051
   1052    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
   1053        BSTR bstr;
   1054        if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
   1055            WCHAR *s = (WCHAR *)bstr;
   1056            SDL_wcslcpy(videodata->ime_readingstring, s, SDL_arraysize(videodata->ime_readingstring));
   1057            IME_SendEditingEvent(videodata);
   1058            SysFreeString(bstr);
   1059        }
   1060        preading->lpVtbl->Release(preading);
   1061    }
   1062    else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
   1063        UILess_GetCandidateList(videodata, pcandlist);
   1064        pcandlist->lpVtbl->Release(pcandlist);
   1065    }
   1066    return S_OK;
   1067}
   1068
   1069STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
   1070{
   1071    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   1072    ITfReadingInformationUIElement *preading = 0;
   1073    ITfCandidateListUIElement *pcandlist = 0;
   1074    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   1075    if (!element)
   1076        return E_INVALIDARG;
   1077
   1078    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
   1079        videodata->ime_readingstring[0] = 0;
   1080        IME_SendEditingEvent(videodata);
   1081        preading->lpVtbl->Release(preading);
   1082    }
   1083    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
   1084        videodata->ime_candref--;
   1085        if (videodata->ime_candref == 0)
   1086            IME_CloseCandidateList(videodata);
   1087
   1088        pcandlist->lpVtbl->Release(pcandlist);
   1089    }
   1090    return S_OK;
   1091}
   1092
   1093STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
   1094{
   1095    if (!ppv)
   1096        return E_INVALIDARG;
   1097
   1098    *ppv = 0;
   1099    if (SDL_IsEqualIID(riid, &IID_IUnknown))
   1100        *ppv = (IUnknown *)sink;
   1101    else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink))
   1102        *ppv = (ITfInputProcessorProfileActivationSink *)sink;
   1103
   1104    if (*ppv) {
   1105        TSFSink_AddRef(sink);
   1106        return S_OK;
   1107    }
   1108    return E_NOINTERFACE;
   1109}
   1110
   1111STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
   1112{
   1113    static const GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
   1114    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   1115    videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
   1116    if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
   1117        IME_InputLangChanged((SDL_VideoData *)sink->data);
   1118
   1119    IME_HideCandidateList(videodata);
   1120    return S_OK;
   1121}
   1122
   1123static void *vtUIElementSink[] = {
   1124    (void *)(UIElementSink_QueryInterface),
   1125    (void *)(TSFSink_AddRef),
   1126    (void *)(TSFSink_Release),
   1127    (void *)(UIElementSink_BeginUIElement),
   1128    (void *)(UIElementSink_UpdateUIElement),
   1129    (void *)(UIElementSink_EndUIElement)
   1130};
   1131
   1132static void *vtIPPASink[] = {
   1133    (void *)(IPPASink_QueryInterface),
   1134    (void *)(TSFSink_AddRef),
   1135    (void *)(TSFSink_Release),
   1136    (void *)(IPPASink_OnActivated)
   1137};
   1138
   1139static void
   1140UILess_EnableUIUpdates(SDL_VideoData *videodata)
   1141{
   1142    ITfSource *source = 0;
   1143    if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE)
   1144        return;
   1145
   1146    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
   1147        source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
   1148        source->lpVtbl->Release(source);
   1149    }
   1150}
   1151
   1152static void
   1153UILess_DisableUIUpdates(SDL_VideoData *videodata)
   1154{
   1155    ITfSource *source = 0;
   1156    if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE)
   1157        return;
   1158
   1159    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
   1160        source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
   1161        videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
   1162        source->lpVtbl->Release(source);
   1163    }
   1164}
   1165
   1166static SDL_bool
   1167UILess_SetupSinks(SDL_VideoData *videodata)
   1168{
   1169    TfClientId clientid = 0;
   1170    SDL_bool result = SDL_FALSE;
   1171    ITfSource *source = 0;
   1172    if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex)))
   1173        return SDL_FALSE;
   1174
   1175    if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY)))
   1176        return SDL_FALSE;
   1177
   1178    videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink));
   1179    videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink));
   1180
   1181    videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
   1182    videodata->ime_uielemsink->refcount = 1;
   1183    videodata->ime_uielemsink->data = videodata;
   1184
   1185    videodata->ime_ippasink->lpVtbl = vtIPPASink;
   1186    videodata->ime_ippasink->refcount = 1;
   1187    videodata->ime_ippasink->data = videodata;
   1188
   1189    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
   1190        if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
   1191            if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
   1192                result = SDL_TRUE;
   1193            }
   1194        }
   1195        source->lpVtbl->Release(source);
   1196    }
   1197    return result;
   1198}
   1199
   1200#define SAFE_RELEASE(p)                             \
   1201{                                                   \
   1202    if (p) {                                        \
   1203        (p)->lpVtbl->Release((p));                  \
   1204        (p) = 0;                                    \
   1205    }                                               \
   1206}
   1207
   1208static void
   1209UILess_ReleaseSinks(SDL_VideoData *videodata)
   1210{
   1211    ITfSource *source = 0;
   1212    if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
   1213        source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
   1214        source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
   1215        SAFE_RELEASE(source);
   1216        videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
   1217        SAFE_RELEASE(videodata->ime_threadmgrex);
   1218        TSFSink_Release(videodata->ime_uielemsink);
   1219        videodata->ime_uielemsink = 0;
   1220        TSFSink_Release(videodata->ime_ippasink);
   1221        videodata->ime_ippasink = 0;
   1222    }
   1223}
   1224
   1225static void *
   1226StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
   1227{
   1228    BITMAPINFO info;
   1229    BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
   1230    BYTE *bits = NULL;
   1231    if (hhbm) {
   1232        SDL_zero(info);
   1233        infoHeader->biSize = sizeof(BITMAPINFOHEADER);
   1234        infoHeader->biWidth = width;
   1235        infoHeader->biHeight = -1 * SDL_abs(height);
   1236        infoHeader->biPlanes = 1;
   1237        infoHeader->biBitCount = 32;
   1238        infoHeader->biCompression = BI_RGB;
   1239        *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
   1240        if (*hhbm)
   1241            SelectObject(hdc, *hhbm);
   1242    }
   1243    return bits;
   1244}
   1245
   1246static void
   1247StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
   1248{
   1249    if (hhbm && *hhbm) {
   1250        DeleteObject(*hhbm);
   1251        *hhbm = NULL;
   1252    }
   1253}
   1254
   1255/* This draws only within the specified area and fills the entire region. */
   1256static void
   1257DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
   1258{
   1259    /* The case of no pen (PenSize = 0) is automatically taken care of. */
   1260    const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
   1261    left += pensize / 2;
   1262    top += pensize / 2;
   1263    right -= penadjust;
   1264    bottom -= penadjust;
   1265    Rectangle(hdc, left, top, right, bottom);
   1266}
   1267
   1268static void
   1269IME_DestroyTextures(SDL_VideoData *videodata)
   1270{
   1271}
   1272
   1273#define SDL_swap(a,b) { \
   1274    int c = (a);        \
   1275    (a) = (b);          \
   1276    (b) = c;            \
   1277    }
   1278
   1279static void
   1280IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
   1281{
   1282    int left, top, right, bottom;
   1283    SDL_bool ok = SDL_FALSE;
   1284    int winw = videodata->ime_winwidth;
   1285    int winh = videodata->ime_winheight;
   1286
   1287    /* Bottom */
   1288    left = videodata->ime_rect.x;
   1289    top = videodata->ime_rect.y + videodata->ime_rect.h;
   1290    right = left + size.cx;
   1291    bottom = top + size.cy;
   1292    if (right >= winw) {
   1293        left -= right - winw;
   1294        right = winw;
   1295    }
   1296    if (bottom < winh)
   1297        ok = SDL_TRUE;
   1298
   1299    /* Top */
   1300    if (!ok) {
   1301        left = videodata->ime_rect.x;
   1302        top = videodata->ime_rect.y - size.cy;
   1303        right = left + size.cx;
   1304        bottom = videodata->ime_rect.y;
   1305        if (right >= winw) {
   1306            left -= right - winw;
   1307            right = winw;
   1308        }
   1309        if (top >= 0)
   1310            ok = SDL_TRUE;
   1311    }
   1312
   1313    /* Right */
   1314    if (!ok) {
   1315        left = videodata->ime_rect.x + size.cx;
   1316        top = 0;
   1317        right = left + size.cx;
   1318        bottom = size.cy;
   1319        if (right < winw)
   1320            ok = SDL_TRUE;
   1321    }
   1322
   1323    /* Left */
   1324    if (!ok) {
   1325        left = videodata->ime_rect.x - size.cx;
   1326        top = 0;
   1327        right = videodata->ime_rect.x;
   1328        bottom = size.cy;
   1329        if (right >= 0)
   1330            ok = SDL_TRUE;
   1331    }
   1332
   1333    /* Window too small, show at (0,0) */
   1334    if (!ok) {
   1335        left = 0;
   1336        top = 0;
   1337        right = size.cx;
   1338        bottom = size.cy;
   1339    }
   1340
   1341    videodata->ime_candlistrect.x = left;
   1342    videodata->ime_candlistrect.y = top;
   1343    videodata->ime_candlistrect.w = right - left;
   1344    videodata->ime_candlistrect.h = bottom - top;
   1345}
   1346
   1347static void
   1348IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
   1349{
   1350    int i, j;
   1351    SIZE size = {0};
   1352    SIZE candsizes[MAX_CANDLIST];
   1353    SIZE maxcandsize = {0};
   1354    HBITMAP hbm = NULL;
   1355    const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
   1356    SDL_bool vertical = videodata->ime_candvertical;
   1357
   1358    const int listborder = 1;
   1359    const int listpadding = 0;
   1360    const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
   1361    const int listfillcolor = RGB(255, 255, 255);
   1362
   1363    const int candborder = 1;
   1364    const int candpadding = 0;
   1365    const int candmargin = 1;
   1366    const COLORREF candbordercolor = RGB(255, 255, 255);
   1367    const COLORREF candfillcolor = RGB(255, 255, 255);
   1368    const COLORREF candtextcolor = RGB(0, 0, 0);
   1369    const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
   1370    const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
   1371    const COLORREF seltextcolor = RGB(0, 0, 0);
   1372    const int horzcandspacing = 5;
   1373
   1374    HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   1375    HBRUSH listbrush = CreateSolidBrush(listfillcolor);
   1376    HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   1377    HBRUSH candbrush = CreateSolidBrush(candfillcolor);
   1378    HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   1379    HBRUSH selbrush = CreateSolidBrush(selfillcolor);
   1380    HFONT font = CreateFont((int)(1 + videodata->ime_rect.h * 0.75f), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT("Microsoft Sans Serif"));
   1381
   1382    SetBkMode(hdc, TRANSPARENT);
   1383    SelectObject(hdc, font);
   1384
   1385    for (i = 0; i < candcount; ++i) {
   1386        const WCHAR *s = videodata->ime_candidates[i];
   1387        if (!*s)
   1388            break;
   1389
   1390        GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &candsizes[i]);
   1391        maxcandsize.cx = SDL_max(maxcandsize.cx, candsizes[i].cx);
   1392        maxcandsize.cy = SDL_max(maxcandsize.cy, candsizes[i].cy);
   1393
   1394    }
   1395    if (vertical) {
   1396        size.cx =
   1397            (listborder * 2) +
   1398            (listpadding * 2) +
   1399            (candmargin * 2) +
   1400            (candborder * 2) +
   1401            (candpadding * 2) +
   1402            (maxcandsize.cx)
   1403            ;
   1404        size.cy =
   1405            (listborder * 2) +
   1406            (listpadding * 2) +
   1407            ((candcount + 1) * candmargin) +
   1408            (candcount * candborder * 2) +
   1409            (candcount * candpadding * 2) +
   1410            (candcount * maxcandsize.cy)
   1411            ;
   1412    }
   1413    else {
   1414        size.cx =
   1415            (listborder * 2) +
   1416            (listpadding * 2) +
   1417            ((candcount + 1) * candmargin) +
   1418            (candcount * candborder * 2) +
   1419            (candcount * candpadding * 2) +
   1420            ((candcount - 1) * horzcandspacing);
   1421        ;
   1422
   1423        for (i = 0; i < candcount; ++i)
   1424            size.cx += candsizes[i].cx;
   1425
   1426        size.cy =
   1427            (listborder * 2) +
   1428            (listpadding * 2) +
   1429            (candmargin * 2) +
   1430            (candborder * 2) +
   1431            (candpadding * 2) +
   1432            (maxcandsize.cy)
   1433            ;
   1434    }
   1435
   1436    StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
   1437
   1438    SelectObject(hdc, listpen);
   1439    SelectObject(hdc, listbrush);
   1440    DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
   1441
   1442    SelectObject(hdc, candpen);
   1443    SelectObject(hdc, candbrush);
   1444    SetTextColor(hdc, candtextcolor);
   1445    SetBkMode(hdc, TRANSPARENT);
   1446
   1447    for (i = 0; i < candcount; ++i) {
   1448        const WCHAR *s = videodata->ime_candidates[i];
   1449        int left, top, right, bottom;
   1450        if (!*s)
   1451            break;
   1452
   1453        if (vertical) {
   1454            left = listborder + listpadding + candmargin;
   1455            top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
   1456            right = size.cx - listborder - listpadding - candmargin;
   1457            bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
   1458        }
   1459        else {
   1460            left = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * horzcandspacing);
   1461
   1462            for (j = 0; j < i; ++j)
   1463                left += candsizes[j].cx;
   1464
   1465            top = listborder + listpadding + candmargin;
   1466            right = left + candsizes[i].cx + (candpadding * 2) + (candborder * 2);
   1467            bottom = size.cy - listborder - listpadding - candmargin;
   1468        }
   1469
   1470        if (i == videodata->ime_candsel) {
   1471            SelectObject(hdc, selpen);
   1472            SelectObject(hdc, selbrush);
   1473            SetTextColor(hdc, seltextcolor);
   1474        }
   1475        else {
   1476            SelectObject(hdc, candpen);
   1477            SelectObject(hdc, candbrush);
   1478            SetTextColor(hdc, candtextcolor);
   1479        }
   1480
   1481        DrawRect(hdc, left, top, right, bottom, candborder);
   1482        ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
   1483    }
   1484    StopDrawToBitmap(hdc, &hbm);
   1485
   1486    DeleteObject(listpen);
   1487    DeleteObject(listbrush);
   1488    DeleteObject(candpen);
   1489    DeleteObject(candbrush);
   1490    DeleteObject(selpen);
   1491    DeleteObject(selbrush);
   1492    DeleteObject(font);
   1493
   1494    IME_PositionCandidateList(videodata, size);
   1495}
   1496
   1497static void
   1498IME_Render(SDL_VideoData *videodata)
   1499{
   1500    HDC hdc = CreateCompatibleDC(NULL);
   1501
   1502    if (videodata->ime_candlist)
   1503        IME_RenderCandidateList(videodata, hdc);
   1504
   1505    DeleteDC(hdc);
   1506
   1507    videodata->ime_dirty = SDL_FALSE;
   1508}
   1509
   1510void IME_Present(SDL_VideoData *videodata)
   1511{
   1512    if (videodata->ime_dirty)
   1513        IME_Render(videodata);
   1514
   1515    /* FIXME: Need to show the IME bitmap */
   1516}
   1517
   1518#endif /* SDL_DISABLE_WINDOWS_IME */
   1519
   1520#endif /* SDL_VIDEO_DRIVER_WINDOWS */
   1521
   1522/* vi: set ts=4 sw=4 expandtab: */