SDL: Removed unneeded Text Services Framework code from IME handling

From d9d7104feb8c8c286f62922582c0c0600589a88f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 28 Jun 2024 15:10:19 -0700
Subject: [PATCH] Removed unneeded Text Services Framework code from IME
 handling

We can get the candidate list in uiless mode using ImmGetCandidateListW() once message processing has completed
---
 src/video/windows/SDL_windowsevents.c   |   4 +-
 src/video/windows/SDL_windowskeyboard.c | 513 +++++-------------------
 src/video/windows/SDL_windowskeyboard.h |   3 +-
 src/video/windows/SDL_windowsvideo.h    |   9 +-
 4 files changed, 115 insertions(+), 414 deletions(-)

diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 533e21cbd77d3..3af82457774c1 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1051,7 +1051,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
 #endif /* WMMSG_DEBUG */
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
-    if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata)) {
+    if (WIN_HandleIMEMessage(hwnd, msg, wParam, &lParam, data->videodata)) {
         return 0;
     }
 #endif
@@ -2302,6 +2302,8 @@ void WIN_PumpEvents(SDL_VideoDevice *_this)
 
 #endif /*!defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)*/
 
+    WIN_UpdateIMECandidates(_this);
+
 #ifdef SDL_PLATFORM_GDK
     GDK_DispatchTaskQueue();
 #endif
diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c
index 80016fea2c3bf..95d6c3ea1bcf4 100644
--- a/src/video/windows/SDL_windowskeyboard.c
+++ b/src/video/windows/SDL_windowskeyboard.c
@@ -40,6 +40,8 @@ static int IME_Init(SDL_VideoData *videodata, SDL_Window *window);
 static void IME_Enable(SDL_VideoData *videodata, HWND hwnd);
 static void IME_Disable(SDL_VideoData *videodata, HWND hwnd);
 static void IME_SetTextInputArea(SDL_VideoData *videodata, const SDL_Rect *rect, int cursor);
+static void IME_ClearComposition(SDL_VideoData *videodata);
+static void IME_GetCandidateList(SDL_VideoData *videodata, HWND hwnd);
 static void IME_Quit(SDL_VideoData *videodata);
 #endif /* !SDL_DISABLE_WINDOWS_IME */
 
@@ -59,10 +61,6 @@ void WIN_InitKeyboard(SDL_VideoDevice *_this)
     data->ime_candlistindexbase = 1;
     data->ime_composition_length = 32 * sizeof(WCHAR);
     data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR));
-    data->ime_uielemsinkcookie = TF_INVALID_COOKIE;
-    data->ime_alpnsinkcookie = TF_INVALID_COOKIE;
-    data->ime_openmodesinkcookie = TF_INVALID_COOKIE;
-    data->ime_convmodesinkcookie = TF_INVALID_COOKIE;
 #endif /* !SDL_DISABLE_WINDOWS_IME */
 
     WIN_UpdateKeymap(SDL_FALSE);
@@ -242,39 +240,29 @@ int WIN_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
     return 0;
 }
 
-#ifdef SDL_DISABLE_WINDOWS_IME
-
 int WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window)
 {
+#ifndef SDL_DISABLE_WINDOWS_IME
+    SDL_VideoData *videodata = _this->driverdata;
+
+    IME_ClearComposition(videodata);
+#endif
     return 0;
 }
 
-SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
+#ifdef SDL_DISABLE_WINDOWS_IME
+
+SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
 {
     return SDL_FALSE;
 }
 
-#else
+void WIN_UpdateIMECandidates(SDL_VideoDevice *_this)
+{
+    return;
+}
 
-#ifdef SDL_msctf_h_
-#define USE_INIT_GUID
-#elif defined(__GNUC__)
-#define USE_INIT_GUID
-#endif
-#ifdef USE_INIT_GUID
-#undef DEFINE_GUID
-#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 } }
-DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink, 0x71C6E74E, 0x0F28, 0x11D8, 0xA8, 0x2A, 0x00, 0x06, 0x5B, 0x84, 0x43, 0x5C);
-DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136, 0x19DF, 0x11D7, 0xA6, 0xD2, 0x00, 0x06, 0x5B, 0x84, 0x43, 0x5C);
-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_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_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(IID_ITfThreadMgrEx, 0x3E90ADE3, 0x7594, 0x4CB0, 0xBB, 0x58, 0x69, 0x62, 0x8F, 0x5F, 0x45, 0x8C);
-#endif
+#else
 
 #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
 #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
