[NTUSER] Implement NtUserAssociateInputContext (#4334)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Sat, 5 Feb 2022 11:23:57 +0000 (20:23 +0900)
committerGitHub <noreply@github.com>
Sat, 5 Feb 2022 11:23:57 +0000 (20:23 +0900)
- Add IntReAllocatePoolWithTag function in window.c.
- Define WINDOWLIST structure in "window.h".
- Add IntBuildHwndList and IntFreeHwndList helper functions in window.c.
- Add IntAssociateInputContext and IntAssociateInputContextEx helper functions in ime.c.
- Implement NtUserAssociateInputContext function.
CORE-11700

sdk/include/psdk/imm.h
win32ss/user/ntuser/ime.c
win32ss/user/ntuser/window.c
win32ss/user/ntuser/window.h

index 83908c9..95ac099 100644 (file)
@@ -577,11 +577,10 @@ DWORD WINAPI ImeGetImeMenuItems(HIMC, DWORD, DWORD, LPIMEMENUITEMINFOW, LPIMEMEN
 #define IME_REGWORD_STYLE_USER_FIRST    0x80000000
 #define IME_REGWORD_STYLE_USER_LAST     0xFFFFFFFF
 
-
 /* dwFlags for ImmAssociateContextEx */
-#define IACE_CHILDREN                  0x0001
-#define IACE_DEFAULT                   0x0010
-#define IACE_IGNORENOCONTEXT           0x0020
+#define IACE_CHILDREN           0x0001
+#define IACE_DEFAULT            0x0010
+#define IACE_IGNORENOCONTEXT    0x0020
 
 /* dwFlags for ImmGetImeMenuItems */
 #define IGIMIF_RIGHTMENU               0x0001
index 0e7d4cf..06b7eab 100644 (file)
@@ -695,12 +695,97 @@ Quit:
     return ret;
 }
 
+HIMC FASTCALL IntAssociateInputContext(PWND pWnd, PIMC pImc)
+{
+    HIMC hOldImc = pWnd->hImc;
+    pWnd->hImc = (pImc ? UserHMGetHandle(pImc) : NULL);
+    return hOldImc;
+}
+
+DWORD FASTCALL IntAssociateInputContextEx(PWND pWnd, PIMC pIMC, DWORD dwFlags)
+{
+    DWORD ret = 0;
+    PWINDOWLIST pwl;
+    BOOL bIgnoreNullImc = (dwFlags & IACE_IGNORENOCONTEXT);
+    PTHREADINFO pti = pWnd->head.pti;
+    PWND pwndTarget, pwndFocus = pti->MessageQueue->spwndFocus;
+    HWND *phwnd;
+    HIMC hIMC;
+
+    if (dwFlags & IACE_DEFAULT)
+    {
+        pIMC = pti->spDefaultImc;
+    }
+    else
+    {
+        if (pIMC && pti != pIMC->head.pti)
+            return 2;
+    }
+
+    if (pWnd->head.pti->ppi != GetW32ThreadInfo()->ppi ||
+        (pIMC && pIMC->head.rpdesk != pWnd->head.rpdesk))
+    {
+        return 2;
+    }
+
+    if ((dwFlags & IACE_CHILDREN) && pWnd->spwndChild)
+    {
+        pwl = IntBuildHwndList(pWnd->spwndChild, IACE_CHILDREN | IACE_LIST, pti);
+        if (pwl)
+        {
+            for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
+            {
+                pwndTarget = ValidateHwndNoErr(*phwnd);
+                if (!pwndTarget)
+                    continue;
+
+                hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
+                if (pwndTarget->hImc == hIMC || (bIgnoreNullImc && !pwndTarget->hImc))
+                    continue;
+
+                IntAssociateInputContext(pwndTarget, pIMC);
+                if (pwndTarget == pwndFocus)
+                    ret = 1;
+            }
+
+            IntFreeHwndList(pwl);
+        }
+    }
+
+    if (!bIgnoreNullImc || pWnd->hImc)
+    {
+        hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
+        if (pWnd->hImc != hIMC)
+        {
+            IntAssociateInputContext(pWnd, pIMC);
+            if (pWnd == pwndFocus)
+                ret = 1;
+        }
+    }
+
+    return ret;
+}
+
 DWORD
 APIENTRY
 NtUserAssociateInputContext(HWND hWnd, HIMC hIMC, DWORD dwFlags)
 {
-    STUB
-    return 0;
+    DWORD ret = 2;
+    PWND pWnd;
+    PIMC pIMC;
+
+    UserEnterExclusive();
+
+    pWnd = ValidateHwndNoErr(hWnd);
+    if (!pWnd || !IS_IMM_MODE())
+        goto Quit;
+
+    pIMC = (hIMC ? UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT) : NULL);
+    ret = IntAssociateInputContextEx(pWnd, pIMC, dwFlags);
+
+Quit:
+    UserLeave();
+    return ret;
 }
 
 BOOL FASTCALL UserUpdateInputContext(PIMC pIMC, DWORD dwType, DWORD_PTR dwValue)
