src/video/windows/SDL_windowskeyboard.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 12 Jul 2013 23:45:12 -0700
changeset 7430 f204394628db
parent 7225 4070fc496bc1
child 7870 3aed2f137788
permissions -rw-r--r--
Fixed bug 1938 - Buffer overflows in the Windows IME code

norfanin

There are a few potential buffer overflows in the Windows IME code located in the SDL_windowskeyboard.c file. [1] They mainly happen because the code passes the number of bytes instead of the number of characters to the wide-character string functions wcslcpy and wcslcat. In another place, the code assumes that the composition cursor position can never go beyond the size of the composition string buffer.

Some of these overflows and overruns can occur with the Japanese IME on Vista and simplified Chinese IME on XP. I don't actually speak those languages and it's my first time using the IMEs, so I probably pushed them to the limit where nobody would still be compositing proper words. They don't cause any immediate access violation, although the possibility of trashing the SDL_VideoData structure is never good.

I've attached a patch that fixes those I found, but because I'm very new to the code it may be worthwhile if someone else also has a look over the code.

I'll go over the changes in my patch and explain what, why and how.

In the function IME_GetReadingString, there is a wcslcpy to copy the reading string from the IMC memory to the SDL reading string buffer. [2] This assumes that the length of the reading string never exceeds the SDL buffer size. I guess that is possible and I wasn't able to get a long reading string in my tests, but the patch adds a simple check anyway.

In the function IME_GetCompositionString, the first line calls ImmGetCompositionStringW to get the composition string. [3] The Microsoft documentation states that the fourth argument is for the destination buffer size in bytes (even with unicode) and the code correctly passes the value of sizeof. However, at the end of IME_GetCompositionString, the string is terminated by setting the element at index 'length' to 0. 'length' is calculated by dividing the number of bytes (those written by ImmGetCompositionStringW) by 2. If it managed to write 64 bytes, the code sets element 32 to 0, which would be the beginning of the reading string if the alignment places it there. My patch adds a subtraction to the fourth argument, essentially making it always pass 62 instead.

In the same function, the code assumes that the composition cursor position doesn't go beyond the buffer size. [4] My patch adds a simple range check in front of the indirection.

In the function IME_SendEditingEvent, the size for the wide-character string functions is passed in bytes instead of characters. [5] Oddly, the current code subtracts 'len' from the size in one function call. This results in truncation in certain situations as the third argument is the number of characters available in the destination buffer. If I'm understanding it correctly, this is supposed to copy x characters of the composition buffer, then concatenate the whole reading string buffer, and then the rest of the composition buffer (where x is the composition cursor position). I don't see how a truncation of the rest would be helpful here. Perhaps this is just an error? My patch removes the subtraction.

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