@@ -312,18 +300,12 @@ DEFINE_GUID(IID_ITfThreadMgrEx, 0x3E90ADE3, 0x7594, 0x4CB0, 0xBB, 0x58, 0x69, 0x
 #define SUBLANG()      SUBLANGID(LANG())
 
 static void IME_UpdateInputLocale(SDL_VideoData *videodata);
-static void IME_ClearComposition(SDL_VideoData *videodata);
 static void IME_SetWindow(SDL_VideoData *videodata, SDL_Window *window);
 static void IME_SetupAPI(SDL_VideoData *videodata);
 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
 static void IME_SendEditingEvent(SDL_VideoData *videodata);
 static void IME_SendClearComposition(SDL_VideoData *videodata);
 
-static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
-static void UILess_ReleaseSinks(SDL_VideoData *videodata);
-static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
-static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
-
 static SDL_bool WIN_ShouldShowNativeUI()
 {
     return SDL_GetHintBoolean(SDL_HINT_IME_SHOW_UI, SDL_TRUE);
@@ -332,21 +314,12 @@ static SDL_bool WIN_ShouldShowNativeUI()
 static int IME_Init(SDL_VideoData *videodata, SDL_Window *window)
 {
     HWND hwnd = window->driverdata->hwnd;
-    HRESULT hResult = S_OK;
 
     if (videodata->ime_initialized) {
         return 0;
     }
 
     videodata->ime_hwnd_main = hwnd;
-    if (SUCCEEDED(WIN_CoInitialize())) {
-        videodata->ime_com_initialized = SDL_TRUE;
-        hResult = CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
-        if (hResult != S_OK) {
-            videodata->ime_available = SDL_FALSE;
-            return SDL_SetError("CoCreateInstance() failed, HRESULT is %08X", (unsigned int)hResult);
-        }
-    }
     videodata->ime_initialized = SDL_TRUE;
     videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
     if (!videodata->ime_himm32) {
@@ -375,7 +348,7 @@ static int IME_Init(SDL_VideoData *videodata, SDL_Window *window)
     if (WIN_ShouldShowNativeUI()) {
         videodata->ime_uiless = SDL_FALSE;
     } else {
-        videodata->ime_uiless = UILess_SetupSinks(videodata);
+        videodata->ime_uiless = SDL_TRUE;
     }
     IME_UpdateInputLocale(videodata);
     IME_Disable(videodata, hwnd);
@@ -398,7 +371,6 @@ static void IME_Enable(SDL_VideoData *videodata, HWND hwnd)
 
     videodata->ime_enabled = SDL_TRUE;
     IME_UpdateInputLocale(videodata);
-    UILess_EnableUIUpdates(videodata);
 }
 
 static void IME_Disable(SDL_VideoData *videodata, HWND hwnd)
@@ -413,7 +385,6 @@ static void IME_Disable(SDL_VideoData *videodata, HWND hwnd)
     }
 
     videodata->ime_enabled = SDL_FALSE;
-    UILess_DisableUIUpdates(videodata);
 }
 
 static void IME_Quit(SDL_VideoData *videodata)
@@ -422,7 +393,6 @@ static void IME_Quit(SDL_VideoData *videodata)
         return;
     }
 
-    UILess_ReleaseSinks(videodata);
     if (videodata->ime_hwnd_main) {
         ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
     }
@@ -433,14 +403,6 @@ static void IME_Quit(SDL_VideoData *videodata)
         SDL_UnloadObject(videodata->ime_himm32);
         videodata->ime_himm32 = 0;
     }