index d9cba32..f3aeab5 100644 (file)
@@ -3,7 +3,8 @@
  * PROJECT:          ReactOS Win32k subsystem
  * PURPOSE:          Windows
  * FILE:             win32ss/user/ntuser/window.c
- * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * PROGRAMERS:       Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *                   Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
  */
 
 #include <win32k.h>
@@ -11,8 +12,28 @@ DBG_DEFAULT_CHANNEL(UserWnd);
 
 INT gNestedWindowLimit = 50;
 
+PWINDOWLIST gpwlList = NULL;
+PWINDOWLIST gpwlCache = NULL;
+
 /* HELPER FUNCTIONS ***********************************************************/
 
+PVOID FASTCALL
+IntReAllocatePoolWithTag(
+    POOL_TYPE PoolType,
+    PVOID pOld,
+    SIZE_T cbOld,
+    SIZE_T cbNew,
+    ULONG Tag)
+{
+    PVOID pNew = ExAllocatePoolWithTag(PoolType, cbNew, Tag);
+    if (!pNew)
+        return NULL;
+
+    RtlCopyMemory(pNew, pOld, min(cbOld, cbNew));
+    ExFreePoolWithTag(pOld, Tag);
+    return pNew;
+}
+
 BOOL FASTCALL UserUpdateUiState(PWND Wnd, WPARAM wParam)
 {
     WORD Action = LOWORD(wParam);
@@ -1320,6 +1341,133 @@ IntUnlinkWindow(PWND Wnd)
     Wnd->spwndPrev = Wnd->spwndNext = NULL;
 }
 
