mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
784 lines
18 KiB
C
784 lines
18 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
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
|
|
#include <winnls.h>
|
|
|
|
DBG_DEFAULT_CHANNEL(UserKbdLayout);
|
|
|
|
PKL gspklBaseLayout = NULL;
|
|
PKBDFILE gpkfList = NULL;
|
|
DWORD gSystemFS = 0;
|
|
UINT gSystemCPCharSet = 0;
|
|
|
|
typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID);
|
|
|
|
|
|
/* PRIVATE FUNCTIONS ******************************************************/
|
|
|
|
/*
|
|
* 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 /* Dump keyboard layout */
|
|
{
|
|
unsigned i;
|
|
PVK_TO_BIT pVkToBit = (*pKbdTables)->pCharModifiers->pVkToBit;
|
|
PVK_TO_WCHAR_TABLE pVkToWchTbl = (*pKbdTables)->pVkToWcharTable;
|
|
PVSC_VK pVscVk = (*pKbdTables)->pVSCtoVK_E0;
|
|
DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", (*pKbdTables)->fLocaleFlags, (*pKbdTables)->bMaxVSCtoVK);
|
|
DbgPrint("wMaxModBits %x\n", (*pKbdTables)->pCharModifiers->wMaxModBits);
|
|
while (pVkToBit->Vk)
|
|
{
|
|
DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits);
|
|
++pVkToBit;
|
|
}
|
|
for (i = 0; i <= (*pKbdTables)->pCharModifiers->wMaxModBits; ++i)
|
|
DbgPrint("ModNumber %x -> %x\n", i, (*pKbdTables)->pCharModifiers->ModNumber[i]);
|
|
while (pVkToWchTbl->pVkToWchars)
|
|
{
|
|
PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars;
|
|
DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl->nModifications, pVkToWchTbl->cbSize);
|
|
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);
|
|
}
|
|
++pVkToWchTbl;
|
|
}
|
|
DbgPrint("pusVSCtoVK: { ");
|
|
for (i = 0; i < (*pKbdTables)->bMaxVSCtoVK; ++i)
|
|
DbgPrint("%x -> %x, ", i, (*pKbdTables)->pusVSCtoVK[i]);
|
|
DbgPrint("}\n");
|
|
DbgPrint("pVSCtoVK_E0: { ");
|
|
while (pVscVk->Vsc)
|
|
{
|
|
DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
|
|
++pVscVk;
|
|
}
|
|
DbgPrint("}\n");
|
|
pVscVk = (*pKbdTables)->pVSCtoVK_E1;
|
|
DbgPrint("pVSCtoVK_E1: { ");
|
|
while (pVscVk->Vsc)
|
|
{
|
|
DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
|
|
++pVscVk;
|
|
}
|
|
DbgPrint("}\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#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 = 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(pkf->head.h, TYPE_KBDFILE);
|
|
}
|
|
|
|
return pRet;
|
|
}
|
|
|
|
/*
|
|
* UserLoadKbdLayout
|
|
*
|
|
* Loads keyboard layout and creates KL object
|
|
*/
|
|
static PKL
|
|
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(pKl->head.h, TYPE_KBDLAYOUT);
|
|
return NULL;
|
|
}
|
|
|
|
// Up to Language Identifiers..
|
|
if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid)))
|
|
{
|
|
ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID);
|
|
UserDeleteObject(pKl->head.h, 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(pkf->head.h, 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);
|
|
UserDeleteObject(pKl->head.h, 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;
|
|
}
|
|
|
|
/*
|
|
* UserSetDefaultInputLang
|
|
*
|
|
* Sets default kyboard layout for system. Called from UserSystemParametersInfo.
|
|
*/
|
|
BOOL
|
|
NTAPI
|
|
UserSetDefaultInputLang(HKL hKl)
|
|
{
|
|
PKL pKl;
|
|
|
|
pKl = UserHklToKbl(hKl);
|
|
if (!pKl)
|
|
return FALSE;
|
|
|
|
gspklBaseLayout = 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;
|
|
if (pklPrev)
|
|
UserDereferenceObject(pklPrev);
|
|
|
|
pti->KeyboardLayout = pKl;
|
|
pti->pClientInfo->hKL = pKl->hkl;
|
|
UserReferenceObject(pKl);
|
|
|
|
if (Flags & KLF_SETFORPROCESS)
|
|
{
|
|
// FIXME
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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 uRet = 0;
|
|
PKL pKl;
|
|
|
|
if (!pHklBuff)
|
|
nBuff = 0;
|
|
|
|
UserEnterShared();
|
|
|
|
if (!gspklBaseLayout)
|
|
{
|
|
UserLeave();
|
|
return 0;
|
|
}
|
|
pKl = gspklBaseLayout;
|
|
|
|
if (nBuff == 0)
|
|
{
|
|
do
|
|
{
|
|
uRet++;
|
|
pKl = pKl->pklNext;
|
|
} while (pKl != gspklBaseLayout);
|
|
}
|
|
else
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(pHklBuff, nBuff*sizeof(HKL), 4);
|
|
|
|
while (uRet < nBuff)
|
|
{
|
|
pHklBuff[uRet] = pKl->hkl;
|
|
uRet++;
|
|
pKl = pKl->pklNext;
|
|
if (pKl == gspklBaseLayout)
|
|
break;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
uRet = 0;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
UserLeave();
|
|
return uRet;
|
|
}
|
|
|
|
/*
|
|
* NtUserGetKeyboardLayoutName
|
|
*
|
|
* Returns KLID of current thread keyboard layout
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetKeyboardLayoutName(
|
|
LPWSTR pwszName)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
PKL pKl;
|
|
PTHREADINFO pti;
|
|
|
|
UserEnterShared();
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
pKl = pti->KeyboardLayout;
|
|
|
|
if (!pKl)
|
|
goto cleanup;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(pwszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
|
|
wcscpy(pwszName, pKl->spkf->awchKF);
|
|
bRet = TRUE;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
cleanup:
|
|
UserLeave();
|
|
return bRet;
|
|
}
|
|
|
|
/*
|
|
* NtUserLoadKeyboardLayoutEx
|
|
*
|
|
* Loads keyboard layout with given locale id
|
|
*/
|
|
HKL
|
|
APIENTRY
|
|
NtUserLoadKeyboardLayoutEx(
|
|
IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
|
|
IN DWORD offTable, // Offset to KbdTables
|
|
IN PUNICODE_STRING puszKeyboardName, // Not used?
|
|
IN HKL hklUnload,
|
|
IN PUNICODE_STRING pustrKLID,
|
|
IN DWORD hkl,
|
|
IN UINT Flags)
|
|
{
|
|
HKL hklRet = NULL;
|
|
PKL pKl = NULL, pklLast;
|
|
WCHAR Buffer[9];
|
|
UNICODE_STRING ustrSafeKLID;
|
|
|
|
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;
|
|
}
|
|
|
|
/* FIXME: It seems KLF_RESET is only supported for WINLOGON */
|
|
|
|
RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer));
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1);
|
|
ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1);
|
|
RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
_SEH2_YIELD(return NULL);
|
|
}
|
|
_SEH2_END;
|
|
|
|
UserEnterExclusive();
|
|
|
|
/* If hklUnload is specified, unload it and load new layput as default */
|
|
if (hklUnload && hklUnload != (HKL)hkl)
|
|
{
|
|
pKl = UserHklToKbl(hklUnload);
|
|
if (pKl)
|
|
UserUnloadKbl(pKl);
|
|
}
|
|
|
|
/* Let's see if layout was already loaded. */
|
|
pKl = UserHklToKbl((HKL)hkl);
|
|
if (!pKl)
|
|
{
|
|
/* It wasn't, so load it. */
|
|
pKl = UserLoadKbdLayout(&ustrSafeKLID, (HKL)hkl);
|
|
if (!pKl)
|
|
goto cleanup;
|
|
|
|
if (gspklBaseLayout)
|
|
{
|
|
/* Find last not unloaded layout */
|
|
pklLast = gspklBaseLayout->pklPrev;
|
|
while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD)
|
|
pklLast = pklLast->pklPrev;
|
|
|
|
/* Add new layout to the list */
|
|
pKl->pklNext = pklLast->pklNext;
|
|
pKl->pklPrev = pklLast;
|
|
pKl->pklNext->pklPrev = pKl;
|
|
pKl->pklPrev->pklNext = pKl;
|
|
}
|
|
else
|
|
{
|
|
/* This is the first layout */
|
|
pKl->pklNext = pKl;
|
|
pKl->pklPrev = pKl;
|
|
gspklBaseLayout = pKl;
|
|
}
|
|
}
|
|
|
|
/* If this layout was prepared to unload, undo it */
|
|
pKl->dwKL_Flags &= ~KLF_UNLOAD;
|
|
|
|
/* Activate this layout in current thread */
|
|
if (Flags & KLF_ACTIVATE)
|
|
co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags);
|
|
|
|
/* Send shell message */
|
|
if (!(Flags & KLF_NOTELLSHELL))
|
|
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
|
|
|
|
/* Return hkl on success */
|
|
hklRet = (HKL)hkl;
|
|
|
|
/* FIXME: KLF_REPLACELANG
|
|
KLF_REORDER */
|
|
|
|
cleanup:
|
|
UserLeave();
|
|
return hklRet;
|
|
}
|
|
|
|
/*
|
|
* NtUserActivateKeyboardLayout
|
|
*
|
|
* Activates specified layout for thread or process
|
|
*/
|
|
HKL
|
|
APIENTRY
|
|
NtUserActivateKeyboardLayout(
|
|
HKL hKl,
|
|
ULONG Flags)
|
|
{
|
|
PKL pKl = NULL;
|
|
HKL hkl = NULL;
|
|
PTHREADINFO pti;
|
|
|
|
UserEnterExclusive();
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
/* hKl can have special value HKL_NEXT or HKL_PREV */
|
|
if (hKl == (HKL)HKL_NEXT)
|
|
{
|
|
/* Get next keyboard layout starting with current */
|
|
if (pti->KeyboardLayout)
|
|
pKl = pti->KeyboardLayout->pklNext;
|
|
}
|
|
else if (hKl == (HKL)HKL_PREV)
|
|
{
|
|
/* Get previous keyboard layout starting with current */
|
|
if (pti->KeyboardLayout)
|
|
pKl = pti->KeyboardLayout->pklPrev;
|
|
}
|
|
else
|
|
pKl = UserHklToKbl(hKl);
|
|
|
|
if (!pKl)
|
|
{
|
|
ERR("Invalid HKL %p!\n", hKl);
|
|
goto cleanup;
|
|
}
|
|
|
|
hkl = pKl->hkl;
|
|
|
|
/* FIXME: KLF_RESET
|
|
KLF_SHIFTLOCK */
|
|
|
|
if (Flags & KLF_REORDER)
|
|
gspklBaseLayout = pKl;
|
|
|
|
if (pKl != pti->KeyboardLayout)
|
|
{
|
|
/* Activate layout for current thread */
|
|
pKl = co_UserActivateKbl(pti, pKl, Flags);
|
|
|
|
/* Send shell message */
|
|
if (!(Flags & KLF_NOTELLSHELL))
|
|
co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
|
|
}
|
|
|
|
cleanup:
|
|
UserLeave();
|
|
return hkl;
|
|
}
|
|
|
|
/*
|
|
* NtUserUnloadKeyboardLayout
|
|
*
|
|
* Unloads keyboard layout with specified hkl value
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserUnloadKeyboardLayout(
|
|
HKL hKl)
|
|
{
|
|
PKL pKl;
|
|
BOOL bRet = FALSE;
|
|
|
|
UserEnterExclusive();
|
|
|
|
pKl = UserHklToKbl(hKl);
|
|
if (pKl)
|
|
bRet = UserUnloadKbl(pKl);
|
|
else
|
|
ERR("Invalid HKL %p!\n", hKl);
|
|
|
|
UserLeave();
|
|
return bRet;
|
|
}
|
|
|
|
/* EOF */
|