-    if (videodata->ime_threadmgr) {
-        videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
-        videodata->ime_threadmgr = 0;
-    }
-    if (videodata->ime_com_initialized) {
-        WIN_CoUninitialize();
-        videodata->ime_com_initialized = SDL_FALSE;
-    }
     for (int i = 0; i < videodata->ime_candcount; ++i) {
         SDL_free(videodata->ime_candidates[i]);
         videodata->ime_candidates[i] = NULL;
@@ -459,10 +421,6 @@ static void IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
     BOOL vertical = FALSE;
     UINT maxuilen = 0;
 
-    if (videodata->ime_uiless) {
-        return;
-    }
-
     videodata->ime_readingstring[0] = 0;
 
     id = IME_GetId(videodata, 0);
@@ -554,9 +512,6 @@ static void IME_InputLangChanged(SDL_VideoData *videodata)
 {
     UINT lang = PRIMLANG();
     IME_UpdateInputLocale(videodata);
-    if (!videodata->ime_uiless) {
-        videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
-    }
 
     IME_SetupAPI(videodata);
     if (lang != PRIMLANG()) {
@@ -647,11 +602,8 @@ static void IME_SetupAPI(SDL_VideoData *videodata)
     char ime_file[MAX_PATH + 1];
     void *hime = 0;
     HKL hkl = 0;
-    videodata->GetReadingString = 0;
-    videodata->ShowReadingWindow = 0;
-    if (videodata->ime_uiless) {
-        return;
-    }
+    videodata->GetReadingString = NULL;
+    videodata->ShowReadingWindow = NULL;
 
     hkl = videodata->ime_hkl;
     if (!ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1)) {
@@ -689,15 +641,6 @@ static void IME_SetWindow(SDL_VideoData *videodata, SDL_Window *window)
         SDL_zero(videodata->ime_candidate_area);
     }
 
-    if (videodata->ime_threadmgr) {
-        struct ITfDocumentMgr *document_mgr = 0;
-        if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
-            if (document_mgr) {
-                document_mgr->lpVtbl->Release(document_mgr);
-            }
-        }
-    }
-
     IME_SetTextInputArea(videodata, &window->text_input_rect, window->text_input_cursor);
 }
 
@@ -751,6 +694,7 @@ static void IME_UpdateInputLocale(SDL_VideoData *videodata)
 
     videodata->ime_hkl = hklnext;
     videodata->ime_horizontal_candidates = (PRIMLANG() == LANG_KOREAN || LANG() == LANG_CHS);
+    videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
 }
 
 static void IME_ClearComposition(SDL_VideoData *videodata)
@@ -766,9 +710,7 @@ static void IME_ClearComposition(SDL_VideoData *videodata)
     }
 
     ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
-    if (videodata->ime_uiless) {
-        ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
-    }
+    ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
 
     ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
     ImmReleaseContext(videodata->ime_hwnd_current, himc);
@@ -924,7 +866,6 @@ static int IME_OpenCandidateList(SDL_VideoData *videodata)
     return 0;
 }
 
-
 static void IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
 {
     if (videodata->ime_candidates[i]) {
@@ -960,10 +901,83 @@ static void IME_CloseCandidateList(SDL_VideoData *videodata)
     }
 }
 
-SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
+static void IME_GetCandidateList(SDL_VideoData *videodata, HWND hwnd)
+{
+    HIMC himc;
+    DWORD size;
+    LPCANDIDATELIST cand_list;
+    SDL_bool has_candidates = SDL_FALSE;
+
+    himc = ImmGetContext(hwnd);
+    if (himc) {
+        size = ImmGetCandidateListW(himc, 0, NULL, 0);
+        if (size != 0) {
+            cand_list = (LPCANDIDATELIST)SDL_malloc(size);
+            if (cand_list != NULL) {
+                size = ImmGetCandidateListW(himc, 0, cand_list, size);
+                if (size != 0) {
+                    if (IME_OpenCandidateList(videodata) == 0) {
+                        UINT i, j;
+                        UINT page_start = 0;
+                        UINT page_size = 0;
+
+                        videodata->ime_candsel = cand_list->dwSelection;
+
+                        if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
+                            const UINT maxcandchar = 18;
+                            size_t cchars = 0;
+
+                            for (i = 0; i < cand_list->dwCount; ++i) {
+                                size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)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;
+                                }
+                            }
+                            page_size = i - page_start;
+                        } else {
+                            page_size = SDL_min(cand_list->dwPageSize == 0 ? MAX_CANDLIST : cand_list->dwPageSize, MAX_CANDLIST);
+                            page_start = (cand_list->dwSelection / page_size) * page_size;
+                        }
+                        for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < page_size; i++, j++) {
+                            LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
+                            IME_AddCandidate(videodata, j, candidate);
+                        }
+
+                        has_candidates = SDL_TRUE;
+                        IME_SendCandidateList(videodata);
+                    }
+                }
+                SDL_free(cand_list);
+            }
+        }
+        ImmReleaseContext(hwnd, himc);
+    }
+
+    if (!has_candidates) {
+        IME_CloseCandidateList(videodata);
+    }
+}
+
+SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
 {
     SDL_bool trap = SDL_FALSE;
     HIMC himc = 0;
+
+    if (msg == WM_IME_SETCONTEXT) {
+        SDL_DebugIMELog("WM_IME_SETCONTEXT\n");
+        if (videodata->ime_uiless) {
+            *lParam = 0;
+        }
+        return SDL_FALSE;
+    }
+
     if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled) {
         return SDL_FALSE;
     }
