mirror of
https://github.com/reactos/reactos.git
synced 2025-01-11 16:51:06 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
691 lines
16 KiB
C
691 lines
16 KiB
C
|
|
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: subsystems/win32/win32k/ntuser/kbdlayout.c
|
|
* PURPOSE: Keyboard layout management
|
|
* COPYRIGHT: Copyright 2007 Saveliy Tretiakov
|
|
* Copyright 2008 Colin Finck
|
|
*
|
|
*/
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <win32k.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
PKBL KBLList = NULL; // Keyboard layout list.
|
|
|
|
typedef PVOID (*KbdLayerDescriptor)(VOID);
|
|
NTSTATUS APIENTRY LdrGetProcedureAddress(PVOID module,
|
|
PANSI_STRING import_name,
|
|
DWORD flags,
|
|
PVOID *func_addr);
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS ******************************************************/
|
|
|
|
|
|
/*
|
|
* Utility function to read a value from the registry more easily.
|
|
*
|
|
* IN PUNICODE_STRING KeyName -> Name of key to open
|
|
* IN PUNICODE_STRING ValueName -> Name of value to open
|
|
* OUT PUNICODE_STRING ReturnedValue -> String contained in registry
|
|
*
|
|
* Returns NTSTATUS
|
|
*/
|
|
|
|
static NTSTATUS APIENTRY ReadRegistryValue( PUNICODE_STRING KeyName,
|
|
PUNICODE_STRING ValueName,
|
|
PUNICODE_STRING ReturnedValue )
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle;
|
|
OBJECT_ATTRIBUTES KeyAttributes;
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
|
|
ULONG Length = 0;
|
|
ULONG ResLength = 0;
|
|
PWCHAR ReturnBuffer;
|
|
|
|
InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
|
|
NULL, NULL);
|
|
Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
|
|
if( !NT_SUCCESS(Status) )
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
|
|
0,
|
|
0,
|
|
&ResLength);
|
|
|
|
if( Status != STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
ResLength += sizeof( *KeyValuePartialInfo );
|
|
KeyValuePartialInfo =
|
|
ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);
|
|
Length = ResLength;
|
|
|
|
if( !KeyValuePartialInfo )
|
|
{
|
|
NtClose(KeyHandle);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = ZwQueryValueKey(KeyHandle,
|
|
ValueName,
|
|
KeyValuePartialInformation,
|
|
(PVOID)KeyValuePartialInfo,
|
|
Length,
|
|
&ResLength);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
{
|
|
NtClose(KeyHandle);
|
|
ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
|
|
return Status;
|
|
}
|
|
|
|
/* At this point, KeyValuePartialInfo->Data contains the key data */
|
|
ReturnBuffer = ExAllocatePoolWithTag(PagedPool,
|
|
KeyValuePartialInfo->DataLength,
|
|
TAG_STRING);
|
|
|
|
if(!ReturnBuffer)
|
|
{
|
|
NtClose(KeyHandle);
|
|
ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
RtlCopyMemory(ReturnBuffer,
|
|
KeyValuePartialInfo->Data,
|
|
KeyValuePartialInfo->DataLength);
|
|
RtlInitUnicodeString(ReturnedValue, ReturnBuffer);
|
|
|
|
ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
|
|
NtClose(KeyHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static BOOL UserLoadKbdDll(WCHAR *wsKLID,
|
|
HANDLE *phModule,
|
|
PKBDTABLES *pKbdTables)
|
|
{
|
|
NTSTATUS Status;
|
|
KbdLayerDescriptor layerDescGetFn;
|
|
ANSI_STRING kbdProcedureName;
|
|
UNICODE_STRING LayoutKeyName;
|
|
UNICODE_STRING LayoutValueName;
|
|
UNICODE_STRING LayoutFile;
|
|
UNICODE_STRING FullLayoutPath;
|
|
UNICODE_STRING klid;
|
|
WCHAR LayoutPathBuffer[MAX_PATH] = L"\\SystemRoot\\System32\\";
|
|
WCHAR KeyNameBuffer[MAX_PATH] = L"\\REGISTRY\\Machine\\SYSTEM\\"
|
|
L"CurrentControlSet\\Control\\Keyboard Layouts\\";
|
|
|
|
RtlInitUnicodeString(&klid, wsKLID);
|
|
RtlInitUnicodeString(&LayoutValueName,L"Layout File");
|
|
RtlInitUnicodeString(&LayoutKeyName, KeyNameBuffer);
|
|
LayoutKeyName.MaximumLength = sizeof(KeyNameBuffer);
|
|
|
|
RtlAppendUnicodeStringToString(&LayoutKeyName, &klid);
|
|
Status = ReadRegistryValue(&LayoutKeyName, &LayoutValueName, &LayoutFile);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Can't get layout filename for %wZ. (%08lx)\n", klid, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
DPRINT("Read registry and got %wZ\n", &LayoutFile);
|
|
RtlInitUnicodeString(&FullLayoutPath, LayoutPathBuffer);
|
|
FullLayoutPath.MaximumLength = sizeof(LayoutPathBuffer);
|
|
RtlAppendUnicodeStringToString(&FullLayoutPath, &LayoutFile);
|
|
DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
|
|
ExFreePoolWithTag(LayoutFile.Buffer, TAG_STRING);
|
|
|
|
*phModule = EngLoadImage(FullLayoutPath.Buffer);
|
|
|
|
if(*phModule)
|
|
{
|
|
DPRINT("Loaded %wZ\n", &FullLayoutPath);
|
|
|
|
RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
|
|
LdrGetProcedureAddress((*(PDRIVERS*)phModule)->BaseAddress,
|
|
&kbdProcedureName,
|
|
0,
|
|
(PVOID*)&layerDescGetFn);
|
|
|
|
if(layerDescGetFn)
|
|
{
|
|
*pKbdTables = layerDescGetFn();
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath);
|
|
}
|
|
|
|
if(!layerDescGetFn || !*pKbdTables)
|
|
{
|
|
DPRINT1("Failed to load the keyboard layout.\n");
|
|
EngUnloadImage(*phModule);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static PKBL UserLoadDllAndCreateKbl(DWORD LocaleId)
|
|
{
|
|
PKBL NewKbl;
|
|
ULONG hKl;
|
|
LANGID langid;
|
|
|
|
NewKbl = ExAllocatePoolWithTag(PagedPool, sizeof(KBL), TAG_KEYBOARD);
|
|
|
|
if(!NewKbl)
|
|
{
|
|
DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
|
|
swprintf(NewKbl->Name, L"%08lx", LocaleId);
|
|
|
|
if(!UserLoadKbdDll(NewKbl->Name, &NewKbl->hModule, &NewKbl->KBTables))
|
|
{
|
|
DPRINT("%s: failed to load %x dll!\n", __FUNCTION__, LocaleId);
|
|
ExFreePoolWithTag(NewKbl, TAG_KEYBOARD);
|
|
return NULL;
|
|
}
|
|
|
|
/* Microsoft Office expects this value to be something specific
|
|
* for Japanese and Korean Windows with an IME the value is 0xe001
|
|
* We should probably check to see if an IME exists and if so then
|
|
* set this word properly.
|
|
*/
|
|
langid = PRIMARYLANGID(LANGIDFROMLCID(LocaleId));
|
|
hKl = LocaleId;
|
|
|
|
if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
|
|
hKl |= 0xe001 << 16; /* FIXME */
|
|
else hKl |= hKl << 16;
|
|
|
|
NewKbl->hkl = (HKL)(ULONG_PTR) hKl;
|
|
NewKbl->klid = LocaleId;
|
|
NewKbl->Flags = 0;
|
|
NewKbl->RefCount = 0;
|
|
|
|
return NewKbl;
|
|
}
|
|
|
|
BOOL UserInitDefaultKeyboardLayout()
|
|
{
|
|
LCID LocaleId;
|
|
NTSTATUS Status;
|
|
|
|
Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Could not get default locale (%08lx).\n", Status);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("DefaultLocale = %08lx\n", LocaleId);
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status) || !(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
|
|
{
|
|
DPRINT1("Trying to load US Keyboard Layout.\n");
|
|
LocaleId = 0x409;
|
|
|
|
if(!(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
|
|
{
|
|
DPRINT1("Failed to load any Keyboard Layout\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
KBLList->Flags |= KBL_PRELOAD;
|
|
|
|
InitializeListHead(&KBLList->List);
|
|
return TRUE;
|
|
}
|
|
|
|
PKBL W32kGetDefaultKeyLayout(VOID)
|
|
{
|
|
const WCHAR szKeyboardLayoutPath[] = L"\\Keyboard Layout\\Preload";
|
|
const WCHAR szDefaultUserPath[] = L"\\REGISTRY\\USER\\.DEFAULT";
|
|
|
|
HANDLE KeyHandle;
|
|
LCID LayoutLocaleId = 0;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES KeyAttributes;
|
|
PKBL pKbl;
|
|
UNICODE_STRING CurrentUserPath;
|
|
UNICODE_STRING FullKeyboardLayoutPath;
|
|
UNICODE_STRING LayoutValueName;
|
|
UNICODE_STRING LayoutLocaleIdString;
|
|
WCHAR wszBuffer[MAX_PATH];
|
|
|
|
// Get the path to HKEY_CURRENT_USER
|
|
Status = RtlFormatCurrentUserKeyPath(&CurrentUserPath);
|
|
|
|
if( NT_SUCCESS(Status) )
|
|
{
|
|
// FIXME: Is this 100% correct?
|
|
// We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.
|
|
InitializeObjectAttributes(&KeyAttributes, &CurrentUserPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
|
|
|
|
if(Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
// It is not available, so read it from HKEY_USERS\.DEFAULT
|
|
RtlCopyMemory(wszBuffer, szDefaultUserPath, sizeof(szDefaultUserPath));
|
|
}
|
|
else
|
|
{
|
|
// The path is available
|
|
ZwClose(KeyHandle);
|
|
RtlCopyMemory(wszBuffer, CurrentUserPath.Buffer, CurrentUserPath.MaximumLength);
|
|
}
|
|
|
|
// Build the full path
|
|
RtlInitUnicodeString(&FullKeyboardLayoutPath, wszBuffer);
|
|
FullKeyboardLayoutPath.MaximumLength = MAX_PATH;
|
|
|
|
Status = RtlAppendUnicodeToString(&FullKeyboardLayoutPath, szKeyboardLayoutPath);
|
|
|
|
if( NT_SUCCESS(Status) )
|
|
{
|
|
// Return the first keyboard layout listed there
|
|
RtlInitUnicodeString(&LayoutValueName, L"1");
|
|
|
|
Status = ReadRegistryValue(&FullKeyboardLayoutPath, &LayoutValueName, &LayoutLocaleIdString);
|
|
|
|
if( NT_SUCCESS(Status) )
|
|
{
|
|
RtlUnicodeStringToInteger(&LayoutLocaleIdString, 16, &LayoutLocaleId);
|
|
ExFreePoolWithTag(LayoutLocaleIdString.Buffer, TAG_STRING);
|
|
}
|
|
else
|
|
DPRINT1("ReadRegistryValue failed! (%08lx).\n", Status);
|
|
}
|
|
else
|
|
DPRINT1("RtlAppendUnicodeToString failed! (%08lx)\n", Status);
|
|
|
|
RtlFreeUnicodeString(&CurrentUserPath);
|
|
}
|
|
else
|
|
DPRINT1("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status);
|
|
|
|
if(!LayoutLocaleId)
|
|
{
|
|
// This block is only reached in case of a failure, so use DPRINT1 here
|
|
DPRINT1("Assuming default locale for the keyboard layout (0x409 - US)\n");
|
|
LayoutLocaleId = 0x409;
|
|
}
|
|
|
|
pKbl = KBLList;
|
|
do
|
|
{
|
|
if(pKbl->klid == LayoutLocaleId)
|
|
{
|
|
return pKbl;
|
|
}
|
|
|
|
pKbl = (PKBL) pKbl->List.Flink;
|
|
} while(pKbl != KBLList);
|
|
|
|
DPRINT("Loading new default keyboard layout.\n");
|
|
pKbl = UserLoadDllAndCreateKbl(LayoutLocaleId);
|
|
|
|
if(!pKbl)
|
|
{
|
|
DPRINT("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId);
|
|
return KBLList;
|
|
}
|
|
|
|
InsertTailList(&KBLList->List, &pKbl->List);
|
|
return pKbl;
|
|
}
|
|
|
|
PKBL UserHklToKbl(HKL hKl)
|
|
{
|
|
PKBL pKbl = KBLList;
|
|
do
|
|
{
|
|
if(pKbl->hkl == hKl) return pKbl;
|
|
pKbl = (PKBL) pKbl->List.Flink;
|
|
} while(pKbl != KBLList);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL UserUnloadKbl(PKBL pKbl)
|
|
{
|
|
/* According to msdn, UnloadKeyboardLayout can fail
|
|
if the keyboard layout identifier was preloaded. */
|
|
|
|
if(pKbl->Flags & KBL_PRELOAD)
|
|
{
|
|
DPRINT1("Attempted to unload preloaded keyboard layout.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if(pKbl->RefCount > 0)
|
|
{
|
|
/* Layout is used by other threads.
|
|
Mark it as unloaded and don't do anything else. */
|
|
pKbl->Flags |= KBL_UNLOAD;
|
|
}
|
|
else
|
|
{
|
|
//Unload the layout
|
|
EngUnloadImage(pKbl->hModule);
|
|
RemoveEntryList(&pKbl->List);
|
|
ExFreePoolWithTag(pKbl, TAG_KEYBOARD);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static PKBL co_UserActivateKbl(PTHREADINFO w32Thread, PKBL pKbl, UINT Flags)
|
|
{
|
|
PKBL Prev;
|
|
|
|
Prev = w32Thread->KeyboardLayout;
|
|
Prev->RefCount--;
|
|
w32Thread->KeyboardLayout = pKbl;
|
|
pKbl->RefCount++;
|
|
|
|
if(Flags & KLF_SETFORPROCESS)
|
|
{
|
|
//FIXME
|
|
|
|
}
|
|
|
|
if(Prev->Flags & KBL_UNLOAD && Prev->RefCount == 0)
|
|
{
|
|
UserUnloadKbl(Prev);
|
|
}
|
|
|
|
// Send WM_INPUTLANGCHANGE to thread's focus window
|
|
co_IntSendMessage(w32Thread->MessageQueue->FocusWindow,
|
|
WM_INPUTLANGCHANGE,
|
|
0, // FIXME: put charset here (what is this?)
|
|
(LPARAM)pKbl->hkl); //klid
|
|
|
|
return Prev;
|
|
}
|
|
|
|
/* EXPORTS *******************************************************************/
|
|
|
|
HKL FASTCALL
|
|
UserGetKeyboardLayout(
|
|
DWORD dwThreadId)
|
|
{
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PTHREADINFO W32Thread;
|
|
HKL Ret;
|
|
|
|
if(!dwThreadId)
|
|
{
|
|
W32Thread = PsGetCurrentThreadWin32Thread();
|
|
return W32Thread->KeyboardLayout->hkl;
|
|
}
|
|
|
|
Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
SetLastWin32Error(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
W32Thread = PsGetThreadWin32Thread(Thread);
|
|
Ret = W32Thread->KeyboardLayout->hkl;
|
|
ObDereferenceObject(Thread);
|
|
return Ret;
|
|
}
|
|
|
|
UINT
|
|
APIENTRY
|
|
NtUserGetKeyboardLayoutList(
|
|
INT nItems,
|
|
HKL* pHklBuff)
|
|
{
|
|
UINT Ret = 0;
|
|
PKBL pKbl;
|
|
|
|
UserEnterShared();
|
|
pKbl = KBLList;
|
|
|
|
if(nItems == 0)
|
|
{
|
|
do
|
|
{
|
|
Ret++;
|
|
pKbl = (PKBL) pKbl->List.Flink;
|
|
} while(pKbl != KBLList);
|
|
}
|
|
else
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(pHklBuff, nItems*sizeof(HKL), 4);
|
|
|
|
while(Ret < nItems)
|
|
{
|
|
if(!(pKbl->Flags & KBL_UNLOAD))
|
|
{
|
|
pHklBuff[Ret] = pKbl->hkl;
|
|
Ret++;
|
|
pKbl = (PKBL) pKbl->List.Flink;
|
|
if(pKbl == KBLList) break;
|
|
}
|
|
}
|
|
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
Ret = 0;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetKeyboardLayoutName(
|
|
LPWSTR lpszName)
|
|
{
|
|
BOOL ret = FALSE;
|
|
PKBL pKbl;
|
|
PTHREADINFO pti;
|
|
|
|
UserEnterShared();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(lpszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
pKbl = pti->KeyboardLayout;
|
|
RtlCopyMemory(lpszName, pKbl->Name, KL_NAMELENGTH*sizeof(WCHAR));
|
|
ret = TRUE;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
ret = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
UserLeave();
|
|
return ret;
|
|
}
|
|
|
|
|
|
HKL
|
|
APIENTRY
|
|
NtUserLoadKeyboardLayoutEx(
|
|
IN HANDLE Handle,
|
|
IN DWORD offTable,
|
|
IN PUNICODE_STRING puszKeyboardName,
|
|
IN HKL hKL,
|
|
IN PUNICODE_STRING puszKLID,
|
|
IN DWORD dwKLID,
|
|
IN UINT Flags)
|
|
{
|
|
HKL Ret = NULL;
|
|
PKBL pKbl = NULL, Cur;
|
|
|
|
UserEnterExclusive();
|
|
|
|
//Let's see if layout was already loaded.
|
|
Cur = KBLList;
|
|
do
|
|
{
|
|
if(Cur->klid == dwKLID)
|
|
{
|
|
pKbl = Cur;
|
|
pKbl->Flags &= ~KBL_UNLOAD;
|
|
break;
|
|
}
|
|
|
|
Cur = (PKBL) Cur->List.Flink;
|
|
} while(Cur != KBLList);
|
|
|
|
//It wasn't, so load it.
|
|
if(!pKbl)
|
|
{
|
|
pKbl = UserLoadDllAndCreateKbl(dwKLID);
|
|
|
|
if(!pKbl)
|
|
{
|
|
goto the_end;
|
|
}
|
|
|
|
InsertTailList(&KBLList->List, &pKbl->List);
|
|
}
|
|
|
|
if(Flags & KLF_REORDER) KBLList = pKbl;
|
|
|
|
if(Flags & KLF_ACTIVATE)
|
|
{
|
|
co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl, Flags);
|
|
}
|
|
|
|
Ret = pKbl->hkl;
|
|
|
|
//FIXME: KLF_NOTELLSHELL
|
|
// KLF_REPLACELANG
|
|
// KLF_SUBSTITUTE_OK
|
|
|
|
the_end:
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
HKL
|
|
APIENTRY
|
|
NtUserActivateKeyboardLayout(
|
|
HKL hKl,
|
|
ULONG Flags)
|
|
{
|
|
PKBL pKbl;
|
|
HKL Ret = NULL;
|
|
PTHREADINFO pWThread;
|
|
|
|
UserEnterExclusive();
|
|
|
|
pWThread = PsGetCurrentThreadWin32Thread();
|
|
|
|
if(pWThread->KeyboardLayout->hkl == hKl)
|
|
{
|
|
Ret = hKl;
|
|
goto the_end;
|
|
}
|
|
|
|
if(hKl == (HKL)HKL_NEXT)
|
|
{
|
|
pKbl = (PKBL)pWThread->KeyboardLayout->List.Flink;
|
|
}
|
|
else if(hKl == (HKL)HKL_PREV)
|
|
{
|
|
pKbl = (PKBL)pWThread->KeyboardLayout->List.Blink;
|
|
}
|
|
else pKbl = UserHklToKbl(hKl);
|
|
|
|
//FIXME: KLF_RESET, KLF_SHIFTLOCK
|
|
|
|
if(pKbl)
|
|
{
|
|
if(Flags & KLF_REORDER)
|
|
KBLList = pKbl;
|
|
|
|
if(pKbl == pWThread->KeyboardLayout)
|
|
{
|
|
Ret = pKbl->hkl;
|
|
}
|
|
else
|
|
{
|
|
pKbl = co_UserActivateKbl(pWThread, pKbl, Flags);
|
|
Ret = pKbl->hkl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);
|
|
}
|
|
|
|
the_end:
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
NtUserUnloadKeyboardLayout(
|
|
HKL hKl)
|
|
{
|
|
PKBL pKbl;
|
|
BOOL Ret = FALSE;
|
|
|
|
UserEnterExclusive();
|
|
|
|
if((pKbl = UserHklToKbl(hKl)))
|
|
{
|
|
Ret = UserUnloadKbl(pKbl);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);
|
|
}
|
|
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/* EOF */
|