src/video/windows/SDL_windowskeyboard.c
author Ryan C. Gordon
Mon, 01 Jan 2018 19:16:51 -0500
changeset 11803 454f6dc9cb85
parent 11471 510e461e584a
child 11811 5d94cb6b24d3
permissions -rw-r--r--
windows: Remove references to GetVersionExA (thanks, Andrew Pilley!).

"GetVersionExA is deprecated in windows 8.1 and above's SDK, causing a warning
when building against the win10 SDK. Attached patch cleans up the usage for a
warning-free build.

GetVersionExA was being used to test to see if SDL was running on win9x or
winnt. A quick chat with Ryan on twitter suggested that SDL doesn't
officially support win9x anymore, so the call to this can be outright removed.

As an aside, replacing the call to GetVersionExA with VerifyVersionInfoA (the
recommended path) would have been pointless, as VerifyVersionInfoA only
supports VER_PLATFORM_WIN32_NT and doesn't officially support any other value
for dwPlatformId currently. (And it's probable that win9x SDKs didn't have
VerifyVersionInfo* in them anyway.)"

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