@@ -981,12 +995,6 @@ SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, S
         SDL_DebugIMELog("WM_INPUTLANGCHANGE\n");
         IME_InputLangChanged(videodata);
         break;
-    case WM_IME_SETCONTEXT:
-        SDL_DebugIMELog("WM_IME_SETCONTEXT\n");
-        if (videodata->ime_uiless) {
-            *lParam = 0;
-        }
-        break;
     case WM_IME_STARTCOMPOSITION:
         SDL_DebugIMELog("WM_IME_STARTCOMPOSITION\n");
         trap = SDL_TRUE;
@@ -1003,10 +1011,7 @@ SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, S
         }
         if (*lParam & GCS_COMPSTR) {
             SDL_DebugIMELog("GCS_COMPSTR\n");
-            if (!videodata->ime_uiless) {
-                videodata->ime_readingstring[0] = 0;
-            }
-
+            videodata->ime_readingstring[0] = 0;
             IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
             IME_SendEditingEvent(videodata);
         }
@@ -1038,11 +1043,18 @@ SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, S
         case IMN_OPENCANDIDATE:
         case IMN_CHANGECANDIDATE:
             SDL_DebugIMELog("%s\n", wParam == IMN_OPENCANDIDATE ? "IMN_OPENCANDIDATE" : "IMN_CHANGECANDIDATE");
-            trap = SDL_TRUE;
+            if (videodata->ime_uiless) {
+                videodata->ime_update_candidates = SDL_TRUE;
+                trap = SDL_TRUE;
+            }
             break;
         case IMN_CLOSECANDIDATE:
             SDL_DebugIMELog("IMN_CLOSECANDIDATE\n");
-            trap = SDL_TRUE;
+            if (videodata->ime_uiless) {
+                videodata->ime_update_candidates = SDL_FALSE;
+                IME_CloseCandidateList(videodata);
+                trap = SDL_TRUE;
+            }
             break;
         case IMN_PRIVATE:
         {
@@ -1080,323 +1092,16 @@ SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, S
     return trap;
 }
 
