Windows candidate list support.
authorDaniel Wyatt
Tue, 23 Nov 2010 17:46:47 -0500
changeset 49136b89d83b0b5a
parent 4912 37576cdf6751
child 4914 cc7ac6aaac5d
Windows candidate list support.
Candidate list should now be drawn and function normally.
Tested in XP and 7.
src/video/SDL_video.c
src/video/win32/SDL_win32keyboard.c
     1.1 --- a/src/video/SDL_video.c	Tue Nov 23 17:44:10 2010 -0500
     1.2 +++ b/src/video/SDL_video.c	Tue Nov 23 17:46:47 2010 -0500
     1.3 @@ -34,6 +34,11 @@
     1.4  #include "../events/SDL_sysevents.h"
     1.5  #include "../events/SDL_events_c.h"
     1.6  
     1.7 +#if SDL_VIDEO_DRIVER_WIN32
     1.8 +#include "win32/SDL_win32video.h"
     1.9 +extern void IME_Present(SDL_VideoData *videodata);
    1.10 +#endif
    1.11 +
    1.12  #if SDL_VIDEO_OPENGL_ES
    1.13  #include "SDL_opengles.h"
    1.14  #endif /* SDL_VIDEO_OPENGL_ES */
    1.15 @@ -2687,6 +2692,9 @@
    1.16      if (!renderer || !renderer->RenderPresent) {
    1.17          return;
    1.18      }
    1.19 +#if SDL_VIDEO_DRIVER_WIN32
    1.20 +    IME_Present((SDL_VideoData *)_this->driverdata);
    1.21 +#endif
    1.22      renderer->RenderPresent(renderer);
    1.23  }
    1.24  
     2.1 --- a/src/video/win32/SDL_win32keyboard.c	Tue Nov 23 17:44:10 2010 -0500
     2.2 +++ b/src/video/win32/SDL_win32keyboard.c	Tue Nov 23 17:46:47 2010 -0500
     2.3 @@ -100,6 +100,23 @@
     2.4      data->ime_composition[0] = 0;
     2.5      data->ime_readingstring[0] = 0;
     2.6      data->ime_cursor = 0;
     2.7 +
     2.8 +    data->ime_candlist = SDL_FALSE;
     2.9 +    SDL_memset(data->ime_candidates, 0, sizeof(data->ime_candidates));
    2.10 +    data->ime_candcount = 0;
    2.11 +    data->ime_candref = 0;
    2.12 +    data->ime_candsel = 0;
    2.13 +    data->ime_candpgsize = 0;
    2.14 +    data->ime_candlistindexbase = 0;
    2.15 +    data->ime_candvertical = SDL_TRUE;
    2.16 +
    2.17 +    data->ime_candtex = NULL;
    2.18 +    data->ime_dirty = SDL_FALSE;
    2.19 +    SDL_memset(&data->ime_rect, 0, sizeof(data->ime_rect));
    2.20 +    SDL_memset(&data->ime_candlistrect, 0, sizeof(data->ime_candlistrect));
    2.21 +    data->ime_winwidth = 0;
    2.22 +    data->ime_winheight = 0;
    2.23 +
    2.24      data->ime_hkl = 0;
    2.25      data->ime_himm32 = 0;
    2.26      data->GetReadingString = 0;
    2.27 @@ -165,6 +182,7 @@
    2.28      if (window) {
    2.29          HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
    2.30          SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
    2.31 +        SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
    2.32          IME_Init(videodata, hwnd);
    2.33          IME_Enable(videodata, hwnd);
    2.34      }
    2.35 @@ -185,7 +203,8 @@
    2.36  void
    2.37  WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
    2.38  {
    2.39 -
    2.40 +    SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
    2.41 +    videodata->ime_rect = *rect;
    2.42  }
    2.43  
    2.44  #ifdef __GNUC__
    2.45 @@ -196,6 +215,7 @@
    2.46  DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,                           0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
    2.47  DEFINE_GUID(IID_ITfSource,                                     0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
    2.48  DEFINE_GUID(IID_ITfUIElementMgr,                               0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    2.49 +DEFINE_GUID(IID_ITfCandidateListUIElement,                     0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    2.50  DEFINE_GUID(IID_ITfReadingInformationUIElement,                0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
    2.51  DEFINE_GUID(IID_ITfThreadMgr,                                  0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
    2.52  DEFINE_GUID(CLSID_TF_ThreadMgr,                                0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
    2.53 @@ -243,6 +263,8 @@
    2.54  static void IME_SetupAPI(SDL_VideoData *videodata);
    2.55  static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
    2.56  static void IME_SendEditingEvent(SDL_VideoData *videodata);
    2.57 +static void IME_DestroyTextures(SDL_VideoData *videodata);
    2.58 +
    2.59  #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
    2.60  #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
    2.61  
    2.62 @@ -345,6 +367,7 @@
    2.63          CoUninitialize();
    2.64          videodata->ime_com_initialized = SDL_FALSE;
    2.65      }
    2.66 +    IME_DestroyTextures(videodata);
    2.67      videodata->ime_initialized = SDL_FALSE;
    2.68  }
    2.69  
    2.70 @@ -453,17 +476,14 @@
    2.71  IME_InputLangChanged(SDL_VideoData *videodata)
    2.72  {
    2.73      UINT lang = PRIMLANG();
    2.74 -    HWND hwndime = 0;
    2.75      IME_UpdateInputLocale(videodata);
    2.76 +    if (!videodata->ime_uiless)
    2.77 +        videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
    2.78 +
    2.79      IME_SetupAPI(videodata);
    2.80      if (lang != PRIMLANG()) {
    2.81          IME_ClearComposition(videodata);
    2.82      }
    2.83 -    hwndime = ImmGetDefaultIMEWnd(videodata->ime_hwnd_current);
    2.84 -    if (hwndime) {
    2.85 -        SendMessageA(hwndime, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0);
    2.86 -        SendMessageA(hwndime, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);
    2.87 -    }
    2.88  }
    2.89  
    2.90  static DWORD
    2.91 @@ -610,6 +630,21 @@
    2.92          return;
    2.93  
    2.94      hklprev = videodata->ime_hkl;
    2.95 +    switch (PRIMLANG())
    2.96 +    {
    2.97 +    case LANG_CHINESE:
    2.98 +        videodata->ime_candvertical = SDL_TRUE;
    2.99 +        if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
   2.100 +            videodata->ime_candvertical = SDL_FALSE;
   2.101 +
   2.102 +        break;
   2.103 +    case LANG_JAPANESE:
   2.104 +        videodata->ime_candvertical = SDL_TRUE;
   2.105 +        break;
   2.106 +    case LANG_KOREAN:
   2.107 +        videodata->ime_candvertical = SDL_FALSE;
   2.108 +        break;
   2.109 +    }
   2.110  }
   2.111  
   2.112  static void
   2.113 @@ -633,12 +668,6 @@
   2.114  }
   2.115  
   2.116  static void
   2.117 -IME_ClearEditing(SDL_VideoData *videodata)
   2.118 -{
   2.119 -
   2.120 -}
   2.121 -
   2.122 -static void
   2.123  IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
   2.124  {
   2.125      LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition));
   2.126 @@ -690,6 +719,101 @@
   2.127      SDL_free(s);
   2.128  }
   2.129  
   2.130 +static void
   2.131 +IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
   2.132 +{
   2.133 +    LPWSTR dst = videodata->ime_candidates[i];
   2.134 +    *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
   2.135 +    if (videodata->ime_candvertical)
   2.136 +        *dst++ = TEXT(' ');
   2.137 +
   2.138 +    while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
   2.139 +        *dst++ = *candidate++;
   2.140 +
   2.141 +    *dst = (WCHAR)'\0';
   2.142 +}
   2.143 +
   2.144 +static void
   2.145 +IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
   2.146 +{
   2.147 +    LPCANDIDATELIST cand_list = 0;
   2.148 +    DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
   2.149 +    if (size)
   2.150 +    {
   2.151 +        cand_list = (LPCANDIDATELIST)SDL_malloc(size);
   2.152 +        if (cand_list)
   2.153 +        {
   2.154 +            size = ImmGetCandidateListW(himc, 0, cand_list, size);
   2.155 +            if (size)
   2.156 +            {
   2.157 +                int i = 0;
   2.158 +                int j = 0;
   2.159 +                int page_start = 0;
   2.160 +                videodata->ime_candsel = cand_list->dwSelection;
   2.161 +                videodata->ime_candcount = cand_list->dwCount;
   2.162 +
   2.163 +                if (LANG() == LANG_CHS && IME_GetId(videodata, 0))
   2.164 +                {
   2.165 +                    const UINT maxcandchar = 18;
   2.166 +                    UINT i = 0;
   2.167 +                    UINT cchars = 0;
   2.168 +
   2.169 +                    for (; i < videodata->ime_candcount; ++i)
   2.170 +                    {
   2.171 +                        UINT len = SDL_wcslen((LPWSTR)((DWORD)cand_list + cand_list->dwOffset[i])) + 1;
   2.172 +                        if (len + cchars > maxcandchar)
   2.173 +                        {
   2.174 +                            if (i > cand_list->dwSelection)
   2.175 +                                break;
   2.176 +
   2.177 +                            page_start = i;
   2.178 +                            cchars = len;
   2.179 +                        }
   2.180 +                        else
   2.181 +                        {
   2.182 +                            cchars += len;
   2.183 +                        }
   2.184 +                    }
   2.185 +                    videodata->ime_candpgsize = i - page_start;
   2.186 +                }
   2.187 +                else
   2.188 +                {
   2.189 +                    videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
   2.190 +                    page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
   2.191 +                }
   2.192 +                SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   2.193 +                for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++)
   2.194 +                {
   2.195 +                    LPCWSTR candidate = (LPCWSTR)((DWORD)cand_list + cand_list->dwOffset[i]);
   2.196 +                    IME_AddCandidate(videodata, j, candidate);
   2.197 +                }
   2.198 +                if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
   2.199 +                    videodata->ime_candsel = -1;
   2.200 +
   2.201 +            }
   2.202 +            SDL_free(cand_list);
   2.203 +        }
   2.204 +    }
   2.205 +}
   2.206 +
   2.207 +static void
   2.208 +IME_ShowCandidateList(SDL_VideoData *videodata)
   2.209 +{
   2.210 +    videodata->ime_dirty = SDL_TRUE;
   2.211 +    videodata->ime_candlist = SDL_TRUE;
   2.212 +    IME_DestroyTextures(videodata);
   2.213 +    IME_SendEditingEvent(videodata);
   2.214 +}
   2.215 +
   2.216 +static void
   2.217 +IME_HideCandidateList(SDL_VideoData *videodata)
   2.218 +{
   2.219 +    videodata->ime_dirty = SDL_FALSE;
   2.220 +    videodata->ime_candlist = SDL_FALSE;
   2.221 +    IME_DestroyTextures(videodata);
   2.222 +    IME_SendEditingEvent(videodata);
   2.223 +}
   2.224 +
   2.225  SDL_bool
   2.226  IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
   2.227  {
   2.228 @@ -701,7 +825,7 @@
   2.229      switch (msg)
   2.230      {
   2.231      case WM_INPUTLANGCHANGE:
   2.232 -        //IME_InputLangChanged(videodata);
   2.233 +        IME_InputLangChanged(videodata);
   2.234          break;
   2.235      case WM_IME_SETCONTEXT:
   2.236          *lParam = 0;
   2.237 @@ -740,10 +864,21 @@
   2.238              break;
   2.239          case IMN_OPENCANDIDATE:
   2.240          case IMN_CHANGECANDIDATE:
   2.241 +            if (videodata->ime_uiless)
   2.242 +                break;
   2.243 +
   2.244              trap = SDL_TRUE;
   2.245 +            IME_ShowCandidateList(videodata);
   2.246 +            himc = ImmGetContext(hwnd);
   2.247 +            if (!himc)
   2.248 +                break;
   2.249 +
   2.250 +            IME_GetCandidateList(himc, videodata);
   2.251 +            ImmReleaseContext(hwnd, himc);
   2.252              break;
   2.253          case IMN_CLOSECANDIDATE:
   2.254              trap = SDL_TRUE;
   2.255 +            IME_HideCandidateList(videodata);
   2.256              break;
   2.257          case IMN_PRIVATE:
   2.258              {
   2.259 @@ -784,6 +919,68 @@
   2.260      return trap;
   2.261  }
   2.262  
   2.263 +static void
   2.264 +IME_CloseCandidateList(SDL_VideoData *videodata)
   2.265 +{
   2.266 +    IME_HideCandidateList(videodata);
   2.267 +    videodata->ime_candcount = 0;
   2.268 +    SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   2.269 +}
   2.270 +
   2.271 +static void
   2.272 +UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
   2.273 +{
   2.274 +    UINT selection = 0;
   2.275 +    UINT count = 0;
   2.276 +    UINT page = 0;
   2.277 +    UINT pgcount = 0;
   2.278 +    DWORD pgstart = 0;
   2.279 +    DWORD pgsize = 0;
   2.280 +    UINT i, j;
   2.281 +    pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
   2.282 +    pcandlist->lpVtbl->GetCount(pcandlist, &count);
   2.283 +    pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
   2.284 +
   2.285 +    videodata->ime_candsel = selection;
   2.286 +    videodata->ime_candcount = count;
   2.287 +    IME_ShowCandidateList(videodata);
   2.288 +
   2.289 +    pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
   2.290 +    if (pgcount > 0)
   2.291 +    {
   2.292 +        UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
   2.293 +        if (idxlist)
   2.294 +        {
   2.295 +            pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
   2.296 +            pgstart = idxlist[page];
   2.297 +            if (page < pgcount - 1)
   2.298 +                pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
   2.299 +            else
   2.300 +                pgsize = count - pgstart;
   2.301 +
   2.302 +            SDL_free(idxlist);
   2.303 +        }
   2.304 +    }
   2.305 +    videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
   2.306 +    videodata->ime_candsel = videodata->ime_candsel - pgstart;
   2.307 +
   2.308 +    SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   2.309 +    for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++)
   2.310 +    {
   2.311 +        BSTR bstr;
   2.312 +        if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr)))
   2.313 +        {
   2.314 +            if (bstr)
   2.315 +            {
   2.316 +                IME_AddCandidate(videodata, j, bstr);
   2.317 +                SysFreeString(bstr);
   2.318 +            }
   2.319 +        }
   2.320 +    }
   2.321 +    if (PRIMLANG() == LANG_KOREAN)
   2.322 +        videodata->ime_candsel = -1;
   2.323 +}
   2.324 +
   2.325  STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
   2.326  {
   2.327      return ++sink->refcount;
   2.328 @@ -835,6 +1032,7 @@
   2.329  {
   2.330      ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   2.331      ITfReadingInformationUIElement *preading = 0;
   2.332 +    ITfCandidateListUIElement *pcandlist = 0;
   2.333      SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   2.334      if (!element)
   2.335          return E_INVALIDARG;
   2.336 @@ -848,6 +1046,11 @@
   2.337          }
   2.338          preading->lpVtbl->Release(preading);
   2.339      }
   2.340 +    else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist)))	{
   2.341 +        videodata->ime_candref++;
   2.342 +        UILess_GetCandidateList(videodata, pcandlist);
   2.343 +        pcandlist->lpVtbl->Release(pcandlist);
   2.344 +    }
   2.345      return S_OK;
   2.346  }
   2.347  
   2.348 @@ -855,6 +1058,7 @@
   2.349  {
   2.350      ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   2.351      ITfReadingInformationUIElement *preading = 0;
   2.352 +    ITfCandidateListUIElement *pcandlist = 0;
   2.353      SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   2.354      if (!element)
   2.355          return E_INVALIDARG;
   2.356 @@ -869,6 +1073,10 @@
   2.357          }
   2.358          preading->lpVtbl->Release(preading);
   2.359      }
   2.360 +    else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist)))	{
   2.361 +        UILess_GetCandidateList(videodata, pcandlist);
   2.362 +        pcandlist->lpVtbl->Release(pcandlist);
   2.363 +    }
   2.364      return S_OK;
   2.365  }
   2.366  
   2.367 @@ -876,6 +1084,7 @@
   2.368  {
   2.369      ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
   2.370      ITfReadingInformationUIElement *preading = 0;
   2.371 +    ITfCandidateListUIElement *pcandlist = 0;
   2.372      SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   2.373      if (!element)
   2.374          return E_INVALIDARG;
   2.375 @@ -885,6 +1094,13 @@
   2.376          IME_SendEditingEvent(videodata);
   2.377          preading->lpVtbl->Release(preading);
   2.378      }
   2.379 +    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
   2.380 +        videodata->ime_candref--;
   2.381 +        if (videodata->ime_candref == 0)
   2.382 +            IME_CloseCandidateList(videodata);
   2.383 +
   2.384 +        pcandlist->lpVtbl->Release(pcandlist);
   2.385 +    }
   2.386      return S_OK;
   2.387  }
   2.388  
   2.389 @@ -908,9 +1124,13 @@
   2.390  
   2.391  STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
   2.392  {
   2.393 +    static GUID TF_PROFILE_DAYI = {0x037B2C25, 0x480C, 0x4D7F, 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A};
   2.394 +    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
   2.395 +    videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
   2.396      if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
   2.397          IME_InputLangChanged((SDL_VideoData *)sink->data);
   2.398  
   2.399 +    IME_HideCandidateList(videodata);
   2.400      return S_OK;
   2.401  }
   2.402  
   2.403 @@ -1016,4 +1236,332 @@
   2.404      }
   2.405  }
   2.406  
   2.407 +static void *
   2.408 +StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
   2.409 +{
   2.410 +    BITMAPINFO info = {0};
   2.411 +    BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
   2.412 +    BYTE *bits = NULL;
   2.413 +    if (hhbm)
   2.414 +    {
   2.415 +        infoHeader->biSize = sizeof(BITMAPINFOHEADER);
   2.416 +        infoHeader->biWidth = width;
   2.417 +        infoHeader->biHeight = -1 * SDL_abs(height);
   2.418 +        infoHeader->biPlanes = 1;
   2.419 +        infoHeader->biBitCount = 32;
   2.420 +        infoHeader->biCompression = BI_RGB;
   2.421 +        *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
   2.422 +        if (*hhbm)
   2.423 +            SelectObject(hdc, *hhbm);
   2.424 +    }
   2.425 +    return bits;
   2.426 +}
   2.427 +
   2.428 +static void
   2.429 +StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
   2.430 +{
   2.431 +    if (hhbm && *hhbm)
   2.432 +    {
   2.433 +        DeleteObject(*hhbm);
   2.434 +        *hhbm = NULL;
   2.435 +    }
   2.436 +}
   2.437 +
   2.438 +static void
   2.439 +BitmapToTexture(HBITMAP hbm, BYTE *bits, int width, int height, SDL_Texture **texture)
   2.440 +{
   2.441 +    SDL_Surface *surface = NULL;
   2.442 +    BITMAP bm = {0};
   2.443 +
   2.444 +    if (GetObject(hbm, sizeof(bm), &bm) == 0)
   2.445 +        return;
   2.446 +
   2.447 +    if (bits && texture)
   2.448 +    {
   2.449 +        /*
   2.450 +            For transparency:
   2.451 +
   2.452 +            const Uint8 alpha = 130;
   2.453 +            unsigned long *p = (unsigned long *)bits;
   2.454 +            unsigned long *end = (unsigned long *)(bits + (bm.bmWidthBytes * bm.bmHeight));
   2.455 +            while (p < end)
   2.456 +            {
   2.457 +                *p = RGB(GetRValue(*p), GetGValue(*p), GetBValue(*p)) | (alpha << 24);
   2.458 +                ++p;
   2.459 +            }
   2.460 +            surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
   2.461 +        */
   2.462 +        surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
   2.463 +        if (surface)
   2.464 +        {
   2.465 +            *texture = SDL_CreateTextureFromSurface(0, surface);
   2.466 +            SDL_FreeSurface(surface);
   2.467 +        }
   2.468 +    }
   2.469 +}
   2.470 +
   2.471 +/* This draws only within the specified area and fills the entire region. */
   2.472 +static void
   2.473 +DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
   2.474 +{
   2.475 +    /* The case of no pen (PenSize = 0) is automatically taken care of. */
   2.476 +    const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
   2.477 +    left += pensize / 2;
   2.478 +    top += pensize / 2;
   2.479 +    right -= penadjust;
   2.480 +    bottom -= penadjust;
   2.481 +    Rectangle(hdc, left, top, right, bottom);
   2.482 +}
   2.483 +
   2.484 +static void
   2.485 +DestroyTexture(SDL_Texture **texture)
   2.486 +{
   2.487 +    if (texture && *texture)
   2.488 +    {
   2.489 +        SDL_DestroyTexture(*texture);
   2.490 +        *texture = NULL;
   2.491 +    }
   2.492 +}
   2.493 +
   2.494 +static void
   2.495 +IME_DestroyTextures(SDL_VideoData *videodata)
   2.496 +{
   2.497 +    DestroyTexture(&videodata->ime_candtex);
   2.498 +}
   2.499 +
   2.500 +#define SDL_swap(a,b) { \
   2.501 +    int c = (a);        \
   2.502 +    (a) = (b);          \
   2.503 +    (b) = c;            \
   2.504 +    }
   2.505 +
   2.506 +static void
   2.507 +IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
   2.508 +{
   2.509 +    int left, top, right, bottom;
   2.510 +    SDL_bool ok = SDL_FALSE;
   2.511 +    int winw = videodata->ime_winwidth;
   2.512 +    int winh = videodata->ime_winheight;
   2.513 +
   2.514 +    /* Bottom */
   2.515 +    left = videodata->ime_rect.x;
   2.516 +    top = videodata->ime_rect.y + videodata->ime_rect.h;
   2.517 +    right = left + size.cx;
   2.518 +    bottom = top + size.cy;
   2.519 +    if (right >= winw)
   2.520 +    {
   2.521 +        left -= right - winw;
   2.522 +        right = winw;
   2.523 +    }
   2.524 +    if (bottom < winh)
   2.525 +        ok = SDL_TRUE;
   2.526 +
   2.527 +    /* Top */
   2.528 +    if (!ok)
   2.529 +    {
   2.530 +        left = videodata->ime_rect.x;
   2.531 +        top = videodata->ime_rect.y - size.cy;
   2.532 +        right = left + size.cx;
   2.533 +        bottom = videodata->ime_rect.y;
   2.534 +        if (right >= winw)
   2.535 +        {
   2.536 +            left -= right - winw;
   2.537 +            right = winw;
   2.538 +        }
   2.539 +        if (top >= 0)
   2.540 +            ok = SDL_TRUE;
   2.541 +    }
   2.542 +
   2.543 +    /* Right */
   2.544 +    if (!ok)
   2.545 +    {
   2.546 +        left = videodata->ime_rect.x + size.cx;
   2.547 +        top = 0;
   2.548 +        right = left + size.cx;
   2.549 +        bottom = size.cy;
   2.550 +        if (right < winw)
   2.551 +            ok = SDL_TRUE;
   2.552 +    }
   2.553 +
   2.554 +    /* Left */
   2.555 +    if (!ok)
   2.556 +    {
   2.557 +        left = videodata->ime_rect.x - size.cx;
   2.558 +        top = 0;
   2.559 +        right = videodata->ime_rect.x;
   2.560 +        bottom = size.cy;
   2.561 +        if (right >= 0)
   2.562 +            ok = SDL_TRUE;
   2.563 +    }
   2.564 +
   2.565 +    /* Window too small, show at (0,0) */
   2.566 +    if (!ok)
   2.567 +    {
   2.568 +        left = 0;
   2.569 +        top = 0;
   2.570 +        right = size.cx;
   2.571 +        bottom = size.cy;
   2.572 +    }
   2.573 +
   2.574 +    videodata->ime_candlistrect.x = left;
   2.575 +    videodata->ime_candlistrect.y = top;
   2.576 +    videodata->ime_candlistrect.w = right - left;
   2.577 +    videodata->ime_candlistrect.h = bottom - top;
   2.578 +}
   2.579 +
   2.580 +static void
   2.581 +IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
   2.582 +{
   2.583 +    int i = 0;
   2.584 +    SIZE size = {0};
   2.585 +    SIZE maxcandsize = {0};
   2.586 +    HBITMAP hbm = NULL;
   2.587 +    BYTE *bits = NULL;
   2.588 +    const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
   2.589 +    SDL_bool vertical = videodata->ime_candvertical;
   2.590 +
   2.591 +    const int listborder = 1;
   2.592 +    const int listpadding = 0;
   2.593 +    const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
   2.594 +    const int listfillcolor = RGB(255, 255, 255);
   2.595 +
   2.596 +    const int candborder = 1;
   2.597 +    const int candpadding = 0;
   2.598 +    const int candmargin = 1;
   2.599 +    const COLORREF candbordercolor = RGB(255, 255, 255);
   2.600 +    const COLORREF candfillcolor = RGB(255, 255, 255);
   2.601 +    const COLORREF candtextcolor = RGB(0, 0, 0);
   2.602 +    const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
   2.603 +    const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
   2.604 +    const COLORREF seltextcolor = RGB(0, 0, 0);
   2.605 +
   2.606 +    HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   2.607 +    HBRUSH listbrush = CreateSolidBrush(listfillcolor);
   2.608 +    HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   2.609 +    HBRUSH candbrush = CreateSolidBrush(candfillcolor);
   2.610 +    HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
   2.611 +    HBRUSH selbrush = CreateSolidBrush(selfillcolor);
   2.612 +    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"));
   2.613 +
   2.614 +    SetBkMode(hdc, TRANSPARENT);
   2.615 +    SelectObject(hdc, font);
   2.616 +
   2.617 +    for (i = 0; i < candcount; ++i)
   2.618 +    {
   2.619 +        const WCHAR *s = videodata->ime_candidates[i];
   2.620 +        if (!*s)
   2.621 +            break;
   2.622 +
   2.623 +        GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &size);
   2.624 +        maxcandsize.cx = SDL_max(maxcandsize.cx, size.cx);
   2.625 +        maxcandsize.cy = SDL_max(maxcandsize.cy, size.cy);
   2.626 +
   2.627 +    }
   2.628 +    if (!vertical)
   2.629 +        SDL_swap(maxcandsize.cx, maxcandsize.cy);
   2.630 +
   2.631 +    size.cx =
   2.632 +        (listborder * 2) +
   2.633 +        (listpadding * 2) +
   2.634 +        (candmargin * 2) +
   2.635 +        (candborder * 2) +
   2.636 +        (candpadding * 2) +
   2.637 +        (maxcandsize.cx)
   2.638 +        ;
   2.639 +    size.cy =
   2.640 +        (listborder * 2) +
   2.641 +        (listpadding * 2) +
   2.642 +        ((candcount + 1) * candmargin) +
   2.643 +        (candcount * candborder * 2) +
   2.644 +        (candcount * candpadding * 2) +
   2.645 +        (candcount * maxcandsize.cy)
   2.646 +        ;
   2.647 +    if (!vertical)
   2.648 +        SDL_swap(size.cx, size.cy);
   2.649 +
   2.650 +    bits = StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
   2.651 +
   2.652 +    SelectObject(hdc, listpen);
   2.653 +    SelectObject(hdc, listbrush);
   2.654 +    DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
   2.655 +
   2.656 +    SelectObject(hdc, candpen);
   2.657 +    SelectObject(hdc, candbrush);
   2.658 +    SetTextColor(hdc, candtextcolor);
   2.659 +    SetBkMode(hdc, TRANSPARENT);
   2.660 +
   2.661 +    for (i = 0; i < candcount; ++i)
   2.662 +    {
   2.663 +        const WCHAR *s = videodata->ime_candidates[i];
   2.664 +        int left, top, right, bottom;
   2.665 +        if (!*s)
   2.666 +            break;
   2.667 +
   2.668 +        left = listborder + listpadding + candmargin;
   2.669 +        top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
   2.670 +        if (!vertical)
   2.671 +            SDL_swap(size.cx, size.cy);
   2.672 +
   2.673 +        right = size.cx - listborder - listpadding - candmargin;
   2.674 +        if (!vertical)
   2.675 +            SDL_swap(size.cx, size.cy);
   2.676 +
   2.677 +        bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
   2.678 +        if (!vertical)
   2.679 +        {
   2.680 +            SDL_swap(left, top);
   2.681 +            SDL_swap(right, bottom);
   2.682 +        }
   2.683 +
   2.684 +        if (i == videodata->ime_candsel)
   2.685 +        {
   2.686 +            SelectObject(hdc, selpen);
   2.687 +            SelectObject(hdc, selbrush);
   2.688 +            SetTextColor(hdc, seltextcolor);
   2.689 +        }
   2.690 +        else
   2.691 +        {
   2.692 +            SelectObject(hdc, candpen);
   2.693 +            SelectObject(hdc, candbrush);
   2.694 +            SetTextColor(hdc, candtextcolor);
   2.695 +        }
   2.696 +
   2.697 +        DrawRect(hdc, left, top, right, bottom, candborder);
   2.698 +        ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
   2.699 +    }
   2.700 +    BitmapToTexture(hbm, bits, size.cx, size.cy, &videodata->ime_candtex);
   2.701 +    StopDrawToBitmap(hdc, &hbm);
   2.702 +
   2.703 +    DeleteObject(listpen);
   2.704 +    DeleteObject(listbrush);
   2.705 +    DeleteObject(candpen);
   2.706 +    DeleteObject(candbrush);
   2.707 +    DeleteObject(selpen);
   2.708 +    DeleteObject(selbrush);
   2.709 +    DeleteObject(font);
   2.710 +
   2.711 +    IME_PositionCandidateList(videodata, size);
   2.712 +}
   2.713 +
   2.714 +static void
   2.715 +IME_Render(SDL_VideoData *videodata)
   2.716 +{
   2.717 +    HDC hdc = CreateCompatibleDC(NULL);
   2.718 +
   2.719 +    if (videodata->ime_candlist)
   2.720 +        IME_RenderCandidateList(videodata, hdc);
   2.721 +
   2.722 +    DeleteDC(hdc);
   2.723 +
   2.724 +    videodata->ime_dirty = SDL_FALSE;
   2.725 +}
   2.726 +
   2.727 +void IME_Present(SDL_VideoData *videodata)
   2.728 +{
   2.729 +    if (videodata->ime_dirty)
   2.730 +        IME_Render(videodata);
   2.731 +
   2.732 +    SDL_RenderCopy(videodata->ime_candtex, NULL, &videodata->ime_candlistrect);
   2.733 +}
   2.734 +
   2.735  /* vi: set ts=4 sw=4 expandtab: */