reactos/win32ss/user/ntuser/kbdlayout.c
Katayama Hirofumi MZ 8ea93d2ab2
[REACTOS] Standardize <imm.h> and <immdev.h> (#6493)
Improve header compatibility and
code quality.
JIRA issue: CORE-19268
- Improve <imm.h> and <immdev.h>
  compatibility by correctly choosing
  the items.
- Use <immdev.h> instead of
  <ddk/immdev.h>.
- Move INPUTCONTEXTDX, IMEINFOEX,
  IMEDPI, and CLIENTIMC into
  <imm32_undoc.h>.
- Adapt to these changes.
2024-02-13 20:33:14 +09:00

1303 lines
31 KiB
C

/*
* PROJECT: ReactOS Win32k subsystem
* LICENSE: GPL - See COPYING in the top level directory
* FILE: win32ss/user/ntuser/kbdlayout.c
* PURPOSE: Keyboard layout management
* COPYRIGHT: Copyright 2007 Saveliy Tretiakov
* Copyright 2008 Colin Finck
* Copyright 2011 Rafal Harabien
* Copyright 2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#include <win32k.h>
#include <immdev.h>
// Was included only because of CP_ACP and required the
// definition of SYSTEMTIME in ndk\rtltypes.h
//#include <winnls.h>
#define CP_ACP 0
DBG_DEFAULT_CHANNEL(UserKbdLayout);
PKL gspklBaseLayout = NULL; /* FIXME: Please move this to pWinSta->spklList */
PKBDFILE gpkfList = NULL;
DWORD gSystemFS = 0;
UINT gSystemCPCharSet = 0;
DWORD gLCIDSentToShell = 0;
typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID);
/* PRIVATE FUNCTIONS ******************************************************/
/*
* Retrieves a PKL by an input locale identifier (HKL).
* @implemented
* Win: HKLtoPKL
*/
PKL FASTCALL IntHKLtoPKL(_Inout_ PTHREADINFO pti, _In_ HKL hKL)
{
PKL pFirstKL, pKL;
pFirstKL = pti->KeyboardLayout;
if (!pFirstKL)
return NULL;
pKL = pFirstKL;
/* hKL can have special value HKL_NEXT or HKL_PREV */
if (hKL == (HKL)(ULONG_PTR)HKL_NEXT) /* Looking forward */
{
do
{
pKL = pKL->pklNext;
if (!(pKL->dwKL_Flags & KLF_UNLOAD))
return pKL;
} while (pKL != pFirstKL);
}
else if (hKL == (HKL)(ULONG_PTR)HKL_PREV) /* Looking backward */
{
do
{
pKL = pKL->pklPrev;
if (!(pKL->dwKL_Flags & KLF_UNLOAD))
return pKL;
} while (pKL != pFirstKL);
}
else if (HIWORD(hKL)) /* hKL is a full input locale identifier */
{
/* No KLF_UNLOAD check */
do
{
if (pKL->hkl == hKL)
return pKL;
pKL = pKL->pklNext;
} while (pKL != pFirstKL);
}
else /* Language only specified */
{
/* No KLF_UNLOAD check */
do
{
if (LOWORD(pKL->hkl) == LOWORD(hKL)) /* Low word is language ID */
return pKL;
pKL = pKL->pklNext;
} while (pKL != pFirstKL);
}
return NULL;
}
/*
* A helper function for NtUserGetKeyboardLayoutList.
* @implemented
* Win: _GetKeyboardLayoutList
*/
static UINT APIENTRY
IntGetKeyboardLayoutList(
_Inout_ PWINSTATION_OBJECT pWinSta,
_In_ ULONG nBuff,
_Out_ HKL *pHklBuff)
{
UINT ret = 0;
PKL pKL, pFirstKL;
pFirstKL = gspklBaseLayout; /* FIXME: Use pWinSta->spklList instead */
if (!pWinSta || !pFirstKL)
return 0;
pKL = pFirstKL;
if (nBuff == 0)
{
/* Count the effective PKLs */
do
{
if (!(pKL->dwKL_Flags & KLF_UNLOAD))
++ret;
pKL = pKL->pklNext;
} while (pKL != pFirstKL);
}
else
{
/* Copy the effective HKLs to pHklBuff */
do
{
if (!(pKL->dwKL_Flags & KLF_UNLOAD))
{
*pHklBuff = pKL->hkl;
++pHklBuff;
++ret;
--nBuff;
if (nBuff == 0)
break;
}
pKL = pKL->pklNext;
} while (pKL != pFirstKL);
}
return ret;
}
#if 0 && DBG
static VOID
DumpKbdLayout(
IN PKBDTABLES pKbdTbl)
{
PVK_TO_BIT pVkToBit;
PVK_TO_WCHAR_TABLE pVkToWchTbl;
PVSC_VK pVscVk;
ULONG i;
DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n",
pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK);
DbgPrint("wMaxModBits %x\n",
pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits
: 0);
if (pKbdTbl->pCharModifiers)
{
pVkToBit = pKbdTbl->pCharModifiers->pVkToBit;
if (pVkToBit)
{
for (; pVkToBit->Vk; ++pVkToBit)
{
DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits);
}
}
for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i)
{
DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]);
}
}
pVkToWchTbl = pKbdTbl->pVkToWcharTable;
if (pVkToWchTbl)
{
for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl)
{
PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars;
DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n",
pVkToWchTbl->nModifications, pVkToWchTbl->cbSize);
if (pVkToWch)
{
while (pVkToWch->VirtualKey)
{
DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ",
pVkToWch->VirtualKey, pVkToWch->Attributes);
for (i = 0; i < pVkToWchTbl->nModifications; ++i)
{
DbgPrint("%x ", pVkToWch->wch[i]);
}
DbgPrint("}\n");
pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize);
}
}
}
}
// TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead
DbgPrint("pusVSCtoVK: { ");
if (pKbdTbl->pusVSCtoVK)
{
for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i)
{
DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]);
}
}
DbgPrint("}\n");
DbgPrint("pVSCtoVK_E0: { ");
pVscVk = pKbdTbl->pVSCtoVK_E0;
if (pVscVk)
{
for (; pVscVk->Vsc; ++pVscVk)
{
DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
}
}
DbgPrint("}\n");
DbgPrint("pVSCtoVK_E1: { ");
pVscVk = pKbdTbl->pVSCtoVK_E1;
if (pVscVk)
{
for (; pVscVk->Vsc; ++pVscVk)
{
DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
}
}
DbgPrint("}\n");
// TODO: Ligatures
}
#endif // DBG
/*
* UserLoadKbdDll
*
* Loads keyboard layout DLL and gets address to KbdTables
*/
static BOOL
UserLoadKbdDll(WCHAR *pwszLayoutPath,
HANDLE *phModule,
PKBDTABLES *pKbdTables)
{
PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor;
/* Load keyboard layout DLL */
TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath);
*phModule = EngLoadImage(pwszLayoutPath);
if (!(*phModule))
{
ERR("Failed to load dll %ws\n", pwszLayoutPath);
return FALSE;
}
/* Find KbdLayerDescriptor function and get layout tables */
TRACE("Loaded %ws\n", pwszLayoutPath);
pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor");
/* FIXME: Windows reads file instead of executing!
It's not safe to kbdlayout DLL in kernel mode! */
if (pfnKbdLayerDescriptor)
*pKbdTables = pfnKbdLayerDescriptor();
else
ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath);
if (!pfnKbdLayerDescriptor || !*pKbdTables)
{
ERR("Failed to load the keyboard layout.\n");
EngUnloadImage(*phModule);
return FALSE;
}
#if 0 && DBG
/* Dump keyboard layout */
DumpKbdLayout(*pKbdTables);
#endif
return TRUE;
}
/*
* UserLoadKbdFile
*
* Loads keyboard layout DLL and creates KBDFILE object
*/
static PKBDFILE
UserLoadKbdFile(PUNICODE_STRING pwszKLID)
{
PKBDFILE pkf, pRet = NULL;
NTSTATUS Status;
ULONG cbSize;
HKEY hKey = NULL;
WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\";
WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
L"Control\\Keyboard Layouts\\";
/* Create keyboard layout file object */
pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE));
if (!pkf)
{
ERR("Failed to create object!\n");
return NULL;
}
/* Set keyboard layout name */
swprintf(pkf->awchKF, L"%wZ", pwszKLID);
/* Open layout registry key */
RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF);
Status = RegOpenKey(wszLayoutRegKey, &hKey);
if (!NT_SUCCESS(Status))
{
ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status);
goto cleanup;
}
/* Read filename of layout DLL */
cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR));
Status = RegQueryValue(hKey,
L"Layout File",
REG_SZ,
wszLayoutPath + wcslen(wszLayoutPath),
&cbSize);
if (!NT_SUCCESS(Status))
{
ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status);
goto cleanup;
}
/* Load keyboard file now */
if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl))
{
ERR("Failed to load %ws dll!\n", wszLayoutPath);
goto cleanup;
}
/* Update next field */
pkf->pkfNext = gpkfList;
gpkfList = pkf;
/* Return keyboard file */
pRet = pkf;
cleanup:
if (hKey)
ZwClose(hKey);
if (pkf)
UserDereferenceObject(pkf); // we dont need ptr anymore
if (!pRet)
{
/* We have failed - destroy created object */
if (pkf)
UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE);
}
return pRet;
}
/*
* co_UserLoadKbdLayout
*
* Loads keyboard layout and creates KL object
*/
static PKL
co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL)
{
LCID lCid;
CHARSETINFO cs;
PKL pKl;
/* Create keyboard layout object */
pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL));
if (!pKl)
{
ERR("Failed to create object!\n");
return NULL;
}
pKl->hkl = hKL;
pKl->spkf = UserLoadKbdFile(pustrKLID);
/* Dereference keyboard layout */
UserDereferenceObject(pKl);
/* If we failed, remove KL object */
if (!pKl->spkf)
{
ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID);
UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
return NULL;
}
// Up to Language Identifiers..
if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid)))
{
ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID);
UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
return NULL;
}
TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid);
if (co_IntGetCharsetInfo(lCid, &cs))
{
pKl->iBaseCharset = cs.ciCharset;
pKl->dwFontSigs = cs.fs.fsCsb[0];
pKl->CodePage = (USHORT)cs.ciACP;
TRACE("Charset %u Font Sig %lu CodePage %u\n",
pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage);
}
else
{
pKl->iBaseCharset = ANSI_CHARSET;
pKl->dwFontSigs = FS_LATIN1;
pKl->CodePage = CP_ACP;
}
// Set initial system character set and font signature.
if (gSystemFS == 0)
{
gSystemCPCharSet = pKl->iBaseCharset;
gSystemFS = pKl->dwFontSigs;
}
return pKl;
}
/*
* UnloadKbdFile
*
* Destroys specified Keyboard File object
*/
static
VOID
UnloadKbdFile(_In_ PKBDFILE pkf)
{
PKBDFILE *ppkfLink = &gpkfList;
NT_ASSERT(pkf != NULL);
/* Find previous object */
while (*ppkfLink)
{
if (*ppkfLink == pkf)
break;
ppkfLink = &(*ppkfLink)->pkfNext;
}
if (*ppkfLink == pkf)
*ppkfLink = pkf->pkfNext;
EngUnloadImage(pkf->hBase);
UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE);
}
/*
* UserUnloadKbl
*
* Unloads specified Keyboard Layout if possible
*/
BOOL
UserUnloadKbl(PKL pKl)
{
/* According to msdn, UnloadKeyboardLayout can fail
if the keyboard layout identifier was preloaded. */
if (pKl == gspklBaseLayout)
{
if (pKl->pklNext == pKl->pklPrev)
{
/* There is only one layout */
return FALSE;
}
/* Set next layout as default */
gspklBaseLayout = pKl->pklNext;
}
if (pKl->head.cLockObj > 1)
{
/* Layout is used by other threads */
pKl->dwKL_Flags |= KLF_UNLOAD;
return FALSE;
}
/* Unload the layout */
pKl->pklPrev->pklNext = pKl->pklNext;
pKl->pklNext->pklPrev = pKl->pklPrev;
UnloadKbdFile(pKl->spkf);
if (pKl->piiex)
{
ExFreePoolWithTag(pKl->piiex, USERTAG_IME);
}
UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
return TRUE;
}
/*
* W32kGetDefaultKeyLayout
*
* Returns default layout for new threads
*/
PKL
W32kGetDefaultKeyLayout(VOID)
{
PKL pKl = gspklBaseLayout;
if (!pKl)
return NULL;
/* Return not unloaded layout */
do
{
if (!(pKl->dwKL_Flags & KLF_UNLOAD))
return pKl;
pKl = pKl->pklPrev; /* Confirmed on Win2k */
} while(pKl != gspklBaseLayout);
/* We have not found proper KL */
return NULL;
}
/*
* UserHklToKbl
*
* Gets KL object from hkl value
*/
PKL
NTAPI
UserHklToKbl(HKL hKl)
{
PKL pKl = gspklBaseLayout;
if (!gspklBaseLayout)
return NULL;
do
{
if (pKl->hkl == hKl)
return pKl;
pKl = pKl->pklNext;
} while (pKl != gspklBaseLayout);
return NULL;
}
// Win: ReorderKeyboardLayouts
VOID FASTCALL
IntReorderKeyboardLayouts(
_Inout_ PWINSTATION_OBJECT pWinSta,
_Inout_ PKL pNewKL)
{
PKL pOldKL = gspklBaseLayout;
if ((pWinSta->Flags & WSS_NOIO) || pNewKL == pOldKL)
return;
pNewKL->pklPrev->pklNext = pNewKL->pklNext;
pNewKL->pklNext->pklPrev = pNewKL->pklPrev;
pNewKL->pklNext = pOldKL;
pNewKL->pklPrev = pOldKL->pklPrev;
pOldKL->pklPrev->pklNext = pNewKL;
pOldKL->pklPrev = pNewKL;
gspklBaseLayout = pNewKL; /* Should we use UserAssignmentLock? */
}
/*
* UserSetDefaultInputLang
*
* Sets default keyboard layout for system. Called from UserSystemParametersInfo.
*/
BOOL
NTAPI
UserSetDefaultInputLang(HKL hKl)
{
PKL pKl;
pKl = UserHklToKbl(hKl);
if (!pKl)
return FALSE;
IntReorderKeyboardLayouts(IntGetProcessWindowStation(NULL), pKl);
return TRUE;
}
/*
* co_UserActivateKbl
*
* Activates given layout in specified thread
*/
static PKL
co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags)
{
PKL pklPrev;
PWND pWnd;
pklPrev = pti->KeyboardLayout;
UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKl);
pti->pClientInfo->hKL = pKl->hkl;
if (Flags & KLF_SETFORPROCESS)
{
FIXME("KLF_SETFORPROCESS\n");
}
if (!(pWnd = pti->MessageQueue->spwndFocus))
{
pWnd = pti->MessageQueue->spwndActive;
}
// Send WM_INPUTLANGCHANGE to thread's focus window
co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0,
WM_INPUTLANGCHANGE,
(WPARAM)pKl->iBaseCharset, // FIXME: How to set it?
(LPARAM)pKl->hkl); // hkl
return pklPrev;
}
VOID APIENTRY
IntImmActivateLayout(
_Inout_ PTHREADINFO pti,
_Inout_ PKL pKL)
{
PWND pImeWnd;
HWND hImeWnd;
USER_REFERENCE_ENTRY Ref;
if (pti->KeyboardLayout == pKL)
return;
pImeWnd = pti->spwndDefaultIme;
if (pImeWnd)
{
UserRefObjectCo(pImeWnd, &Ref);
hImeWnd = UserHMGetHandle(pImeWnd);
co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl);
UserDerefObjectCo(pImeWnd);
}
else
{
/* Remember old keyboard layout to switch back for Chinese IMEs */
pti->hklPrev = pti->KeyboardLayout->hkl;
if (pti->spDefaultImc)
{
/* IME Activation is needed */
pti->pClientInfo->CI_flags |= CI_IMMACTIVATE;
}
}
UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL);
pti->pClientInfo->hKL = pKL->hkl;
pti->pClientInfo->CodePage = pKL->CodePage;
}
static VOID co_IntSetKeyboardLayoutForProcess(PPROCESSINFO ppi, PKL pKL)
{
PTHREADINFO ptiNode, ptiNext;
PCLIENTINFO pClientInfo;
BOOL bImmMode = IS_IMM_MODE();
for (ptiNode = ppi->ptiList; ptiNode; ptiNode = ptiNext)
{
IntReferenceThreadInfo(ptiNode);
ptiNext = ptiNode->ptiSibling;
/* Skip this thread if its keyboard layout is already the correct one, or if it's dying */
if (ptiNode->KeyboardLayout == pKL || (ptiNode->TIF_flags & TIF_INCLEANUP))
{
IntDereferenceThreadInfo(ptiNode);
continue;
}
if (bImmMode)
{
IntImmActivateLayout(ptiNode, pKL);
}
else
{
UserAssignmentLock((PVOID*)&ptiNode->KeyboardLayout, pKL);
pClientInfo = ptiNode->pClientInfo;
pClientInfo->CodePage = pKL->CodePage;
pClientInfo->hKL = pKL->hkl;
}
IntDereferenceThreadInfo(ptiNode);
}
}
HKL APIENTRY
co_UserActivateKeyboardLayout(
_Inout_ PKL pKL,
_In_ ULONG uFlags,
_Inout_ PWND pWnd)
{
HKL hOldKL = NULL;
PKL pOldKL = NULL;
PTHREADINFO pti = GetW32ThreadInfo();
PWND pTargetWnd, pImeWnd;
HWND hTargetWnd, hImeWnd;
USER_REFERENCE_ENTRY Ref1, Ref2;
PCLIENTINFO ClientInfo;
BOOL bSetForProcess = !!(uFlags & KLF_SETFORPROCESS);
IntReferenceThreadInfo(pti);
ClientInfo = pti->pClientInfo;
if (pti->KeyboardLayout)
{
pOldKL = pti->KeyboardLayout;
if (pOldKL)
hOldKL = pOldKL->hkl;
}
if (uFlags & KLF_RESET)
{
FIXME("KLF_RESET\n");
}
if (!bSetForProcess && pKL == pti->KeyboardLayout)
{
IntDereferenceThreadInfo(pti);
return hOldKL;
}
pKL->wchDiacritic = 0;
if (pOldKL)
UserRefObjectCo(pOldKL, &Ref1);
if (pti->TIF_flags & TIF_CSRSSTHREAD)
{
UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
ClientInfo->CodePage = pKL->CodePage;
ClientInfo->hKL = pKL->hkl;
}
else if (bSetForProcess)
{
co_IntSetKeyboardLayoutForProcess(pti->ppi, pKL);
}
else
{
if (IS_IMM_MODE())
IntImmActivateLayout(pti, pKL);
else
UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
ClientInfo->CodePage = pKL->CodePage;
ClientInfo->hKL = pKL->hkl;
}
if (gptiForeground && (gptiForeground->ppi == pti->ppi))
{
/* Send shell message */
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl);
}
if (pti->MessageQueue)
{
/* Determine the target window */
pTargetWnd = pti->MessageQueue->spwndFocus;
if (!pTargetWnd)
{
pTargetWnd = pti->MessageQueue->spwndActive;
if (!pTargetWnd)
pTargetWnd = pWnd;
}
/* Send WM_INPUTLANGCHANGE message */
if (pTargetWnd)
{
UserRefObjectCo(pTargetWnd, &Ref2);
hTargetWnd = UserHMGetHandle(pTargetWnd);
co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl);
UserDerefObjectCo(pTargetWnd);
}
}
/* Send WM_IME_SYSTEM:IMS_SENDNOTIFICATION message if necessary */
if (pti && !(pti->TIF_flags & TIF_CSRSSTHREAD))
{
if (IS_IME_HKL(pKL->hkl) || IS_CICERO_MODE())
{
pImeWnd = pti->spwndDefaultIme;
if (pImeWnd)
{
UserRefObjectCo(pImeWnd, &Ref2);
hImeWnd = UserHMGetHandle(pImeWnd);
co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bSetForProcess);
UserDerefObjectCo(pImeWnd);
}
}
}
if (pOldKL)
UserDerefObjectCo(pOldKL);
IntDereferenceThreadInfo(pti);
return hOldKL;
}
/* Win: xxxActivateKeyboardLayout */
HKL APIENTRY
co_IntActivateKeyboardLayout(
_Inout_ PWINSTATION_OBJECT pWinSta,
_In_ HKL hKL,
_In_ ULONG uFlags,
_Inout_ PWND pWnd)
{
PKL pKL;
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
pKL = IntHKLtoPKL(pti, hKL);
if (!pKL)
{
ERR("Invalid HKL %p!\n", hKL);
return NULL;
}
if (uFlags & KLF_REORDER)
IntReorderKeyboardLayouts(pWinSta, pKL);
return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd);
}
// Win: xxxInternalUnloadKeyboardLayout
static BOOL APIENTRY
co_IntUnloadKeyboardLayoutEx(
_Inout_ PWINSTATION_OBJECT pWinSta,
_Inout_ PKL pKL,
_In_ DWORD dwFlags)
{
PKL pNextKL;
USER_REFERENCE_ENTRY Ref1, Ref2;
PTHREADINFO pti = gptiCurrent;
if (pKL == gspklBaseLayout && !(dwFlags & 0x80000000))
return FALSE;
UserRefObjectCo(pKL, &Ref1); /* Add reference */
/* Regard as unloaded */
UserMarkObjectDestroy(pKL);
pKL->dwKL_Flags |= KLF_UNLOAD;
if (!(dwFlags & 0x80000000) && pti->KeyboardLayout == pKL)
{
pNextKL = IntHKLtoPKL(pti, (HKL)(ULONG_PTR)HKL_NEXT);
if (pNextKL)
{
UserRefObjectCo(pNextKL, &Ref2); /* Add reference */
co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL);
UserDerefObjectCo(pNextKL); /* Release reference */
}
}
if (gspklBaseLayout == pKL && pKL != pKL->pklNext)
{
/* Set next layout as default (FIXME: Use UserAssignmentLock?) */
gspklBaseLayout = pKL->pklNext;
}
UserDerefObjectCo(pKL); /* Release reference */
if (pti->pDeskInfo->fsHooks)
{
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0);
gLCIDSentToShell = 0;
}
return TRUE;
}
// Win: xxxUnloadKeyboardLayout
static BOOL APIENTRY
IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL)
{
PKL pKL = IntHKLtoPKL(gptiCurrent, hKL);
if (!pKL)
{
ERR("Invalid HKL %p!\n", hKL);
return FALSE;
}
return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0);
}
PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL)
{
PIMEINFOEX piiex;
if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE())
return NULL;
piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME);
if (!piiex)
return NULL;
if (!co_ClientImmLoadLayout(hKL, piiex))
{
ExFreePoolWithTag(piiex, USERTAG_IME);
return NULL;
}
return piiex;
}
HKL APIENTRY
co_IntLoadKeyboardLayoutEx(
IN OUT PWINSTATION_OBJECT pWinSta,
IN HANDLE hSafeFile,
IN HKL hOldKL,
IN PUNICODE_STRING puszSafeKLID,
IN HKL hNewKL,
IN UINT Flags)
{
PKL pOldKL, pNewKL;
UNREFERENCED_PARAMETER(hSafeFile);
if (hNewKL == NULL || (pWinSta->Flags & WSS_NOIO))
return NULL;
/* If hOldKL is specified, unload it and load new layput as default */
if (hOldKL && hOldKL != hNewKL)
{
pOldKL = UserHklToKbl(hOldKL);
if (pOldKL)
UserUnloadKbl(pOldKL);
}
/* FIXME: It seems KLF_RESET is only supported for WINLOGON */
/* Let's see if layout was already loaded. */
pNewKL = UserHklToKbl(hNewKL);
if (!pNewKL)
{
/* It wasn't, so load it. */
pNewKL = co_UserLoadKbdLayout(puszSafeKLID, hNewKL);
if (!pNewKL)
return NULL;
if (gspklBaseLayout)
{
/* Find last not unloaded layout */
PKL pLastKL = gspklBaseLayout->pklPrev;
while (pLastKL != gspklBaseLayout && (pLastKL->dwKL_Flags & KLF_UNLOAD))
pLastKL = pLastKL->pklPrev;
/* Add new layout to the list */
pNewKL->pklNext = pLastKL->pklNext;
pNewKL->pklPrev = pLastKL;
pNewKL->pklNext->pklPrev = pNewKL;
pNewKL->pklPrev->pklNext = pNewKL;
}
else
{
/* This is the first layout */
pNewKL->pklNext = pNewKL;
pNewKL->pklPrev = pNewKL;
gspklBaseLayout = pNewKL;
}
pNewKL->piiex = co_UserImmLoadLayout(hNewKL);
}
/* If this layout was prepared to unload, undo it */
pNewKL->dwKL_Flags &= ~KLF_UNLOAD;
/* Reorder if necessary */
if (Flags & KLF_REORDER)
IntReorderKeyboardLayouts(pWinSta, pNewKL);
/* Activate this layout in current thread */
if (Flags & KLF_ACTIVATE)
co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pNewKL, Flags);
/* Send shell message */
if (!(Flags & KLF_NOTELLSHELL))
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hNewKL);
/* FIXME: KLF_REPLACELANG */
return hNewKL;
}
HANDLE FASTCALL IntVerifyKeyboardFileHandle(HANDLE hFile)
{
PFILE_OBJECT FileObject;
NTSTATUS Status;
if (hFile == INVALID_HANDLE_VALUE)
return NULL;
Status = ObReferenceObjectByHandle(hFile, FILE_READ_DATA, NULL, UserMode,
(PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
ERR("0x%08X\n", Status);
return NULL;
}
/* FIXME: Is the file in the system directory? */
if (FileObject)
ObDereferenceObject(FileObject);
return hFile;
}
/* EXPORTS *******************************************************************/
/*
* UserGetKeyboardLayout
*
* Returns hkl of given thread keyboard layout
*/
HKL FASTCALL
UserGetKeyboardLayout(
DWORD dwThreadId)
{
PTHREADINFO pti;
PLIST_ENTRY ListEntry;
PKL pKl;
pti = PsGetCurrentThreadWin32Thread();
if (!dwThreadId)
{
pKl = pti->KeyboardLayout;
return pKl ? pKl->hkl : NULL;
}
ListEntry = pti->rpdesk->PtiList.Flink;
//
// Search the Desktop Thread list for related Desktop active Threads.
//
while(ListEntry != &pti->rpdesk->PtiList)
{
pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId))
{
pKl = pti->KeyboardLayout;
return pKl ? pKl->hkl : NULL;
}
ListEntry = ListEntry->Flink;
}
return NULL;
}
/*
* NtUserGetKeyboardLayoutList
*
* Returns list of loaded keyboard layouts in system
*/
UINT
APIENTRY
NtUserGetKeyboardLayoutList(
ULONG nBuff,
HKL *pHklBuff)
{
UINT ret = 0;
PWINSTATION_OBJECT pWinSta;
if (!pHklBuff)
nBuff = 0;
UserEnterShared();
if (nBuff > MAXULONG / sizeof(HKL))
{
SetLastNtError(ERROR_INVALID_PARAMETER);
goto Quit;
}
_SEH2_TRY
{
ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
goto Quit;
}
_SEH2_END;
pWinSta = IntGetProcessWindowStation(NULL);
_SEH2_TRY
{
ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
goto Quit;
}
_SEH2_END;
Quit:
UserLeave();
return ret;
}
/*
* NtUserGetKeyboardLayoutName
*
* Returns KLID of current thread keyboard layout
*/
BOOL
APIENTRY
NtUserGetKeyboardLayoutName(
_Inout_ PUNICODE_STRING pustrName)
{
BOOL bRet = FALSE;
PKL pKl;
PTHREADINFO pti;
UNICODE_STRING ustrNameSafe;
NTSTATUS Status;
UserEnterShared();
pti = PsGetCurrentThreadWin32Thread();
pKl = pti->KeyboardLayout;
if (!pKl)
goto cleanup;
_SEH2_TRY
{
ProbeForWriteUnicodeString(pustrName);
ustrNameSafe = *pustrName;
ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1);
if (IS_IME_HKL(pKl->hkl))
{
Status = RtlIntegerToUnicodeString((ULONG)(ULONG_PTR)pKl->hkl, 16, &ustrNameSafe);
}
else
{
if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
goto cleanup;
}
/* FIXME: Do not use awchKF */
ustrNameSafe.Length = 0;
Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF);
}
if (NT_SUCCESS(Status))
{
*pustrName = ustrNameSafe;
bRet = TRUE;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
cleanup:
UserLeave();
return bRet;
}
/*
* NtUserLoadKeyboardLayoutEx
*
* Loads keyboard layout with given locale id
*
* NOTE: We adopt a different design from Microsoft's one due to security reason.
* We don't use the 3rd parameter of NtUserLoadKeyboardLayoutEx.
* See https://bugtraq.securityfocus.com/detail/50056B96.6040306
*/
HKL
NTAPI
NtUserLoadKeyboardLayoutEx(
IN HANDLE hFile,
IN DWORD offTable,
IN PVOID pTables,
IN HKL hOldKL,
IN PUNICODE_STRING puszKLID,
IN DWORD dwNewKL,
IN UINT Flags)
{
HKL hRetKL;
WCHAR Buffer[KL_NAMELENGTH];
UNICODE_STRING uszSafeKLID;
PWINSTATION_OBJECT pWinSta;
HANDLE hSafeFile;
UNREFERENCED_PARAMETER(offTable);
UNREFERENCED_PARAMETER(pTables);
if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
KLF_RESET|KLF_SHIFTLOCK))
{
ERR("Invalid flags: %x\n", Flags);
EngSetLastError(ERROR_INVALID_FLAGS);
return NULL;
}
RtlInitEmptyUnicodeString(&uszSafeKLID, Buffer, sizeof(Buffer));
_SEH2_TRY
{
ProbeForRead(puszKLID, sizeof(*puszKLID), 1);
ProbeForRead(puszKLID->Buffer, sizeof(puszKLID->Length), 1);
RtlCopyUnicodeString(&uszSafeKLID, puszKLID);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return NULL);
}
_SEH2_END;
UserEnterExclusive();
hSafeFile = (hFile ? IntVerifyKeyboardFileHandle(hFile) : NULL);
pWinSta = IntGetProcessWindowStation(NULL);
hRetKL = co_IntLoadKeyboardLayoutEx(pWinSta,
hSafeFile,
hOldKL,
&uszSafeKLID,
(HKL)(DWORD_PTR)dwNewKL,
Flags);
if (hSafeFile)
ZwClose(hSafeFile);
UserLeave();
return hRetKL;
}
/*
* NtUserActivateKeyboardLayout
*
* Activates specified layout for thread or process
*/
HKL
NTAPI
NtUserActivateKeyboardLayout(
HKL hKL,
ULONG Flags)
{
PWINSTATION_OBJECT pWinSta;
HKL hOldKL;
UserEnterExclusive();
/* FIXME */
pWinSta = IntGetProcessWindowStation(NULL);
hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL);
UserLeave();
return hOldKL;
}
/*
* NtUserUnloadKeyboardLayout
*
* Unloads keyboard layout with specified hkl value
*/
BOOL
APIENTRY
NtUserUnloadKeyboardLayout(
HKL hKl)
{
BOOL ret;
PWINSTATION_OBJECT pWinSta;
UserEnterExclusive();
pWinSta = IntGetProcessWindowStation(NULL);
ret = IntUnloadKeyboardLayout(pWinSta, hKl);
UserLeave();
return ret;
}
/* EOF */