Commit 423b30c5 authored by Daniel Wyatt's avatar Daniel Wyatt

Windows candidate list support.

Candidate list should now be drawn and function normally.
Tested in XP and 7.
parent 86c3638a
...@@ -34,6 +34,11 @@ ...@@ -34,6 +34,11 @@
#include "../events/SDL_sysevents.h" #include "../events/SDL_sysevents.h"
#include "../events/SDL_events_c.h" #include "../events/SDL_events_c.h"
#if SDL_VIDEO_DRIVER_WIN32
#include "win32/SDL_win32video.h"
extern void IME_Present(SDL_VideoData *videodata);
#endif
#if SDL_VIDEO_OPENGL_ES #if SDL_VIDEO_OPENGL_ES
#include "SDL_opengles.h" #include "SDL_opengles.h"
#endif /* SDL_VIDEO_OPENGL_ES */ #endif /* SDL_VIDEO_OPENGL_ES */
...@@ -2687,6 +2692,9 @@ SDL_RenderPresent(void) ...@@ -2687,6 +2692,9 @@ SDL_RenderPresent(void)
if (!renderer || !renderer->RenderPresent) { if (!renderer || !renderer->RenderPresent) {
return; return;
} }
#if SDL_VIDEO_DRIVER_WIN32
IME_Present((SDL_VideoData *)_this->driverdata);
#endif
renderer->RenderPresent(renderer); renderer->RenderPresent(renderer);
} }
......
...@@ -100,6 +100,23 @@ WIN_InitKeyboard(_THIS) ...@@ -100,6 +100,23 @@ WIN_InitKeyboard(_THIS)
data->ime_composition[0] = 0; data->ime_composition[0] = 0;
data->ime_readingstring[0] = 0; data->ime_readingstring[0] = 0;
data->ime_cursor = 0; data->ime_cursor = 0;
data->ime_candlist = SDL_FALSE;
SDL_memset(data->ime_candidates, 0, sizeof(data->ime_candidates));
data->ime_candcount = 0;
data->ime_candref = 0;
data->ime_candsel = 0;
data->ime_candpgsize = 0;
data->ime_candlistindexbase = 0;
data->ime_candvertical = SDL_TRUE;
data->ime_candtex = NULL;
data->ime_dirty = SDL_FALSE;
SDL_memset(&data->ime_rect, 0, sizeof(data->ime_rect));
SDL_memset(&data->ime_candlistrect, 0, sizeof(data->ime_candlistrect));
data->ime_winwidth = 0;
data->ime_winheight = 0;
data->ime_hkl = 0; data->ime_hkl = 0;
data->ime_himm32 = 0; data->ime_himm32 = 0;
data->GetReadingString = 0; data->GetReadingString = 0;
...@@ -165,6 +182,7 @@ WIN_StartTextInput(_THIS) ...@@ -165,6 +182,7 @@ WIN_StartTextInput(_THIS)
if (window) { if (window) {
HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
IME_Init(videodata, hwnd); IME_Init(videodata, hwnd);
IME_Enable(videodata, hwnd); IME_Enable(videodata, hwnd);
} }
...@@ -185,7 +203,8 @@ WIN_StopTextInput(_THIS) ...@@ -185,7 +203,8 @@ WIN_StopTextInput(_THIS)
void void
WIN_SetTextInputRect(_THIS, SDL_Rect *rect) WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
{ {
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
videodata->ime_rect = *rect;
} }
#ifdef __GNUC__ #ifdef __GNUC__
...@@ -196,6 +215,7 @@ DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136,0x19DF ...@@ -196,6 +215,7 @@ DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136,0x19DF
DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD, 0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31); DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD, 0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
DEFINE_GUID(IID_ITfSource, 0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7); DEFINE_GUID(IID_ITfSource, 0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
DEFINE_GUID(IID_ITfUIElementMgr, 0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C); DEFINE_GUID(IID_ITfUIElementMgr, 0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
DEFINE_GUID(IID_ITfCandidateListUIElement, 0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
DEFINE_GUID(IID_ITfReadingInformationUIElement, 0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C); DEFINE_GUID(IID_ITfReadingInformationUIElement, 0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
DEFINE_GUID(IID_ITfThreadMgr, 0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E); DEFINE_GUID(IID_ITfThreadMgr, 0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
DEFINE_GUID(CLSID_TF_ThreadMgr, 0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50); DEFINE_GUID(CLSID_TF_ThreadMgr, 0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
...@@ -243,6 +263,8 @@ static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd); ...@@ -243,6 +263,8 @@ static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd);
static void IME_SetupAPI(SDL_VideoData *videodata); static void IME_SetupAPI(SDL_VideoData *videodata);
static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex); static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
static void IME_SendEditingEvent(SDL_VideoData *videodata); static void IME_SendEditingEvent(SDL_VideoData *videodata);
static void IME_DestroyTextures(SDL_VideoData *videodata);
#define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2) #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
#define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID))) #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
...@@ -345,6 +367,7 @@ IME_Quit(SDL_VideoData *videodata) ...@@ -345,6 +367,7 @@ IME_Quit(SDL_VideoData *videodata)
CoUninitialize(); CoUninitialize();
videodata->ime_com_initialized = SDL_FALSE; videodata->ime_com_initialized = SDL_FALSE;
} }
IME_DestroyTextures(videodata);
videodata->ime_initialized = SDL_FALSE; videodata->ime_initialized = SDL_FALSE;
} }
...@@ -453,17 +476,14 @@ static void ...@@ -453,17 +476,14 @@ static void
IME_InputLangChanged(SDL_VideoData *videodata) IME_InputLangChanged(SDL_VideoData *videodata)
{ {
UINT lang = PRIMLANG(); UINT lang = PRIMLANG();
HWND hwndime = 0;
IME_UpdateInputLocale(videodata); IME_UpdateInputLocale(videodata);
if (!videodata->ime_uiless)
videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
IME_SetupAPI(videodata); IME_SetupAPI(videodata);
if (lang != PRIMLANG()) { if (lang != PRIMLANG()) {
IME_ClearComposition(videodata); IME_ClearComposition(videodata);
} }
hwndime = ImmGetDefaultIMEWnd(videodata->ime_hwnd_current);
if (hwndime) {
SendMessageA(hwndime, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0);
SendMessageA(hwndime, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);
}
} }
static DWORD static DWORD
...@@ -610,6 +630,21 @@ IME_UpdateInputLocale(SDL_VideoData *videodata) ...@@ -610,6 +630,21 @@ IME_UpdateInputLocale(SDL_VideoData *videodata)
return; return;
hklprev = videodata->ime_hkl; hklprev = videodata->ime_hkl;
switch (PRIMLANG())
{
case LANG_CHINESE:
videodata->ime_candvertical = SDL_TRUE;
if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
videodata->ime_candvertical = SDL_FALSE;
break;
case LANG_JAPANESE:
videodata->ime_candvertical = SDL_TRUE;
break;
case LANG_KOREAN:
videodata->ime_candvertical = SDL_FALSE;
break;
}
} }
static void static void
...@@ -632,12 +667,6 @@ IME_ClearComposition(SDL_VideoData *videodata) ...@@ -632,12 +667,6 @@ IME_ClearComposition(SDL_VideoData *videodata)
SDL_SendEditingText("", 0, 0); SDL_SendEditingText("", 0, 0);
} }
static void
IME_ClearEditing(SDL_VideoData *videodata)
{
}
static void static void
IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
{ {
...@@ -690,6 +719,101 @@ IME_SendEditingEvent(SDL_VideoData *videodata) ...@@ -690,6 +719,101 @@ IME_SendEditingEvent(SDL_VideoData *videodata)
SDL_free(s); SDL_free(s);
} }
static void
IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
{
LPWSTR dst = videodata->ime_candidates[i];
*dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
if (videodata->ime_candvertical)
*dst++ = TEXT(' ');
while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
*dst++ = *candidate++;
*dst = (WCHAR)'\0';
}
static void
IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
{
LPCANDIDATELIST cand_list = 0;
DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
if (size)
{
cand_list = (LPCANDIDATELIST)SDL_malloc(size);
if (cand_list)
{
size = ImmGetCandidateListW(himc, 0, cand_list, size);
if (size)
{
int i = 0;
int j = 0;
int page_start = 0;
videodata->ime_candsel = cand_list->dwSelection;
videodata->ime_candcount = cand_list->dwCount;
if (LANG() == LANG_CHS && IME_GetId(videodata, 0))
{
const UINT maxcandchar = 18;
UINT i = 0;
UINT cchars = 0;
for (; i < videodata->ime_candcount; ++i)
{
UINT len = SDL_wcslen((LPWSTR)((DWORD)cand_list + cand_list->dwOffset[i])) + 1;
if (len + cchars > maxcandchar)
{
if (i > cand_list->dwSelection)
break;
page_start = i;
cchars = len;
}
else
{
cchars += len;
}
}
videodata->ime_candpgsize = i - page_start;
}
else
{
videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
}
SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++)
{
LPCWSTR candidate = (LPCWSTR)((DWORD)cand_list + cand_list->dwOffset[i]);
IME_AddCandidate(videodata, j, candidate);
}
if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
videodata->ime_candsel = -1;
}
SDL_free(cand_list);
}
}
}
static void
IME_ShowCandidateList(SDL_VideoData *videodata)
{
videodata->ime_dirty = SDL_TRUE;
videodata->ime_candlist = SDL_TRUE;
IME_DestroyTextures(videodata);
IME_SendEditingEvent(videodata);
}
static void
IME_HideCandidateList(SDL_VideoData *videodata)
{
videodata->ime_dirty = SDL_FALSE;
videodata->ime_candlist = SDL_FALSE;
IME_DestroyTextures(videodata);
IME_SendEditingEvent(videodata);
}
SDL_bool SDL_bool
IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
{ {
...@@ -701,7 +825,7 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD ...@@ -701,7 +825,7 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD
switch (msg) switch (msg)
{ {
case WM_INPUTLANGCHANGE: case WM_INPUTLANGCHANGE:
//IME_InputLangChanged(videodata); IME_InputLangChanged(videodata);
break; break;
case WM_IME_SETCONTEXT: case WM_IME_SETCONTEXT:
*lParam = 0; *lParam = 0;
...@@ -740,10 +864,21 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD ...@@ -740,10 +864,21 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD
break; break;
case IMN_OPENCANDIDATE: case IMN_OPENCANDIDATE:
case IMN_CHANGECANDIDATE: case IMN_CHANGECANDIDATE:
if (videodata->ime_uiless)
break;
trap = SDL_TRUE; trap = SDL_TRUE;
IME_ShowCandidateList(videodata);
himc = ImmGetContext(hwnd);
if (!himc)
break;
IME_GetCandidateList(himc, videodata);
ImmReleaseContext(hwnd, himc);
break; break;
case IMN_CLOSECANDIDATE: case IMN_CLOSECANDIDATE:
trap = SDL_TRUE; trap = SDL_TRUE;
IME_HideCandidateList(videodata);
break; break;
case IMN_PRIVATE: case IMN_PRIVATE:
{ {
...@@ -784,6 +919,68 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD ...@@ -784,6 +919,68 @@ IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoD
return trap; return trap;
} }
static void
IME_CloseCandidateList(SDL_VideoData *videodata)
{
IME_HideCandidateList(videodata);
videodata->ime_candcount = 0;
SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
}
static void
UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
{
UINT selection = 0;
UINT count = 0;
UINT page = 0;
UINT pgcount = 0;
DWORD pgstart = 0;
DWORD pgsize = 0;
UINT i, j;
pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
pcandlist->lpVtbl->GetCount(pcandlist, &count);
pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
videodata->ime_candsel = selection;
videodata->ime_candcount = count;
IME_ShowCandidateList(videodata);
pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
if (pgcount > 0)
{
UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
if (idxlist)
{
pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
pgstart = idxlist[page];
if (page < pgcount - 1)
pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
else
pgsize = count - pgstart;
SDL_free(idxlist);
}
}
videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
videodata->ime_candsel = videodata->ime_candsel - pgstart;
SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++)
{
BSTR bstr;
if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr)))
{
if (bstr)
{
IME_AddCandidate(videodata, j, bstr);
SysFreeString(bstr);
}
}
}
if (PRIMLANG() == LANG_KOREAN)
videodata->ime_candsel = -1;
}
STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink) STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
{ {
return ++sink->refcount; return ++sink->refcount;
...@@ -835,6 +1032,7 @@ STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BO ...@@ -835,6 +1032,7 @@ STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BO
{ {
ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
ITfReadingInformationUIElement *preading = 0; ITfReadingInformationUIElement *preading = 0;
ITfCandidateListUIElement *pcandlist = 0;
SDL_VideoData *videodata = (SDL_VideoData *)sink->data; SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
if (!element) if (!element)
return E_INVALIDARG; return E_INVALIDARG;
...@@ -848,6 +1046,11 @@ STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BO ...@@ -848,6 +1046,11 @@ STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BO
} }
preading->lpVtbl->Release(preading); preading->lpVtbl->Release(preading);
} }
else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
videodata->ime_candref++;
UILess_GetCandidateList(videodata, pcandlist);
pcandlist->lpVtbl->Release(pcandlist);
}
return S_OK; return S_OK;
} }
...@@ -855,6 +1058,7 @@ STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId) ...@@ -855,6 +1058,7 @@ STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
{ {
ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
ITfReadingInformationUIElement *preading = 0; ITfReadingInformationUIElement *preading = 0;
ITfCandidateListUIElement *pcandlist = 0;
SDL_VideoData *videodata = (SDL_VideoData *)sink->data; SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
if (!element) if (!element)
return E_INVALIDARG; return E_INVALIDARG;
...@@ -869,6 +1073,10 @@ STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId) ...@@ -869,6 +1073,10 @@ STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
} }
preading->lpVtbl->Release(preading); preading->lpVtbl->Release(preading);
} }
else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
UILess_GetCandidateList(videodata, pcandlist);
pcandlist->lpVtbl->Release(pcandlist);
}
return S_OK; return S_OK;
} }
...@@ -876,6 +1084,7 @@ STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId) ...@@ -876,6 +1084,7 @@ STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
{ {
ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
ITfReadingInformationUIElement *preading = 0; ITfReadingInformationUIElement *preading = 0;
ITfCandidateListUIElement *pcandlist = 0;
SDL_VideoData *videodata = (SDL_VideoData *)sink->data; SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
if (!element) if (!element)
return E_INVALIDARG; return E_INVALIDARG;
...@@ -885,6 +1094,13 @@ STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId) ...@@ -885,6 +1094,13 @@ STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
IME_SendEditingEvent(videodata); IME_SendEditingEvent(videodata);
preading->lpVtbl->Release(preading); preading->lpVtbl->Release(preading);
} }
if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
videodata->ime_candref--;
if (videodata->ime_candref == 0)
IME_CloseCandidateList(videodata);
pcandlist->lpVtbl->Release(pcandlist);
}
return S_OK; return S_OK;
} }
...@@ -908,9 +1124,13 @@ STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv) ...@@ -908,9 +1124,13 @@ STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags) STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
{ {
static GUID TF_PROFILE_DAYI = {0x037B2C25, 0x480C, 0x4D7F, 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A};
SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE)) if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
IME_InputLangChanged((SDL_VideoData *)sink->data); IME_InputLangChanged((SDL_VideoData *)sink->data);
IME_HideCandidateList(videodata);
return S_OK; return S_OK;
} }
...@@ -1016,4 +1236,332 @@ UILess_ReleaseSinks(SDL_VideoData *videodata) ...@@ -1016,4 +1236,332 @@ UILess_ReleaseSinks(SDL_VideoData *videodata)
} }
} }
static void *
StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
{
BITMAPINFO info = {0};
BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
BYTE *bits = NULL;
if (hhbm)
{
infoHeader->biSize = sizeof(BITMAPINFOHEADER);
infoHeader->biWidth = width;
infoHeader->biHeight = -1 * SDL_abs(height);
infoHeader->biPlanes = 1;
infoHeader->biBitCount = 32;
infoHeader->biCompression = BI_RGB;
*hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
if (*hhbm)
SelectObject(hdc, *hhbm);
}
return bits;
}
static void
StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
{
if (hhbm && *hhbm)
{
DeleteObject(*hhbm);
*hhbm = NULL;
}
}
static void
BitmapToTexture(HBITMAP hbm, BYTE *bits, int width, int height, SDL_Texture **texture)
{
SDL_Surface *surface = NULL;
BITMAP bm = {0};
if (GetObject(hbm, sizeof(bm), &bm) == 0)
return;
if (bits && texture)
{
/*
For transparency:
const Uint8 alpha = 130;
unsigned long *p = (unsigned long *)bits;
unsigned long *end = (unsigned long *)(bits + (bm.bmWidthBytes * bm.bmHeight));
while (p < end)
{
*p = RGB(GetRValue(*p), GetGValue(*p), GetBValue(*p)) | (alpha << 24);
++p;
}
surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
*/
surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
if (surface)
{
*texture = SDL_CreateTextureFromSurface(0, surface);
SDL_FreeSurface(surface);
}
}
}
/* This draws only within the specified area and fills the entire region. */
static void
DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
{
/* The case of no pen (PenSize = 0) is automatically taken care of. */
const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
left += pensize / 2;
top += pensize / 2;
right -= penadjust;
bottom -= penadjust;
Rectangle(hdc, left, top, right, bottom);
}
static void
DestroyTexture(SDL_Texture **texture)
{
if (texture && *texture)
{
SDL_DestroyTexture(*texture);
*texture = NULL;
}
}
static void
IME_DestroyTextures(SDL_VideoData *videodata)
{
DestroyTexture(&videodata->ime_candtex);
}
#define SDL_swap(a,b) { \
int c = (a); \
(a) = (b); \
(b) = c; \
}
static void
IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
{
int left, top, right, bottom;
SDL_bool ok = SDL_FALSE;
int winw = videodata->ime_winwidth;
int winh = videodata->ime_winheight;
/* Bottom */
left = videodata->ime_rect.x;
top = videodata->ime_rect.y + videodata->ime_rect.h;
right = left + size.cx;
bottom = top + size.cy;
if (right >= winw)
{
left -= right - winw;
right = winw;
}
if (bottom < winh)
ok = SDL_TRUE;
/* Top */
if (!ok)
{
left = videodata->ime_rect.x;
top = videodata->ime_rect.y - size.cy;
right = left + size.cx;
bottom = videodata->ime_rect.y;
if (right >= winw)
{
left -= right - winw;
right = winw;
}
if (top >= 0)
ok = SDL_TRUE;
}
/* Right */
if (!ok)
{
left = videodata->ime_rect.x + size.cx;
top = 0;
right = left + size.cx;
bottom = size.cy;
if (right < winw)
ok = SDL_TRUE;
}
/* Left */
if (!ok)
{
left = videodata->ime_rect.x - size.cx;
top = 0;
right = videodata->ime_rect.x;
bottom = size.cy;
if (right >= 0)
ok = SDL_TRUE;
}
/* Window too small, show at (0,0) */
if (!ok)
{
left = 0;
top = 0;
right = size.cx;
bottom = size.cy;
}
videodata->ime_candlistrect.x = left;
videodata->ime_candlistrect.y = top;
videodata->ime_candlistrect.w = right - left;
videodata->ime_candlistrect.h = bottom - top;
}
static void
IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
{
int i = 0;
SIZE size = {0};
SIZE maxcandsize = {0};
HBITMAP hbm = NULL;
BYTE *bits = NULL;
const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
SDL_bool vertical = videodata->ime_candvertical;
const int listborder = 1;
const int listpadding = 0;
const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
const int listfillcolor = RGB(255, 255, 255);
const int candborder = 1;
const int candpadding = 0;
const int candmargin = 1;
const COLORREF candbordercolor = RGB(255, 255, 255);
const COLORREF candfillcolor = RGB(255, 255, 255);
const COLORREF candtextcolor = RGB(0, 0, 0);
const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
const COLORREF seltextcolor = RGB(0, 0, 0);
HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
HBRUSH listbrush = CreateSolidBrush(listfillcolor);
HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
HBRUSH candbrush = CreateSolidBrush(candfillcolor);
HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
HBRUSH selbrush = CreateSolidBrush(selfillcolor);
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"));
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdc, font);
for (i = 0; i < candcount; ++i)
{
const WCHAR *s = videodata->ime_candidates[i];
if (!*s)
break;
GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &size);
maxcandsize.cx = SDL_max(maxcandsize.cx, size.cx);
maxcandsize.cy = SDL_max(maxcandsize.cy, size.cy);
}
if (!vertical)
SDL_swap(maxcandsize.cx, maxcandsize.cy);
size.cx =
(listborder * 2) +
(listpadding * 2) +
(candmargin * 2) +
(candborder * 2) +
(candpadding * 2) +
(maxcandsize.cx)
;
size.cy =
(listborder * 2) +
(listpadding * 2) +
((candcount + 1) * candmargin) +
(candcount * candborder * 2) +
(candcount * candpadding * 2) +
(candcount * maxcandsize.cy)
;
if (!vertical)
SDL_swap(size.cx, size.cy);
bits = StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
SelectObject(hdc, listpen);
SelectObject(hdc, listbrush);
DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
SelectObject(hdc, candpen);
SelectObject(hdc, candbrush);
SetTextColor(hdc, candtextcolor);
SetBkMode(hdc, TRANSPARENT);
for (i = 0; i < candcount; ++i)
{
const WCHAR *s = videodata->ime_candidates[i];
int left, top, right, bottom;
if (!*s)
break;
left = listborder + listpadding + candmargin;
top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
if (!vertical)
SDL_swap(size.cx, size.cy);
right = size.cx - listborder - listpadding - candmargin;
if (!vertical)
SDL_swap(size.cx, size.cy);
bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
if (!vertical)
{
SDL_swap(left, top);
SDL_swap(right, bottom);
}
if (i == videodata->ime_candsel)
{
SelectObject(hdc, selpen);
SelectObject(hdc, selbrush);
SetTextColor(hdc, seltextcolor);
}
else
{
SelectObject(hdc, candpen);
SelectObject(hdc, candbrush);
SetTextColor(hdc, candtextcolor);
}
DrawRect(hdc, left, top, right, bottom, candborder);
ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
}
BitmapToTexture(hbm, bits, size.cx, size.cy, &videodata->ime_candtex);
StopDrawToBitmap(hdc, &hbm);
DeleteObject(listpen);
DeleteObject(listbrush);
DeleteObject(candpen);
DeleteObject(candbrush);
DeleteObject(selpen);
DeleteObject(selbrush);
DeleteObject(font);
IME_PositionCandidateList(videodata, size);
}
static void
IME_Render(SDL_VideoData *videodata)
{
HDC hdc = CreateCompatibleDC(NULL);
if (videodata->ime_candlist)
IME_RenderCandidateList(videodata, hdc);
DeleteDC(hdc);
videodata->ime_dirty = SDL_FALSE;
}
void IME_Present(SDL_VideoData *videodata)
{
if (videodata->ime_dirty)
IME_Render(videodata);
SDL_RenderCopy(videodata->ime_candtex, NULL, &videodata->ime_candlistrect);
}
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment