reactos/subsystems/win32/win32k/ntuser/kbdlayout.c
Jérôme Gardou 88c9e7c6e8 Sync with trunk (r47116), hopefully without breaking anything.
svn path=/branches/reactos-yarotows/; revision=47117
2010-05-07 07:41:13 +00:00

689 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" );
layerDescGetFn = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor");
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 = ExAllocatePool(PagedPool, sizeof(KBL));
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);
ExFreePool(NewKbl);
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);
ExFreePool(pKbl);
}
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 */