-static void UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
+void WIN_UpdateIMECandidates(SDL_VideoDevice *_this)
 {
-    UINT selection = 0;
-    UINT count = 0;
-    UINT page = 0;
-    UINT pgcount = 0;
-    DWORD pgstart = 0;
-    DWORD pgsize = 0;
-    UINT i, j;
-
-    if (IME_OpenCandidateList(videodata) < 0) {
-        return;
-    }
-
-    pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
-    pcandlist->lpVtbl->GetCount(pcandlist, &count);
-    pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
-
-    pcandlist->lpVtbl->GetPageIndex(pcandlist, NULL, 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);
-        }
-    }
-    pgsize = SDL_min(pgsize, MAX_CANDLIST);
-    videodata->ime_candsel = selection - pgstart;
-    for (i = pgstart, j = 0; (DWORD)i < count && j < pgsize; i++, j++) {
-        BSTR bstr;
-        if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
-            if (bstr) {
-                IME_AddCandidate(videodata, j, bstr);
-                SysFreeString(bstr);
-            }
-        }
-    }
-
-    IME_SendCandidateList(videodata);
-}
-
-STDMETHODIMP_(ULONG)
-TSFSink_AddRef(TSFSink *sink)
-{
-    return ++sink->refcount;
-}
-
-STDMETHODIMP_(ULONG)
-TSFSink_Release(TSFSink *sink)
-{
-    --sink->refcount;
-    if (sink->refcount == 0) {
-        SDL_free(sink);
-        return 0;
-    }
-    return sink->refcount;
-}
-
-STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
-{
-    if (!ppv) {
-        return E_INVALIDARG;
-    }
-
-    *ppv = 0;
-    if (WIN_IsEqualIID(riid, &IID_IUnknown)) {
-        *ppv = (IUnknown *)sink;
-    } else if (WIN_IsEqualIID(riid, &IID_ITfUIElementSink)) {
-        *ppv = (ITfUIElementSink *)sink;
-    }
-
-    if (*ppv) {
-        TSFSink_AddRef(sink);
-        return S_OK;
-    }
-    return E_NOINTERFACE;
-}
-
-ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
-{
-    ITfUIElementMgr *puiem = 0;
-    ITfUIElement *pelem = 0;
-    ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
-
-    if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
-        puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
-        puiem->lpVtbl->Release(puiem);
-    }
-    return pelem;
-}
-
-STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
-{
-    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
-    ITfReadingInformationUIElement *preading = 0;
-    ITfCandidateListUIElement *pcandlist = 0;
-    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
-    if (!element) {
-        return E_INVALIDARG;
-    }
-
-    *pbShow = FALSE;
-    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
-        BSTR bstr;
-        if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
-            SysFreeString(bstr);
-        }
-        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;
-}
-
-STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
-{
-    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
-    ITfReadingInformationUIElement *preading = 0;
-    ITfCandidateListUIElement *pcandlist = 0;
-    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
-    if (!element) {
-        return E_INVALIDARG;
-    }
-
-    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
-        BSTR bstr;
-        if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
-            WCHAR *s = (WCHAR *)bstr;
-            SDL_wcslcpy(videodata->ime_readingstring, s, SDL_arraysize(videodata->ime_readingstring));
-            IME_SendEditingEvent(videodata);
-            SysFreeString(bstr);
-        }
-        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;
-}
-
-STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
-{
-    ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
-    ITfReadingInformationUIElement *preading = 0;
-    ITfCandidateListUIElement *pcandlist = 0;
-    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
-    if (!element) {
-        return E_INVALIDARG;
-    }
-
-    if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
-        videodata->ime_readingstring[0] = 0;
-        IME_SendEditingEvent(videodata);
-        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;
-}
-
-STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
-{
-    if (!ppv) {
-        return E_INVALIDARG;
-    }
-
-    *ppv = 0;
-    if (WIN_IsEqualIID(riid, &IID_IUnknown)) {
-        *ppv = (IUnknown *)sink;
-    } else if (WIN_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink)) {
-        *ppv = (ITfInputProcessorProfileActivationSink *)sink;
-    }
-
-    if (*ppv) {
-        TSFSink_AddRef(sink);
-        return S_OK;
-    }
-    return E_NOINTERFACE;
-}
-
-STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
-{
-    static const GUID SDL_TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
-    SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
-    videodata->ime_candlistindexbase = WIN_IsEqualGUID(&SDL_TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
-    if (WIN_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE)) {
-        IME_InputLangChanged((SDL_VideoData *)sink->data);
-    }
-
-    IME_CloseCandidateList(videodata);
-    return S_OK;
-}
-
-static void *vtUIElementSink[] = {
-    (void *)(UIElementSink_QueryInterface),
-    (void *)(TSFSink_AddRef),
-    (void *)(TSFSink_Release),
-    (void *)(UIElementSink_BeginUIElement),
-    (void *)(UIElementSink_UpdateUIElement),
-    (void *)(UIElementSink_EndUIElement)
-};
-
-static void *vtIPPASink[] = {
-    (void *)(IPPASink_QueryInterface),
-    (void *)(TSFSink_AddRef),
-    (void *)(TSFSink_Release),
-    (void *)(IPPASink_OnActivated)
-};
-
-static void UILess_EnableUIUpdates(SDL_VideoData *videodata)
-{
-    ITfSource *source = 0;
-    if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE) {
-        return;
-    }
-
-    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
-        source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
-        source->lpVtbl->Release(source);
-    }
-}
-
-static void UILess_DisableUIUpdates(SDL_VideoData *videodata)
-{
-    ITfSource *source = 0;
-    if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE) {
-        return;
-    }
-
-    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
-        source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
-        videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
-        source->lpVtbl->Release(source);
-    }
-}
-
-static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata)
-{
-    TfClientId clientid = 0;
-    SDL_bool result = SDL_FALSE;
-    ITfSource *source = 0;
-    if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex))) {
-        return SDL_FALSE;
-    }
-
-    if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY))) {
-        return SDL_FALSE;
-    }
-
-    videodata->ime_uielemsink = (TSFSink *)SDL_malloc(sizeof(TSFSink));
-    videodata->ime_ippasink = (TSFSink *)SDL_malloc(sizeof(TSFSink));
-
-    videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
-    videodata->ime_uielemsink->refcount = 1;
-    videodata->ime_uielemsink->data = videodata;
-
-    videodata->ime_ippasink->lpVtbl = vtIPPASink;
-    videodata->ime_ippasink->refcount = 1;
-    videodata->ime_ippasink->data = videodata;
-
-    if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
-        if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
-            if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
-                result = SDL_TRUE;
-            }
-        }
-        source->lpVtbl->Release(source);
-    }
-    return result;
-}
-
-#define SAFE_RELEASE(p)                \
-    {                                  \
-        if (p) {                       \
-            (p)->lpVtbl->Release((p)); \
-            (p) = 0;                   \
-        }                              \
-    }
+    SDL_VideoData *videodata = _this->driverdata;
 