+BOOL FASTCALL IntGrowHwndList(PWINDOWLIST *ppwl)
+{
+    PWINDOWLIST pwlOld, pwlNew;
+    SIZE_T ibOld, ibNew;
+
+#define GROW_COUNT 8
+    pwlOld = *ppwl;
+    ibOld = (LPBYTE)pwlOld->phwndLast - (LPBYTE)pwlOld;
+    ibNew = ibOld + GROW_COUNT * sizeof(HWND);
+#undef GROW_COUNT
+    pwlNew = IntReAllocatePoolWithTag(PagedPool, pwlOld, ibOld, ibNew, USERTAG_WINDOWLIST);
+    if (!pwlNew)
+        return FALSE;
+
+    pwlNew->phwndLast = (HWND *)((LPBYTE)pwlNew + ibOld);
+    pwlNew->phwndEnd = (HWND *)((LPBYTE)pwlNew + ibNew);
+    *ppwl = pwlNew;
+    return TRUE;
+}
+
+PWINDOWLIST FASTCALL IntPopulateHwndList(PWINDOWLIST pwl, PWND pwnd, DWORD dwFlags)
+{
+    ASSERT(!WL_IS_BAD(pwl));
+
+    for (; pwnd; pwnd = pwnd->spwndNext)
+    {
+        if (!pwl->pti || pwl->pti == pwnd->head.pti)
+        {
+            *(pwl->phwndLast) = UserHMGetHandle(pwnd);
+            ++(pwl->phwndLast);
+
+            if (pwl->phwndLast == pwl->phwndEnd && !IntGrowHwndList(&pwl))
+                break;
+        }
+
+        if ((dwFlags & IACE_CHILDREN) && pwnd->spwndChild)
+        {
+            pwl = IntPopulateHwndList(pwl, pwnd->spwndChild, IACE_CHILDREN | IACE_LIST);
+            if (WL_IS_BAD(pwl))
+                break;
+        }
+
+        if (!(dwFlags & IACE_LIST))
+            break;
+    }
+
+    return pwl;
+}
+
+PWINDOWLIST FASTCALL IntBuildHwndList(PWND pwnd, DWORD dwFlags, PTHREADINFO pti)
+{
+    PWINDOWLIST pwl;
+    DWORD cbWL;
+
+    if (gpwlCache)
+    {
+        pwl = gpwlCache;
+        gpwlCache = NULL;
+    }
+    else
+    {
+#define INITIAL_COUNT 32
+        cbWL = sizeof(WINDOWLIST) + (INITIAL_COUNT - 1) * sizeof(HWND);
+        pwl = ExAllocatePoolWithTag(PagedPool, cbWL, USERTAG_WINDOWLIST);
+        if (!pwl)
+            return NULL;
+
+        pwl->phwndEnd = &pwl->ahwnd[INITIAL_COUNT];
+#undef INITIAL_COUNT
+    }
+
+    pwl->pti = pti;
+    pwl->phwndLast = pwl->ahwnd;
+    pwl = IntPopulateHwndList(pwl, pwnd, dwFlags);
+    if (WL_IS_BAD(pwl))
+    {
+        ExFreePoolWithTag(pwl, USERTAG_WINDOWLIST);
+        return NULL;
+    }
+
+    *(pwl->phwndLast) = HWND_TERMINATOR;
+
+    if (dwFlags & 0x8)
+    {
+        // TODO:
+    }
+
+    pwl->pti = GetW32ThreadInfo();
+    pwl->pNextList = gpwlList;
+    gpwlList = pwl;
+
+    return pwl;
+}
+
+VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget)
+{
+    PWINDOWLIST pwl, *ppwl;
+
+    for (ppwl = &gpwlList; *ppwl; ppwl = &(*ppwl)->pNextList)
+    {
+        if (*ppwl != pwlTarget)
+            continue;
+
+        *ppwl = pwlTarget->pNextList;
+
+        if (gpwlCache)
+        {
+            if (WL_CAPACITY(pwlTarget) > WL_CAPACITY(gpwlCache))
+            {
+                pwl = gpwlCache;
+                gpwlCache = pwlTarget;
+                ExFreePoolWithTag(pwl, USERTAG_WINDOWLIST);
+            }
+            else
+            {
+                ExFreePoolWithTag(pwlTarget, USERTAG_WINDOWLIST);
+            }
+        }
+        else
+        {
+            gpwlCache = pwlTarget;
+        }
+
+        break;
+    }
+}
+
 /* FUNCTIONS *****************************************************************/
 
 /*
index f2cd877..fd63e1c 100644 (file)
@@ -79,4 +79,24 @@ LONG_PTR FASTCALL co_UserSetWindowLongPtr(HWND, DWORD, LONG_PTR, BOOL);
 HWND FASTCALL IntGetWindow(HWND,UINT);
 LRESULT co_UserFreeWindow(PWND,PPROCESSINFO,PTHREADINFO,BOOLEAN);
 
+#define HWND_TERMINATOR ((HWND)(ULONG_PTR)1)
+
+typedef struct tagWINDOWLIST
+{
+    struct tagWINDOWLIST *pNextList;
+    HWND *phwndLast;
+    HWND *phwndEnd;
+    PTHREADINFO pti;
+    HWND ahwnd[ANYSIZE_ARRAY]; /* Terminated by HWND_TERMINATOR */
+} WINDOWLIST, *PWINDOWLIST;
+
+#define WL_IS_BAD(pwl)   ((pwl)->phwndEnd <= (pwl)->phwndLast)
+#define WL_CAPACITY(pwl) ((pwl)->phwndEnd - &((pwl)->ahwnd[0]))
+
+PWINDOWLIST FASTCALL IntBuildHwndList(PWND pwnd, DWORD dwFlags, PTHREADINFO pti);
+VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget);
+
+/* Undocumented dwFlags for IntBuildHwndList */
+#define IACE_LIST  0x0002
+
 /* EOF */