-static void UILess_ReleaseSinks(SDL_VideoData *videodata)
-{
-    ITfSource *source = 0;
-    if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
-        source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
-        source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
-        SAFE_RELEASE(source);
-        videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
-        SAFE_RELEASE(videodata->ime_threadmgrex);
-        TSFSink_Release(videodata->ime_uielemsink);
-        videodata->ime_uielemsink = 0;
-        TSFSink_Release(videodata->ime_ippasink);
-        videodata->ime_ippasink = 0;
+    if (videodata->ime_update_candidates) {
+        IME_GetCandidateList(videodata, videodata->ime_hwnd_current);
+        videodata->ime_update_candidates = SDL_FALSE;
     }
 }
 
-int WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    SDL_VideoData *videodata = _this->driverdata;
-    IME_ClearComposition(videodata);
-    return 0;
-}
-
 #endif /* SDL_DISABLE_WINDOWS_IME */
 
 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
diff --git a/src/video/windows/SDL_windowskeyboard.h b/src/video/windows/SDL_windowskeyboard.h
index e62ce045f87cc..15de35f208c46 100644
--- a/src/video/windows/SDL_windowskeyboard.h
+++ b/src/video/windows/SDL_windowskeyboard.h
@@ -34,6 +34,7 @@ extern int WIN_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
 extern int WIN_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
 extern int WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window);
 
-extern SDL_bool IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, struct SDL_VideoData *videodata);
+extern SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, struct SDL_VideoData *videodata);
+extern void WIN_UpdateIMECandidates(SDL_VideoDevice *_this);
 
 #endif /* SDL_windowskeyboard_h_ */
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index 3d078619ad30c..c2cf4677aa027 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -423,7 +423,6 @@ struct SDL_VideoData
 
 #ifndef SDL_DISABLE_WINDOWS_IME
     SDL_bool ime_com_initialized;
-    struct ITfThreadMgr *ime_threadmgr;
     SDL_bool ime_initialized;
     SDL_bool ime_enabled;
     SDL_bool ime_available;
@@ -440,6 +439,7 @@ struct SDL_VideoData
     int ime_selected_length;
 
     SDL_bool ime_candidates_open;
+    SDL_bool ime_update_candidates;
     char *ime_candidates[MAX_CANDLIST];
     int ime_candcount;
     DWORD ime_candref;
@@ -462,13 +462,6 @@ struct SDL_VideoData
     /* *INDENT-ON* */ /* clang-format on */
 
     SDL_bool ime_uiless;
-    struct ITfThreadMgrEx *ime_threadmgrex;
-    DWORD ime_uielemsinkcookie;
-    DWORD ime_alpnsinkcookie;
-    DWORD ime_openmodesinkcookie;
-    DWORD ime_convmodesinkcookie;
-    TSFSink *ime_uielemsink;
-    TSFSink *ime_ippasink;
 #endif /* !SDL_DISABLE_WINDOWS_IME */
 
     BYTE pre_hook_key_state[256];