|
|
/**************************** Module Header ********************************\
* Module Name: kbdlyout.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Keyboard Layout API * * History: * 04-14-92 IanJa Created \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* Workers (forward declarations) */ BOOL xxxInternalUnloadKeyboardLayout(PWINDOWSTATION, PKL, UINT); VOID ReorderKeyboardLayouts(PWINDOWSTATION, PKL);
/****************************************************************************\
* HKLtoPKL * * pti - thread to look in * hkl - HKL_NEXT or HKL_PREV * Finds the the next/prev LOADED layout, NULL if none. * (Starts from the pti's active layout, may return pklActive itself) * - a real HKL (Keyboard Layout handle): * Finds the kbd layout struct (loaded or not), NULL if no match found. * * History: * 1997-02-05 IanJa added pti parameter \****************************************************************************/ PKL HKLtoPKL( PTHREADINFO pti, HKL hkl) { PKL pklActive; PKL pkl;
UserAssert(pti != NULL); if ((pklActive = pti->spklActive) == NULL) { return NULL; }
pkl = pklActive;
if (hkl == (HKL)HKL_PREV) { do { pkl = pkl->pklPrev; if (!(pkl->dwKL_Flags & KL_UNLOADED)) { return pkl; } } while (pkl != pklActive); return NULL; } else if (hkl == (HKL)HKL_NEXT) { do { pkl = pkl->pklNext; if (!(pkl->dwKL_Flags & KL_UNLOADED)) { return pkl; } } while (pkl != pklActive); return NULL; }
/*
* Find the pkl for this hkl. * If the kbd layout isn't specified (in the HIWORD), ignore it and look * for a Locale match only. (Mohamed Hamid's fix for Word bug) */ if (HandleToUlong(hkl) & 0xffff0000) { do { if (pkl->hkl == hkl) { return pkl; } pkl = pkl->pklNext; } while (pkl != pklActive); } else { do { if (LOWORD(HandleToUlong(pkl->hkl)) == LOWORD(HandleToUlong(hkl))) { return pkl; } pkl = pkl->pklNext; } while (pkl != pklActive); }
return NULL; }
/***************************************************************************\
* ReadLayoutFile * * Maps layout file into memory and initializes layout table. * * History: * 01-10-95 JimA Created. \***************************************************************************/
#define GET_HEADER_FIELD(x) \
((fWin64Header) ? (NtHeader64->x) : (NtHeader32->x))
/*
* Note that this only works for sections < 64K * Implicitly assumes pBaseVirt, pBaseDst and dwDataSize. */
#if DBG
BOOL gfEnableChecking = TRUE; #else
BOOL gfEnableChecking = FALSE; #endif
#define EXIT_READ(p) \
RIPMSGF1(RIP_WARNING, #p " is @ invalid address %p", p); \ if (gfEnableChecking) { \ goto exitread; \ }
#define VALIDATE_PTR(p) \
if ((PBYTE)(p) < (PBYTE)pBaseDst || (PBYTE)(p) + sizeof *(p) > (PBYTE)pBaseDst + dwDataSize) { \ EXIT_READ(p); \ }
#define FIXUP_PTR(p) \
if (p) { \ p = (PVOID)((PBYTE)pBaseVirt + (WORD)(ULONG_PTR)(p)); \ VALIDATE_PTR(p); \ } \ TAGMSGF1(DBGTAG_KBD, #p " validation finished %p", p);
PKBDTABLES ReadLayoutFile( PKBDFILE pkf, HANDLE hFile, UINT offTable, UINT offNlsTable ) { HANDLE hmap = NULL; OBJECT_ATTRIBUTES ObjectAttributes; SIZE_T ulViewSize = 0; NTSTATUS Status; PIMAGE_DOS_HEADER DosHdr = NULL; BOOLEAN fWin64Header; PIMAGE_NT_HEADERS32 NtHeader32; PIMAGE_NT_HEADERS64 NtHeader64; PIMAGE_SECTION_HEADER SectionTableEntry; ULONG NumberOfSubsections; ULONG OffsetToSectionTable; PBYTE pBaseDst = NULL, pBaseVirt = NULL; PKBDTABLES pktNew = NULL; DWORD dwDataSize; PKBDNLSTABLES pknlstNew = NULL; BOOL fSucceeded = FALSE;
TAGMSGF1(DBGTAG_KBD, "entering for '%ls'", pkf->awchDllName);
/*
* Mask off hiword. */ UserAssert((offTable & ~0xffff) == 0); UserAssert((offNlsTable & ~0xffff) == 0);
/*
* Initialize KbdNlsTables with NULL. */ pkf->pKbdNlsTbl = NULL;
InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
/*
* Map the layout file into memory */ Status = ZwCreateSection(&hmap, SECTION_MAP_READ, &ObjectAttributes, NULL, PAGE_READONLY, SEC_COMMIT, hFile); if (!NT_SUCCESS(Status)) { RIPMSGF3(RIP_WARNING, "failed to create a section for %ls, hFile=%p, stat=%08x", pkf->awchDllName, hFile, Status); goto exitread; }
Status = ZwMapViewOfSection(hmap, NtCurrentProcess(), &DosHdr, 0, 0, NULL, &ulViewSize, ViewUnmap, 0, PAGE_READONLY); if (!NT_SUCCESS(Status)) { RIPMSGF1(RIP_WARNING, "failed to map the view for %ls", pkf->awchDllName); goto exitread; }
if (ulViewSize < sizeof *DosHdr || ulViewSize > (128 * 1024)) { RIPMSGF1(RIP_WARNING, "ViewSize is too small or large %08x", ulViewSize); goto exitread; }
/*
* We find the .data section in the file header and by * subtracting the virtual address from offTable find * the offset in the section of the layout table. */ UserAssert(sizeof *NtHeader64 >= sizeof *NtHeader32);
try { NtHeader64 = (PIMAGE_NT_HEADERS64)((PBYTE)DosHdr + (ULONG)DosHdr->e_lfanew); NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader64;
#if defined(_WIN64)
if ((PBYTE)NtHeader64 < (PBYTE)DosHdr || // signed Overflow
(PBYTE)NtHeader64 + sizeof *NtHeader64 >= (PBYTE)DosHdr + ulViewSize) { RIPMSGF1(RIP_WARNING, "Header is out of Range %p", NtHeader64); goto exitread; }
fWin64Header = (NtHeader64->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) || (NtHeader64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64); #else
if ((PBYTE)NtHeader32 < (PBYTE)DosHdr || // signed Overflow
(PBYTE)NtHeader32 + sizeof *NtHeader32 >= (PBYTE)DosHdr + ulViewSize) { RIPMSGF1(RIP_WARNING, "Header is out of Range %p", NtHeader32); goto exitread; }
fWin64Header = FALSE; #endif
TAGMSGF2(DBGTAG_KBD, "DLL='%ls', Is64=%d", pkf->awchDllName, fWin64Header);
/*
* At this point the object table is read in (if it was not * already read in) and may displace the image header. */ NumberOfSubsections = GET_HEADER_FIELD(FileHeader.NumberOfSections);
OffsetToSectionTable = sizeof(ULONG) + // Signature
sizeof(IMAGE_FILE_HEADER) + // FileHeader
GET_HEADER_FIELD(FileHeader.SizeOfOptionalHeader);
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PBYTE)NtHeader32 + OffsetToSectionTable);
while (NumberOfSubsections > 0) { /*
* Validate the SectionTableEntry. */ if ((PBYTE)SectionTableEntry < (PBYTE)DosHdr || (PBYTE)SectionTableEntry + sizeof *SectionTableEntry >= (PBYTE)DosHdr + ulViewSize) { RIPMSGF1(RIP_WARNING, "SectionTableEntry @ %p is not within the view section.", SectionTableEntry); goto exitread; }
/*
* Is this the .data section that we are looking for? */ if (strcmp(SectionTableEntry->Name, ".data") == 0) { break; }
SectionTableEntry++; NumberOfSubsections--; }
if (NumberOfSubsections == 0) { RIPMSGF1(RIP_WARNING, "number of sections is 0 for %ls.", pkf->awchDllName); goto exitread; }
/*
* We found the section, now compute starting offset and the table size. */ offTable -= SectionTableEntry->VirtualAddress; dwDataSize = SectionTableEntry->Misc.VirtualSize;
/*
* Validate the offTable to see if it fits in the section. */ if (offTable >= dwDataSize) { RIPMSGF3(RIP_WARNING, "illegal offTable=0x%x or offNlsTable=0x%x, dwDataSize=0x%x", offTable, offNlsTable, dwDataSize); goto exitread; }
/*
* Validate the .data size not exceeding our assumption (<64KB). */ if (dwDataSize >= 0xffff) { RIPMSGF1(RIP_WARNING, "unexpected size in .data: 0x%x", dwDataSize); goto exitread; }
/*
* Validate we are not over-shooting our view */ if ((PBYTE)DosHdr + SectionTableEntry->PointerToRawData + dwDataSize >= (PBYTE)DosHdr + ulViewSize) { RIPMSGF1(RIP_WARNING, "Layout Table @ %p is not within the view section.", (PBYTE)DosHdr + SectionTableEntry->PointerToRawData); goto exitread; }
/*
* Allocate layout table and copy from file. */ TAGMSGF2(DBGTAG_KBD, "data size for '%S' = %x", pkf->awchDllName, dwDataSize); pBaseDst = UserAllocPool(dwDataSize, TAG_KBDTABLE);
#if DBG
if (pBaseDst == NULL) { RIPMSGF2(RIP_WARNING, "failed to allocate 0x%x bytes of memory for %ls", dwDataSize, pkf->awchDllName); } #endif
if (pBaseDst != NULL) { VK_TO_WCHAR_TABLE *pVkToWcharTable; VSC_LPWSTR *pKeyName;
pkf->hBase = (HANDLE)pBaseDst;
RtlMoveMemory(pBaseDst, (PBYTE)DosHdr + SectionTableEntry->PointerToRawData, dwDataSize);
if (ISTS()) { pkf->Size = dwDataSize; // For shadow hotkey processing
}
/*
* Compute table address and fixup pointers in table. */ pktNew = (PKBDTABLES)(pBaseDst + offTable);
/*
* The address in the data section has the virtual address * added in, so we need to adjust the fixup pointer to * compensate. */ pBaseVirt = pBaseDst - SectionTableEntry->VirtualAddress;
FIXUP_PTR(pktNew->pCharModifiers); FIXUP_PTR(pktNew->pCharModifiers->pVkToBit); /*
* Validate pVkToBit table. */ { PVK_TO_BIT pVkToBit;
for (pVkToBit = pktNew->pCharModifiers->pVkToBit; ; pVkToBit++) { VALIDATE_PTR(pVkToBit); if (pVkToBit->Vk == 0) { break; } } }
FIXUP_PTR(pktNew->pVkToWcharTable); #if DBG
if (pktNew->pVkToWcharTable == NULL) { RIPMSGF1(RIP_WARNING, "KL %ls does not have pVkToWcharTable???", pkf->awchDllName); } #endif
if (pktNew->pVkToWcharTable) { /*
* Fix up and validate VkToWchar table. */ for (pVkToWcharTable = pktNew->pVkToWcharTable; ; pVkToWcharTable++) { VALIDATE_PTR(pVkToWcharTable); if (pVkToWcharTable->pVkToWchars == NULL) { break; } FIXUP_PTR(pVkToWcharTable->pVkToWchars); } }
FIXUP_PTR(pktNew->pDeadKey); /*
* Validate pDeadKey array. */ { PDEADKEY pDeadKey = pktNew->pDeadKey; while (pDeadKey) { VALIDATE_PTR(pDeadKey); if (pDeadKey->dwBoth == 0) { break; } pDeadKey++; } }
/*
* Version 1 layouts support ligatures. */ if (GET_KBD_VERSION(pktNew)) { FIXUP_PTR(pktNew->pLigature); }
FIXUP_PTR(pktNew->pKeyNames); if (pktNew->pKeyNames == NULL) { RIPMSGF1(RIP_WARNING, "KL %ls does not have pKeyNames???", pkf->awchDllName); }
if (pktNew->pKeyNames) { for (pKeyName = pktNew->pKeyNames; ; pKeyName++) { VALIDATE_PTR(pKeyName); if (pKeyName->vsc == 0) { break; } FIXUP_PTR(pKeyName->pwsz); } }
FIXUP_PTR(pktNew->pKeyNamesExt); if (pktNew->pKeyNamesExt) { for (pKeyName = pktNew->pKeyNamesExt; ; pKeyName++) { VALIDATE_PTR(pKeyName); if (pKeyName->vsc == 0) { break; } FIXUP_PTR(pKeyName->pwsz); } }
FIXUP_PTR(pktNew->pKeyNamesDead); if (pktNew->pKeyNamesDead) { LPWSTR *lpDeadKey; for (lpDeadKey = pktNew->pKeyNamesDead; ; lpDeadKey++) { LPCWSTR lpwstr;
VALIDATE_PTR(lpDeadKey); if (*lpDeadKey == NULL) { break; } FIXUP_PTR(*lpDeadKey); UserAssert(*lpDeadKey); for (lpwstr = *lpDeadKey; ; lpwstr++) { VALIDATE_PTR(lpwstr); if (*lpwstr == L'\0') { break; } } }; }
/*
* Fix up and validate Virtual Scan Code to VK table. */ if (pktNew->pusVSCtoVK == NULL) { RIPMSGF1(RIP_WARNING, "KL %ls does not have the basic VSC to VK table", pkf->awchDllName); goto exitread; } FIXUP_PTR(pktNew->pusVSCtoVK); VALIDATE_PTR(pktNew->pusVSCtoVK + pktNew->bMaxVSCtoVK);
FIXUP_PTR(pktNew->pVSCtoVK_E0); if (pktNew->pVSCtoVK_E0) { PVSC_VK pVscVk; for (pVscVk = pktNew->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) { VALIDATE_PTR(pVscVk); } }
FIXUP_PTR(pktNew->pVSCtoVK_E1); if (pktNew->pVSCtoVK_E1) { PVSC_VK pVscVk; for (pVscVk = pktNew->pVSCtoVK_E1; ; pVscVk++) { VALIDATE_PTR(pVscVk); if (pVscVk->Vk == 0) { break; } } }
if (offNlsTable) { /*
* Compute table address and fixup pointers in table. */ offNlsTable -= SectionTableEntry->VirtualAddress; pknlstNew = (PKBDNLSTABLES)(pBaseDst + offNlsTable);
VALIDATE_PTR(pknlstNew);
/*
* Fixup and validate the address. */ FIXUP_PTR(pknlstNew->pVkToF); if (pknlstNew->pVkToF) { VALIDATE_PTR(&pknlstNew->pVkToF[pknlstNew->NumOfVkToF - 1]); }
FIXUP_PTR(pknlstNew->pusMouseVKey); if (pknlstNew->pusMouseVKey) { VALIDATE_PTR(&pknlstNew->pusMouseVKey[pknlstNew->NumOfMouseVKey - 1]); }
/*
* Save the pointer. */ pkf->pKbdNlsTbl = pknlstNew;
#if DBG_FE
{ UINT NumOfVkToF = pknlstNew->NumOfVkToF;
DbgPrint("NumOfVkToF - %d\n",NumOfVkToF);
while(NumOfVkToF) { DbgPrint("VK = %x\n",pknlstNew->pVkToF[NumOfVkToF-1].Vk); NumOfVkToF--; } } #endif // DBG_FE
} }
} except(W32ExceptionHandler(FALSE, RIP_WARNING)) { RIPMSGF1(RIP_WARNING, "took exception reading from %ls", pkf->awchDllName); goto exitread; }
fSucceeded = TRUE; exitread:
if (!fSucceeded && pBaseDst) { UserFreePool(pBaseDst); }
/*
* Unmap and release the mapped section. */ if (DosHdr) { ZwUnmapViewOfSection(NtCurrentProcess(), DosHdr); }
if (hmap != NULL) { ZwClose(hmap); }
TAGMSGF1(DBGTAG_KBD, "returning pkl = %p", pktNew);
if (!fSucceeded) { return NULL; }
return pktNew; }
PKBDTABLES PrepareFallbackKeyboardFile(PKBDFILE pkf) { PBYTE pBaseDst;
pBaseDst = UserAllocPool(sizeof(KBDTABLES), TAG_KBDTABLE); if (pBaseDst != NULL) { RtlCopyMemory(pBaseDst, &KbdTablesFallback, sizeof KbdTablesFallback); // Note: Unlike ReadLayoutFile(),
// we don't need to fix up pointers in struct KBDFILE.
} pkf->hBase = (HANDLE)pBaseDst; pkf->pKbdNlsTbl = NULL; return (PKBDTABLES)pBaseDst; }
/***************************************************************************\
* LoadKeyboardLayoutFile * * History: * 10-29-95 GregoryW Created. \***************************************************************************/
PKBDFILE LoadKeyboardLayoutFile( HANDLE hFile, UINT offTable, UINT offNlsTable, LPCWSTR pwszKLID, LPWSTR pwszDllName, DWORD dwType, DWORD dwSubType) { PKBDFILE pkf = gpkfList;
TAGMSG4(DBGTAG_KBD | RIP_THERESMORE, "LoadKeyboardLayoutFile: new KL=%S, dllName='%S', %d:%d", pwszKLID, pwszDllName ? pwszDllName : L"", dwType, dwSubType); UNREFERENCED_PARAMETER(pwszKLID);
/*
* Search for the existing layout file. */ if (pkf) { do { TAGMSG3(DBGTAG_KBD | RIP_THERESMORE, "LoadKeyboardLayoutFile: looking at dll=%S, %d:%d", pkf->awchDllName, pkf->pKbdTbl->dwType, pkf->pKbdTbl->dwSubType); if (pwszDllName && _wcsicmp(pkf->awchDllName, pwszDllName) == 0) { /*
* The layout is already loaded. */ TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: duplicated KBDFILE found(#1). pwszDllName='%ls'\n", pwszDllName); return pkf; } pkf = pkf->pkfNext; } while (pkf); } TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: layout %S is not yet loaded.", pwszDllName);
/*
* Allocate a new Keyboard File structure. */ pkf = (PKBDFILE)HMAllocObject(NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); if (!pkf) { RIPMSG0(RIP_WARNING, "Keyboard Layout File: out of memory"); return (PKBDFILE)NULL; }
/*
* Load layout table. */ if (hFile != NULL) { /*
* Load NLS layout table also... */ wcsncpycch(pkf->awchDllName, pwszDllName, ARRAY_SIZE(pkf->awchDllName)); pkf->awchDllName[ARRAY_SIZE(pkf->awchDllName) - 1] = 0; pkf->pKbdTbl = ReadLayoutFile(pkf, hFile, offTable, offNlsTable); if (dwType || dwSubType) { pkf->pKbdTbl->dwType = dwType; pkf->pKbdTbl->dwSubType = dwSubType; } } else { /*
* We failed to open the keyboard layout file in client side * because the dll was missing. * If this ever happens, we used to fail creating * a window station, but we should allow a user * at least to boot the system. */ TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: hFile is NULL for %ls, preparing the fallback.", pwszDllName); pkf->pKbdTbl = PrepareFallbackKeyboardFile(pkf); // Note: pkf->pKbdNlsTbl has been NULL'ed in PrepareFallbackKeyboardFile()
}
if (pkf->pKbdTbl == NULL) { RIPMSG0(RIP_WARNING, "LoadKeyboardLayoutFile: pkf->pKbdTbl is NULL."); HMFreeObject(pkf); return (PKBDFILE)NULL; }
/*
* Put keyboard layout file at front of list. */ pkf->pkfNext = gpkfList; gpkfList = pkf;
return pkf; }
/***************************************************************************\
* RemoveKeyboardLayoutFile * * History: * 10-29-95 GregoryW Created. \***************************************************************************/ VOID RemoveKeyboardLayoutFile( PKBDFILE pkf) { PKBDFILE pkfPrev, pkfCur;
// FE: NT4 SP4 #107809
if (gpKbdTbl == pkf->pKbdTbl) { gpKbdTbl = &KbdTablesFallback; } if (gpKbdNlsTbl == pkf->pKbdNlsTbl) { gpKbdNlsTbl = NULL; }
/*
* Good old linked list management 101 */ if (pkf == gpkfList) { /*
* Head of the list. */ gpkfList = pkf->pkfNext; return; } pkfPrev = gpkfList; pkfCur = gpkfList->pkfNext; while (pkf != pkfCur) { pkfPrev = pkfCur; pkfCur = pkfCur->pkfNext; } /*
* Found it! */ pkfPrev->pkfNext = pkfCur->pkfNext; }
/***************************************************************************\
* DestroyKF * * Called when a keyboard layout file is destroyed due to an unlock. * * History: * 24-Feb-1997 adams Created. \***************************************************************************/
void DestroyKF(PKBDFILE pkf) { if (!HMMarkObjectDestroy(pkf)) return;
RemoveKeyboardLayoutFile(pkf); UserFreePool(pkf->hBase); HMFreeObject(pkf); }
INT GetThreadsWithPKL( PTHREADINFO **ppptiList, PKL pkl) { PTHREADINFO ptiT, *pptiT, *pptiListAllocated; INT cThreads, cThreadsAllocated; PWINDOWSTATION pwinsta; PDESKTOP pdesk; PLIST_ENTRY pHead, pEntry;
if (ppptiList != NULL) *ppptiList = NULL;
cThreads = 0;
/*
* allocate a first list for 128 entries */ cThreadsAllocated = 128; pptiListAllocated = UserAllocPool(cThreadsAllocated * sizeof(PTHREADINFO), TAG_SYSTEM);
if (pptiListAllocated == NULL) { RIPMSG0(RIP_WARNING, "GetPKLinThreads: out of memory"); return 0; }
// for all the winstations
for (pwinsta = grpWinStaList; pwinsta != NULL ; pwinsta = pwinsta->rpwinstaNext) {
// for all the desktops in that winstation
for (pdesk = pwinsta->rpdeskList; pdesk != NULL ; pdesk = pdesk->rpdeskNext) {
pHead = &pdesk->PtiList;
// for all the threads in that desktop
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
if (ptiT == NULL) { continue; }
if (pkl && (pkl != ptiT->spklActive)) { // #99321 cmp pkls, not hkls?
continue; }
/*
* WindowsBug 349045 * Unload IMEs only for the normal apps.... leave them as is if they are * loaded for services. * Note, this is not really a clean fix, but some customers demand it. */ UserAssert(PsGetCurrentProcessId() == gpidLogon); if (ptiT->ppi->Process != gpepCSRSS && ptiT->ppi->Process != PsGetCurrentProcess()) { /*
* By the time this routine is called (solely by WinLogon), all the other * applications should be gone or terminated. So skipping like above * leaves IMEs loaded in the services. */ continue; }
if (cThreads == cThreadsAllocated) {
cThreadsAllocated += 128;
pptiT = UserReAllocPool(pptiListAllocated, cThreads * sizeof(PTHREADINFO), cThreadsAllocated * sizeof(PTHREADINFO), TAG_SYSTEM);
if (pptiT == NULL) { RIPMSG0(RIP_ERROR, "GetPKLinThreads: Out of memory"); UserFreePool(pptiListAllocated); return 0; }
pptiListAllocated = pptiT;
}
pptiListAllocated[cThreads++] = ptiT; } } }
/*
* add CSRSS threads */ for (ptiT = PpiFromProcess(gpepCSRSS)->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
if (pkl && (pkl != ptiT->spklActive)) { // #99321 cmp pkls, not hkls?
continue; }
if (cThreads == cThreadsAllocated) {
cThreadsAllocated += 128;
pptiT = UserReAllocPool(pptiListAllocated, cThreads * sizeof(PTHREADINFO), cThreadsAllocated * sizeof(PTHREADINFO), TAG_SYSTEM);
if (pptiT == NULL) { RIPMSG0(RIP_ERROR, "GetPKLinThreads: Out of memory"); UserFreePool(pptiListAllocated); return 0; }
pptiListAllocated = pptiT;
}
pptiListAllocated[cThreads++] = ptiT; }
if (cThreads == 0) { UserFreePool(pptiListAllocated); } else if (ppptiList != NULL) { *ppptiList = pptiListAllocated; } else { UserFreePool(pptiListAllocated); }
return cThreads; }
VOID xxxSetPKLinThreads( PKL pklNew, PKL pklToBeReplaced) { PTHREADINFO *pptiList; INT cThreads, i;
UserAssert(pklNew != pklToBeReplaced);
CheckLock(pklNew); CheckLock(pklToBeReplaced);
cThreads = GetThreadsWithPKL(&pptiList, pklToBeReplaced);
/*
* Will the foreground thread's keyboard layout change? */ if (pklNew && gptiForeground && gptiForeground->spklActive == pklToBeReplaced) { ChangeForegroundKeyboardTable(pklToBeReplaced, pklNew); }
if (pptiList != NULL) { if (pklToBeReplaced == NULL) { for (i = 0; i < cThreads; i++) { Lock(&pptiList[i]->spklActive, pklNew); } } else { /*
* This is a replace. First, deactivate the *replaced* IME by * activating the pklNew. Second, unload the *replaced* IME. */ xxxImmActivateAndUnloadThreadsLayout(pptiList, cThreads, NULL, pklNew, HandleToUlong(pklToBeReplaced->hkl)); } UserFreePool(pptiList); }
/*
* If this is a replace, link the new layout immediately after the * layout being replaced. This maintains ordering of layouts when * the *replaced* layout is unloaded. The input locale panel in the * regional settings applet depends on this. */ if (pklToBeReplaced) { if (pklToBeReplaced->pklNext == pklNew) { /*
* Ordering already correct. Nothing to do. */ return; } /*
* Move new layout immediately after layout being replaced. * 1. Remove new layout from current position. * 2. Update links in new layout. * 3. Link new layout into desired position. */ pklNew->pklPrev->pklNext = pklNew->pklNext; pklNew->pklNext->pklPrev = pklNew->pklPrev;
pklNew->pklNext = pklToBeReplaced->pklNext; pklNew->pklPrev = pklToBeReplaced;
pklToBeReplaced->pklNext->pklPrev = pklNew; pklToBeReplaced->pklNext = pklNew; } }
VOID xxxFreeImeKeyboardLayouts( PWINDOWSTATION pwinsta) { PTHREADINFO *pptiList; INT cThreads;
if (pwinsta->dwWSF_Flags & WSF_NOIO) return;
/*
* should make GetThreadsWithPKL aware of pwinsta? */ cThreads = GetThreadsWithPKL(&pptiList, NULL); if (pptiList != NULL) { xxxImmUnloadThreadsLayout(pptiList, cThreads, NULL, IFL_UNLOADIME); UserFreePool(pptiList); }
return; }
/***************************************************************************\
* xxxLoadKeyboardLayoutEx * * History: \***************************************************************************/
HKL xxxLoadKeyboardLayoutEx( PWINDOWSTATION pwinsta, HANDLE hFile, HKL hklToBeReplaced, UINT offTable, PKBDTABLE_MULTI_INTERNAL pKbdTableMulti, LPCWSTR pwszKLID, UINT KbdInputLocale, UINT Flags) { PKL pkl, pklFirst, pklToBeReplaced; PKBDFILE pkf; CHARSETINFO cs; TL tlpkl; PTHREADINFO ptiCurrent; UNICODE_STRING strLcidKF; UNICODE_STRING strKLID; LCID lcidKF; BOOL bCharSet; PIMEINFOEX piiex;
TAGMSG1(DBGTAG_KBD, "xxxLoadKeyboardLayoutEx: new KL: pwszKLID=\"%ls\"", pwszKLID);
/*
* If the windowstation does not do I/O, don't load the * layout. Also check KdbInputLocale for #307132 */ if ((KbdInputLocale == 0) || (pwinsta->dwWSF_Flags & WSF_NOIO)) { return NULL; }
/*
* If hklToBeReplaced is non-NULL make sure it's valid. * NOTE: may want to verify they're not passing HKL_NEXT or HKL_PREV. */ ptiCurrent = PtiCurrent(); if (hklToBeReplaced && !(pklToBeReplaced = HKLtoPKL(ptiCurrent, hklToBeReplaced))) { return NULL; } if (KbdInputLocale == HandleToUlong(hklToBeReplaced)) { /*
* Replacing a layout/lang pair with itself. Nothing to do. */ return pklToBeReplaced->hkl; }
if (Flags & KLF_RESET) { /*
* Only WinLogon can use this flag */ if (PsGetThreadProcessId(ptiCurrent->pEThread) != gpidLogon) { RIPERR0(ERROR_INVALID_FLAGS, RIP_WARNING, "Invalid flag passed to LoadKeyboardLayout" ); return NULL; } xxxFreeImeKeyboardLayouts(pwinsta); /*
* Make sure we don't lose track of the left-over layouts * They have been unloaded, but are still in use by some threads). * The FALSE will prevent xxxFreeKeyboardLayouts from unlocking the * unloaded layouts. */ xxxFreeKeyboardLayouts(pwinsta, FALSE); }
/*
* Does this hkl already exist? */ pkl = pklFirst = pwinsta->spklList;
if (pkl) { do { if (pkl->hkl == (HKL)IntToPtr( KbdInputLocale )) { /*
* The hkl already exists. */
/*
* If it is unloaded (but not yet destroyed because it is * still is use), recover it. */ if (pkl->dwKL_Flags & KL_UNLOADED) { // stop it from being destroyed if not is use.
PHE phe = HMPheFromObject(pkl); // An unloaded layout must be marked for destroy.
UserAssert(phe->bFlags & HANDLEF_DESTROY); phe->bFlags &= ~HANDLEF_DESTROY; #if DBG
phe->bFlags &= ~HANDLEF_MARKED_OK; #endif
pkl->dwKL_Flags &= ~KL_UNLOADED; } else if (!(Flags & KLF_RESET)) { /*
* If it was already loaded and we didn't change all layouts * with KLF_RESET, there is nothing to tell the shell about */ Flags &= ~KLF_NOTELLSHELL; }
goto AllPresentAndCorrectSir; } pkl = pkl->pklNext; } while (pkl != pklFirst); }
if (IS_IME_KBDLAYOUT((HKL)IntToPtr( KbdInputLocale )) #ifdef CUAS_ENABLE
|| IS_CICERO_ENABLED_AND_NOT16BIT() #endif // CUAS_ENABLE
) { /*
* This is an IME keyboard layout, do a callback * to read the extended IME information structure. * Note: We can't fail the call so easily if * KLF_RESET is specified. */ piiex = xxxImmLoadLayout((HKL)IntToPtr( KbdInputLocale )); if (piiex == NULL && (Flags & (KLF_RESET | KLF_INITTIME)) == 0) { /*
* Not Resetting, not creating a window station */ RIPMSG1(RIP_WARNING, "Keyboard Layout: xxxImmLoadLayout(%lx) failed", KbdInputLocale); return NULL; } } else { piiex = NULL; }
/*
* Get the system font's font signature. These are 64-bit FS_xxx values, * but we are only asking for an ANSI ones, so gSystemFS is just a DWORD. * gSystemFS is consulted when posting WM_INPUTLANGCHANGEREQUEST (input.c) */ if (gSystemFS == 0) { LCID lcid;
ZwQueryDefaultLocale(FALSE, &lcid); if (xxxClientGetCharsetInfo(lcid, &cs)) { gSystemFS = cs.fs.fsCsb[0]; gSystemCPCharSet = (BYTE)cs.ciCharset; } else { gSystemFS = 0xFFFF; gSystemCPCharSet = ANSI_CHARSET; } }
/*
* Use the Keyboard Layout's LCID to calculate the charset, codepage etc, * so that characters from that layout don't just becomes ?s if the input * locale doesn't match. This allows "dumb" applications to display the * text if the user chooses the right font. * We can't just use the HIWORD of KbdInputLocale because if a variant * keyboard layout was chosen, this will be something like F008 - have to * look inside the KF to get the real LCID of the kbdfile: this will be * something like L"00010419", and we want the last 4 digits. */ RtlInitUnicodeString(&strLcidKF, pwszKLID + 4); RtlUnicodeStringToInteger(&strLcidKF, 16, (PULONG)&lcidKF); bCharSet = xxxClientGetCharsetInfo(lcidKF, &cs);
/*
* Keyboard Layout Handle object does not exist. Load keyboard layout file, * if not already loaded. */ if ((pkf = LoadKeyboardLayoutFile(hFile, LOWORD(offTable), HIWORD(offTable), pwszKLID, pKbdTableMulti->wszDllName, 0, 0)) == NULL) { goto freePiiex; } /*
* Allocate a new Keyboard Layout structure (hkl) */ pkl = (PKL)HMAllocObject(NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); if (!pkl) { RIPMSG0(RIP_WARNING, "Keyboard Layout: out of memory"); UserFreePool(pkf->hBase); HMMarkObjectDestroy(pkf); HMUnlockObject(pkf); freePiiex: if (piiex) { UserFreePool(piiex); } return NULL; }
Lock(&pkl->spkfPrimary, pkf);
/*
* Load extra keyboard layouts. */ UserAssert(pKbdTableMulti); if (pKbdTableMulti->multi.nTables) { RIPMSG0(RIP_WARNING, "xxxLoadKeyboardLayoutEx: going to read multiple tables."); /*
* Allocate the array for extra keyboard layouts. */ UserAssert(pKbdTableMulti->multi.nTables < KBDTABLE_MULTI_MAX); // check exists in the stub
pkl->pspkfExtra = UserAllocPoolZInit(pKbdTableMulti->multi.nTables * sizeof(PKBDFILE), TAG_KBDTABLE); if (pkl->pspkfExtra) { UINT i; UINT n;
/*
* Load the extra keyboard layouts and lock them. */ for (i = 0, n = 0; i < pKbdTableMulti->multi.nTables; ++i) { UserAssert(i < KBDTABLE_MULTI_MAX); if (pKbdTableMulti->files[i].hFile) { // make sure dll name is null terminated.
pKbdTableMulti->multi.aKbdTables[i].wszDllName[ARRAY_SIZE(pKbdTableMulti->multi.aKbdTables[i].wszDllName) - 1] = 0; // load it.
pkf = LoadKeyboardLayoutFile(pKbdTableMulti->files[i].hFile, pKbdTableMulti->files[i].wTable, pKbdTableMulti->files[i].wNls, pwszKLID, pKbdTableMulti->multi.aKbdTables[i].wszDllName, pKbdTableMulti->multi.aKbdTables[i].dwType, pKbdTableMulti->multi.aKbdTables[i].dwSubType); if (pkf == NULL) { // If allocation fails, simply exit this loop and continue KL creation.
RIPMSG0(RIP_WARNING, "xxxLoadKeyboardLayoutEx: failed to load the extra keyboard layout file(s)."); break; }
Lock(&pkl->pspkfExtra[n], pkf); ++n; } else { RIPMSG2(RIP_WARNING, "xxxLoadKeyboardLayoutEx: pKbdTableMulti(%#p)->files[%x].hFile is NULL", pKbdTableMulti, i); } } pkl->uNumTbl = n; } }
/*
* Link to itself in case we have to DestroyKL */ pkl->pklNext = pkl; pkl->pklPrev = pkl;
/*
* Init KL */ pkl->dwKL_Flags = 0; pkl->wchDiacritic = 0; pkl->hkl = (HKL)IntToPtr( KbdInputLocale ); RtlInitUnicodeString(&strKLID, pwszKLID); RtlUnicodeStringToInteger(&strKLID, 16, &pkl->dwKLID); TAGMSG2(DBGTAG_KBD, "xxxLoadKeyboardLayoutEx: hkl %08p KLID:%08x", pkl->hkl, pkl->dwKLID);
Lock(&pkl->spkf, pkl->spkfPrimary); pkl->dwLastKbdType = pkl->spkf->pKbdTbl->dwType; pkl->dwLastKbdSubType = pkl->spkf->pKbdTbl->dwSubType;
pkl->spkf->pKbdTbl->fLocaleFlags |= KLL_LAYOUT_ATTR_FROM_KLF(Flags);
pkl->piiex = piiex;
if (bCharSet) { pkl->CodePage = (WORD)cs.ciACP; pkl->dwFontSigs = cs.fs.fsCsb[1]; // font signature mask (FS_xxx values)
pkl->iBaseCharset = cs.ciCharset; // charset value
} else { pkl->CodePage = CP_ACP; pkl->dwFontSigs = FS_LATIN1; pkl->iBaseCharset = ANSI_CHARSET; }
/*
* Insert KL in the double-linked circular list, at the end. */ pklFirst = pwinsta->spklList; if (pklFirst == NULL) { Lock(&pwinsta->spklList, pkl); } else { pkl->pklNext = pklFirst; pkl->pklPrev = pklFirst->pklPrev; pklFirst->pklPrev->pklNext = pkl; pklFirst->pklPrev = pkl; }
AllPresentAndCorrectSir:
// FE_IME
ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlpkl);
if (hklToBeReplaced) { TL tlPKLToBeReplaced; ThreadLockAlwaysWithPti(ptiCurrent, pklToBeReplaced, &tlPKLToBeReplaced); xxxSetPKLinThreads(pkl, pklToBeReplaced); xxxInternalUnloadKeyboardLayout(pwinsta, pklToBeReplaced, KLF_INITTIME); ThreadUnlock(&tlPKLToBeReplaced); }
if (Flags & KLF_REORDER) { ReorderKeyboardLayouts(pwinsta, pkl); }
if (!(Flags & KLF_NOTELLSHELL) && IsHooked(PtiCurrent(), WHF_SHELL)) { xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)0, WH_SHELL); gLCIDSentToShell = 0; }
if (Flags & KLF_ACTIVATE) { TL tlPKL; ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlPKL); xxxInternalActivateKeyboardLayout(pkl, Flags, NULL); ThreadUnlock(&tlPKL); }
if (Flags & KLF_RESET) { RIPMSG2(RIP_VERBOSE, "Flag & KLF_RESET, locking gspklBaseLayout(%08x) with new kl(%08x)", gspklBaseLayout ? gspklBaseLayout->hkl : 0, pkl->hkl); Lock(&gspklBaseLayout, pkl); xxxSetPKLinThreads(pkl, NULL); }
/*
* Use the hkl as the layout handle * If the KL is freed somehow, return NULL for safety. -- ianja -- */ pkl = ThreadUnlock(&tlpkl); if (pkl == NULL) { return NULL; } return pkl->hkl; }
HKL xxxActivateKeyboardLayout( PWINDOWSTATION pwinsta, HKL hkl, UINT Flags, PWND pwnd) { PKL pkl; TL tlPKL; HKL hklRet; PTHREADINFO ptiCurrent = PtiCurrent();
CheckLock(pwnd);
pkl = HKLtoPKL(ptiCurrent, hkl); if (pkl == NULL) { return 0; }
if (Flags & KLF_REORDER) { ReorderKeyboardLayouts(pwinsta, pkl); }
ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlPKL); hklRet = xxxInternalActivateKeyboardLayout(pkl, Flags, pwnd); ThreadUnlock(&tlPKL); return hklRet; }
VOID ReorderKeyboardLayouts( PWINDOWSTATION pwinsta, PKL pkl) { PKL pklFirst = pwinsta->spklList;
if (pwinsta->dwWSF_Flags & WSF_NOIO) { RIPMSG1(RIP_WARNING, "ReorderKeyboardLayouts called for non-interactive windowstation %#p", pwinsta); return; }
UserAssert(pklFirst != NULL);
/*
* If the layout is already at the front of the list there's nothing to do. */ if (pkl == pklFirst) { return; } /*
* Cut pkl from circular list: */ pkl->pklPrev->pklNext = pkl->pklNext; pkl->pklNext->pklPrev = pkl->pklPrev;
/*
* Insert pkl at front of list */ pkl->pklNext = pklFirst; pkl->pklPrev = pklFirst->pklPrev;
pklFirst->pklPrev->pklNext = pkl; pklFirst->pklPrev = pkl;
Lock(&pwinsta->spklList, pkl); }
extern VOID AdjustPushStateForKL(PTHREADINFO ptiCurrent, PBYTE pbDone, PKL pklTarget, PKL pklPrev, PKL pklNew); extern void ResetPushState(PTHREADINFO pti, UINT uVk);
VOID ManageKeyboardModifiers(PKL pklPrev, PKL pkl) { PTHREADINFO ptiCurrent = PtiCurrent();
if (ptiCurrent->pq) { if (pklPrev) { BYTE baDone[256 / 8];
RtlZeroMemory(baDone, sizeof baDone);
/*
* Clear the toggle state if needed. First check the modifier keys * of pklPrev. Next check the modifier keys of pklNew. */ TAGMSG2(DBGTAG_IMM, "Changing KL from %08lx to %08lx", pklPrev->hkl, pkl->hkl); AdjustPushStateForKL(ptiCurrent, baDone, pklPrev, pklPrev, pkl); AdjustPushStateForKL(ptiCurrent, baDone, pkl, pklPrev, pkl);
if (pklPrev->spkf && (pklPrev->spkf->pKbdTbl->fLocaleFlags & KLLF_ALTGR)) { if (!TestRawKeyDown(VK_CONTROL)) { /*
* If the previous keyboard has AltGr, and if the Ctrl key is not * physically down, clear the left control. * See xxxAltGr(). */ TAGMSG0(DBGTAG_KBD, "Clearing VK_LCONTROL for AltGr\n"); xxxKeyEvent(VK_LCONTROL | KBDBREAK, 0x1D | SCANCODE_SIMULATED, 0, 0, #ifdef GENERIC_INPUT
NULL, NULL, #endif
FALSE); } } } else { /*
* If the current keyboard is unknown, clear all the push state. */ int i; for (i = 0; i < CBKEYSTATE; i++) { ptiCurrent->pq->afKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK; gafAsyncKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK; gafRawKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK; } } } }
void SetGlobalKeyboardTableInfo(PKL pklNew) { CheckCritIn(); UserAssert(pklNew);
/*
* Set gpKbdTbl so foreground thread processes AltGr appropriately */ gpKbdTbl = pklNew->spkf->pKbdTbl; if (gpKL != pklNew) { gpKL = pklNew; } if (ISTS()) { ghKbdTblBase = pklNew->spkf->hBase; guKbdTblSize = pklNew->spkf->Size; }
TAGMSG1(DBGTAG_KBD, "SetGlobalKeyboardTableInfo:Changing KL NLS Table: new HKL=%#p\n", pklNew->hkl); TAGMSG1(DBGTAG_KBD, "SetGlobalKeyboardTableInfo: new gpKbdNlsTbl=%#p\n", pklNew->spkf->pKbdNlsTbl);
gpKbdNlsTbl = pklNew->spkf->pKbdNlsTbl; }
VOID ChangeForegroundKeyboardTable(PKL pklOld, PKL pklNew) { CheckCritIn(); UserAssert(pklNew != NULL);
if ((pklOld == pklNew || (pklOld != NULL && pklOld->spkf == pklNew->spkf)) && gpKL) { return; }
/*
* Some keys (pressed to switch layout) may still be down. When these come * back up, they may have different VK values due to the new layout, so the * original key will be left stuck down. (eg: an ISV layout from Attachmate * and the CAN/CSA layout, both of which redefine the right-hand Ctrl key's * VK so switching to that layout with right Ctrl+Shift will leave the Ctrl * stuck down). * The solution is to clear all the keydown bits whenever we switch layouts * (leaving the toggle bits alone to preserve CapsLock, NumLock etc.). This * also solves the AltGr problem, where the simulated Ctrl key doesn't come * back up if we switch to a non-AltGr layout before releasing AltGr - IanJa * * Clear down bits only if necessary --- i.e. if the VK value differs between * old and new keyboard layout. We have to take complex path for some of the * keys, like Ctrl or Alt, may have left and right equivalents. - HiroYama */ ManageKeyboardModifiers(pklOld, pklNew);
// Manage the VK_KANA toggle key for Japanese KL.
// Since VK_HANGUL and VK_KANA share the same VK value and
// VK_KANA is a toggle key, when keyboard layouts are switched,
// VK_KANA toggle status should be restored.
//
// If:
// 1) Old and New keyboard layouts are both Japanese, do nothing.
// 2) Old and New keyboard layouts are not Japanese, do nothing.
// 3) Old keyboard is Japanese and new one is not, clear the KANA toggle.
// 4) New keyboard is Japanese and old one is not, restore the KANA toggle.
//
{ enum { KANA_NOOP, KANA_SET, KANA_CLEAR } opKanaToggle = KANA_NOOP;
if (JAPANESE_KBD_LAYOUT(pklNew->hkl)) { if (pklOld == NULL) { /*
* Let's honor the current async toggle state * if the old KL is not specified. */ TAGMSG0(DBGTAG_KBD, "VK_KANA: previous KL is NULL, honoring the async toggle state."); gfKanaToggle = (TestAsyncKeyStateToggle(VK_KANA) != 0); opKanaToggle = gfKanaToggle ? KANA_SET : KANA_CLEAR; } else if (!JAPANESE_KBD_LAYOUT(pklOld->hkl)) { /*
* We're switching from non JPN KL to JPN. * Need to restore the KANA toggle state. */ opKanaToggle = gfKanaToggle ? KANA_SET : KANA_CLEAR; } } else if (pklOld && JAPANESE_KBD_LAYOUT(pklOld->hkl)) { /*
* Previous KL was Japanese, and we're switching to the other language. * Let's clear the KANA toggle status and preserve it for the future * switch back to the Japanese KL. */ gfKanaToggle = (TestAsyncKeyStateToggle(VK_KANA) != 0); opKanaToggle = KANA_CLEAR; }
if (opKanaToggle == KANA_SET) { TAGMSG0(DBGTAG_KBD, "VK_KANA is being set.\n"); SetAsyncKeyStateToggle(VK_KANA); SetRawKeyToggle(VK_KANA); if (gptiForeground && gptiForeground->pq) { SetKeyStateToggle(gptiForeground->pq, VK_KANA); } } else if (opKanaToggle == KANA_CLEAR) { TAGMSG0(DBGTAG_KBD, "VK_KANA is beging cleared.\n"); ClearAsyncKeyStateToggle(VK_KANA); ClearRawKeyToggle(VK_KANA); if (gptiForeground && gptiForeground->pq) { ClearKeyStateToggle(gptiForeground->pq, VK_KANA); } }
if (opKanaToggle != KANA_NOOP) { UpdateKeyLights(TRUE); } }
UserAssert(pklNew); SetGlobalKeyboardTableInfo(pklNew); }
//
// Toggle and push state adjusters:
//
// ResetPushState, AdjustPushState, AdjustPushStateForKL
//
void ResetPushState(PTHREADINFO pti, UINT uVk) { TAGMSG1(DBGTAG_IMM, "ResetPushState: has to reset the push state of vk=%x\n", uVk); if (uVk != 0) { ClearAsyncKeyStateDown(uVk); ClearAsyncKeyStateDown(uVk); ClearRawKeyDown(uVk); ClearRawKeyToggle(uVk); ClearKeyStateDown(pti->pq, uVk); ClearKeyStateToggle(pti->pq, uVk); } }
void AdjustPushState(PTHREADINFO ptiCurrent, BYTE bBaseVk, BYTE bVkL, BYTE bVkR, PKL pklPrev, PKL pklNew) { BOOLEAN fDownL = FALSE, fDownR = FALSE; BOOLEAN fVanishL = FALSE, fVanishR = FALSE;
UINT uScanCode1, uScanCode2;
if (bVkL) { fDownL = TestRawKeyDown(bVkL) || TestAsyncKeyStateDown(bVkL) || TestKeyStateDown(ptiCurrent->pq, bVkL); if (fDownL) { uScanCode1 = InternalMapVirtualKeyEx(bVkL, 0, pklPrev->spkf->pKbdTbl); uScanCode2 = InternalMapVirtualKeyEx(bVkL, 0, pklNew->spkf->pKbdTbl); fVanishL = (uScanCode1 && uScanCode2 == 0); if (fVanishL) { TAGMSG2(DBGTAG_KBD, "AdjustPushState: clearing %02x (%02x)", bVkL, uScanCode1); xxxKeyEvent((WORD)(bVkL | KBDBREAK), (WORD)(uScanCode1 | SCANCODE_SIMULATED), 0, 0, #ifdef GENERIC_INPUT
NULL, NULL, #endif
FALSE); } } }
if (bVkR) { fDownR = TestRawKeyDown(bVkR) || TestAsyncKeyStateDown(bVkR) || TestKeyStateDown(ptiCurrent->pq, bVkR); if (fDownR) { uScanCode1 = InternalMapVirtualKeyEx(bVkR, 0, pklPrev->spkf->pKbdTbl); uScanCode2 = InternalMapVirtualKeyEx(bVkR, 0, pklNew->spkf->pKbdTbl); fVanishR = (uScanCode1 && uScanCode2 == 0); if (fVanishR) { TAGMSG2(DBGTAG_KBD, "AdjustPushState: clearing %02x (%02x)", bVkR, uScanCode1); xxxKeyEvent((WORD)(bVkR | KBDBREAK), (WORD)(uScanCode1 | SCANCODE_SIMULATED), 0, 0, #ifdef GENERIC_INPUT
NULL, NULL, #endif
FALSE); } } }
UNREFERENCED_PARAMETER(bBaseVk); }
VOID AdjustPushStateForKL(PTHREADINFO ptiCurrent, PBYTE pbDone, PKL pklTarget, PKL pklPrev, PKL pklNew) { CONST VK_TO_BIT* pVkToBits;
UserAssert(pklPrev); UserAssert(pklNew);
if (pklTarget->spkf == NULL || pklPrev->spkf == NULL) { return; }
pVkToBits = pklTarget->spkf->pKbdTbl->pCharModifiers->pVkToBit;
for (; pVkToBits->Vk; ++pVkToBits) { BYTE bVkVar1 = 0, bVkVar2 = 0;
//
// Is it already processed ?
//
UserAssert(pVkToBits->Vk < 0x100); if (pbDone[pVkToBits->Vk >> 3] & (1 << (pVkToBits->Vk & 7))) { continue; }
switch (pVkToBits->Vk) { case VK_SHIFT: bVkVar1 = VK_LSHIFT; bVkVar2 = VK_RSHIFT; break; case VK_CONTROL: bVkVar1 = VK_LCONTROL; bVkVar2 = VK_RCONTROL; break; case VK_MENU: bVkVar1 = VK_LMENU; bVkVar2 = VK_RMENU; break; }
TAGMSG3(DBGTAG_IMM, "Adjusting VK=%x var1=%x var2=%x\n", pVkToBits->Vk, bVkVar1, bVkVar2);
AdjustPushState(ptiCurrent, pVkToBits->Vk, bVkVar1, bVkVar2, pklPrev, pklNew);
pbDone[pVkToBits->Vk >> 3] |= (1 << (pVkToBits->Vk & 7)); } }
__inline BOOL IsWinSrvInputThread( PTHREADINFO pti) { UserAssert(pti); UserAssert(pti->TIF_flags & TIF_CSRSSTHREAD);
if (gptiForeground && gptiForeground->rpdesk && gptiForeground->rpdesk->dwConsoleThreadId == TIDq(pti)) { return TRUE; }
return FALSE; }
/*****************************************************************************\
* xxxInternalActivateKeyboardLayout * * pkl - pointer to keyboard layout to switch the current thread to * Flags - KLF_RESET * KLF_SETFORPROCESS * KLLF_SHIFTLOCK (any of KLLF_GLOBAL_ATTRS) * others are ignored * pwnd - If the current thread has no focus or active window, send the * WM_INPUTLANGCHANGE message to this window (unless it is NULL too) * * History: * 1998-10-14 IanJa Added pwnd parameter \*****************************************************************************/ HKL xxxInternalActivateKeyboardLayout( PKL pkl, UINT Flags, PWND pwnd) { HKL hklPrev; PKL pklPrev; TL tlpklPrev; PTHREADINFO ptiCurrent = PtiCurrent();
CheckLock(pkl); CheckLock(pwnd);
/*
* Remember what is about to become the "previously" active hkl * for the return value. */ if (ptiCurrent->spklActive != (PKL)NULL) { pklPrev = ptiCurrent->spklActive; hklPrev = ptiCurrent->spklActive->hkl; } else { pklPrev = NULL; hklPrev = (HKL)0; }
/*
* ShiftLock/CapsLock is a global feature applying to all layouts * Only Winlogon and the Input Locales cpanel applet set KLF_RESET. */ if (Flags & KLF_RESET) { gdwKeyboardAttributes = KLL_GLOBAL_ATTR_FROM_KLF(Flags); }
/*
* Early out */ if (!(Flags & KLF_SETFORPROCESS) && (pkl == ptiCurrent->spklActive)) { return hklPrev; }
/*
* Clear out diacritics when switching kbd layouts #102838 */ pkl->wchDiacritic = 0;
/*
* Update the active layout in the pti. KLF_SETFORPROCESS will always be set * when the keyboard layout switch is initiated by the keyboard hotkey. */
/*
* Lock the previous keyboard layout for it's used later. */ ThreadLockWithPti(ptiCurrent, pklPrev, &tlpklPrev);
/*
* Is this is a console thread, apply this change to any process in it's * window. This can really help character-mode apps! (#58025) */ if (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) { Lock(&ptiCurrent->spklActive, pkl); try { ptiCurrent->pClientInfo->CodePage = pkl->CodePage; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto UnlockAndGo; } } else if ((Flags & KLF_SETFORPROCESS) && !(ptiCurrent->TIF_flags & TIF_16BIT)) { /*
* For 16 bit app., only the calling thread will have its active layout updated. */ PTHREADINFO ptiT;
if (IS_IME_ENABLED()) { /*
* Only allow *NOT* CSRSS to make this call */ UserAssert(PsGetCurrentProcess() != gpepCSRSS); // pti->pClientInfo is updated in xxxImmActivateThreadsLayout()
if (!xxxImmActivateThreadsLayout(ptiCurrent->ppi->ptiList, NULL, pkl)) { RIPMSG1(RIP_WARNING, "no layout change necessary via xxxImmActivateThreadLayout() for process %lx", ptiCurrent->ppi); goto UnlockAndGo; } } else { BOOL fKLChanged = FALSE;
for (ptiT = ptiCurrent->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) { if (ptiT->spklActive != pkl && (ptiT->TIF_flags & TIF_INCLEANUP) == 0) { Lock(&ptiT->spklActive, pkl); UserAssert(ptiT->pClientInfo != NULL); try { ptiT->pClientInfo->CodePage = pkl->CodePage; ptiT->pClientInfo->hKL = pkl->hkl; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto UnlockAndGo; }
fKLChanged = TRUE; } } if (!fKLChanged) { RIPMSG1(RIP_WARNING, "no layout change necessary for process %lx ?", ptiCurrent->ppi); goto UnlockAndGo; } }
} else { if (IS_IME_ENABLED()) { xxxImmActivateLayout(ptiCurrent, pkl); } else { Lock(&ptiCurrent->spklActive, pkl); } UserAssert(ptiCurrent->pClientInfo != NULL); if ((ptiCurrent->TIF_flags & TIF_INCLEANUP) == 0) { try { ptiCurrent->pClientInfo->CodePage = pkl->CodePage; ptiCurrent->pClientInfo->hKL = pkl->hkl; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto UnlockAndGo; } } }
/*
* If ( * 1a. The process is not CSRSS. or * b. it's CSRSS input thread. * 2. and, the process is foreground. * ) * update gpKbdTbl for the proper AltGr processing, * and let the shell hook (primarily Internat.exe) * know the foreground app's new keyboard layout. */
// if ((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0 || IsWinSrvInputThread(ptiCurrent)) {
if (gptiForeground && (gptiForeground->ppi == ptiCurrent->ppi)) { ChangeForegroundKeyboardTable(pklPrev, pkl);
/*
* Call the Shell hook with the new language. * Only call the hook if we are the foreground process, to prevent * background apps from changing the indicator. (All console apps * are part of the same process, but I have never seen a cmd window * app change the layout, let alone in the background) */ if (gLCIDSentToShell != pkl->hkl && (ptiCurrent != gptiRit)) { if (IsHooked(ptiCurrent, WHF_SHELL)) { gLCIDSentToShell = pkl->hkl; xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)pkl->hkl, WH_SHELL); } } } // }
/*
* Tell the app what happened */ if (ptiCurrent->pq) { PWND pwndT; TL tlpwndT;
/*
* If we have no Focus window, use the Active window. * eg: Console full-screen has NULL focus window. */ pwndT = ptiCurrent->pq->spwndFocus; if (pwndT == NULL) { pwndT = ptiCurrent->pq->spwndActive; if (pwndT == NULL) { pwndT = pwnd; } }
if (pwndT != NULL) { ThreadLockAlwaysWithPti( ptiCurrent, pwndT, &tlpwndT); xxxSendMessage(pwndT, WM_INPUTLANGCHANGE, (WPARAM)pkl->iBaseCharset, (LPARAM)pkl->hkl); ThreadUnlock(&tlpwndT); } }
/*
* Tell IME to send mode update notification */ if (ptiCurrent && ptiCurrent->spwndDefaultIme && (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0) { if (IS_IME_KBDLAYOUT(pkl->hkl) #ifdef CUAS_ENABLE
|| IS_CICERO_ENABLED_AND_NOT16BIT() #endif // CUAS_ENABLE
) { BOOL fForProcess = (ptiCurrent->TIF_flags & KLF_SETFORPROCESS) && !(ptiCurrent->TIF_flags & TIF_16BIT); TL tlpwndIme;
TAGMSG1(DBGTAG_IMM, "Sending IMS_SENDNOTIFICATION to pwnd=%#p", ptiCurrent->spwndDefaultIme);
ThreadLockAlwaysWithPti(ptiCurrent, ptiCurrent->spwndDefaultIme, &tlpwndIme); xxxSendMessage(ptiCurrent->spwndDefaultIme, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, fForProcess); ThreadUnlock(&tlpwndIme); } }
UnlockAndGo: ThreadUnlock(&tlpklPrev);
return hklPrev; }
BOOL xxxUnloadKeyboardLayout( PWINDOWSTATION pwinsta, HKL hkl) { PKL pkl;
/*
* Validate HKL and check to make sure an app isn't attempting to unload a system * preloaded layout. */ pkl = HKLtoPKL(PtiCurrent(), hkl); if (pkl == NULL) { return FALSE; }
return xxxInternalUnloadKeyboardLayout(pwinsta, pkl, 0); }
HKL _GetKeyboardLayout( DWORD idThread) { PTHREADINFO ptiT; PLIST_ENTRY pHead, pEntry;
CheckCritIn();
/*
* If idThread is NULL return hkl of the current thread */ if (idThread == 0) { PKL pklActive = PtiCurrentShared()->spklActive;
if (pklActive == NULL) { return (HKL)0; } return pklActive->hkl; } /*
* Look for idThread */ pHead = &PtiCurrent()->rpdesk->PtiList; for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) { ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink); if (GETPTIID(ptiT) == (HANDLE)LongToHandle(idThread)) { if (ptiT->spklActive == NULL) { return (HKL)0; } return ptiT->spklActive->hkl; } } /*
* idThread doesn't exist */ return (HKL)0; }
UINT _GetKeyboardLayoutList( PWINDOWSTATION pwinsta, UINT nItems, HKL *ccxlpBuff) { UINT nHKL = 0; PKL pkl, pklFirst;
if (!pwinsta) { return 0; }
pkl = pwinsta->spklList;
/*
* Windowstations that do not take input could have no layouts */ if (pkl == NULL) { // SetLastError() ????
return 0; }
/*
* The client/server thunk sets nItems to 0 if ccxlpBuff == NULL */ UserAssert(ccxlpBuff || (nItems == 0));
pklFirst = pkl; if (nItems) { try { do { if (!(pkl->dwKL_Flags & KL_UNLOADED)) { if (nItems-- == 0) { break; } nHKL++; *ccxlpBuff++ = pkl->hkl; } pkl = pkl->pklNext; } while (pkl != pklFirst); } except (EXCEPTION_EXECUTE_HANDLER) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_ERROR, "_GetKeyBoardLayoutList: exception writing ccxlpBuff %lx", ccxlpBuff); return 0; } } else do { if (!(pkl->dwKL_Flags & KL_UNLOADED)) { nHKL++; } pkl = pkl->pklNext; } while (pkl != pklFirst);
return nHKL; }
/*
* Layouts are locked by each thread using them and possibly by: * - pwinsta->spklList (head of windowstation's list) * - gspklBaseLayout (default layout for new threads) * The layout is marked for destruction when gets unloaded, so that it will be * unlinked and freed as soon as an Unlock causes the lock count to go to 0. * If it is reloaded before that time, it is unmarked for destruction. This * ensures that laoded layouts stay around even when they go out of use. */ BOOL xxxInternalUnloadKeyboardLayout( PWINDOWSTATION pwinsta, PKL pkl, UINT Flags) { PTHREADINFO ptiCurrent = PtiCurrent(); TL tlpkl;
UserAssert(pkl);
/*
* Never unload the default layout, unless we are destroying the current * windowstation or replacing one user's layouts with another's. */ if ((pkl == gspklBaseLayout) && !(Flags & KLF_INITTIME)) { return FALSE; }
/*
* Keeps pkl good, but also allows destruction when unlocked later */ ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlpkl);
/*
* Mark it for destruction so it gets removed when the lock count reaches 0 * Mark it KL_UNLOADED so that it appears to be gone from the toggle list */ HMMarkObjectDestroy(pkl); pkl->dwKL_Flags |= KL_UNLOADED;
/*
* If unloading this thread's active layout, helpfully activate the next one * (Don't bother if KLF_INITTIME - unloading all previous user's layouts) */ if (!(Flags & KLF_INITTIME)) { UserAssert(ptiCurrent->spklActive != NULL); if (ptiCurrent->spklActive == pkl) { PKL pklNext; pklNext = HKLtoPKL(ptiCurrent, (HKL)HKL_NEXT); if (pklNext != NULL) { TL tlPKL; ThreadLockAlwaysWithPti(ptiCurrent, pklNext, &tlPKL); xxxInternalActivateKeyboardLayout(pklNext, Flags, NULL); ThreadUnlock(&tlPKL); } } }
/*
* If this pkl == pwinsta->spklList, give it a chance to be destroyed by * unlocking it from pwinsta->spklList. */ if (pwinsta->spklList == pkl) { UserAssert(pkl != NULL); if (pkl != pkl->pklNext) { pkl = Lock(&pwinsta->spklList, pkl->pklNext); UserAssert(pkl != NULL); // gspklBaseLayout and ThreadLocked pkl
} }
/*
* This finally destroys the unloaded layout if it is not in use anywhere */ ThreadUnlock(&tlpkl);
/*
* Update keyboard list. */ if (IsHooked(ptiCurrent, WHF_SHELL)) { xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)0, WH_SHELL); gLCIDSentToShell = 0; }
return TRUE; }
VOID xxxFreeKeyboardLayouts( PWINDOWSTATION pwinsta, BOOL bUnlock) { PKL pkl;
/*
* Unload all of the windowstation's layouts. * They may still be locked by some threads (eg: console), so this * may not destroy them all, but it will mark them all KL_UNLOADED. * Set KLF_INITTIME to ensure that the default layout (gspklBaseLayout) * gets unloaded too. * Note: it's much faster to unload non-active layouts, so start with * the next loaded layout, leaving the active layout till last. */ while ((pkl = HKLtoPKL(PtiCurrent(), (HKL)HKL_NEXT)) != NULL) { xxxInternalUnloadKeyboardLayout(pwinsta, pkl, KLF_INITTIME); }
/*
* The WindowStation is being destroyed, or one user's layouts are being * replaced by another user's, so it's OK to Unlock spklList. * Any layout still in the double-linked circular KL list will still be * pointed to by gspklBaseLayout: this is important, since we don't want * to leak any KL or KBDFILE objects by losing pointers to them. * There are no layouts when we first come here (during bootup). */ if (bUnlock) { Unlock(&pwinsta->spklList); } }
/***************************************************************************\
* DestroyKL * * Destroys a keyboard layout. Note that this function does not * follow normal destroy function semantics. See IanJa. * * History: * 25-Feb-1997 adams Created. \***************************************************************************/
VOID DestroyKL( PKL pkl) { PKBDFILE pkf;
/*
* Cut it out of the pwinsta->spklList circular bidirectional list. * We know pwinsta->spklList != pkl, since pkl is unlocked. */ pkl->pklPrev->pklNext = pkl->pklNext; pkl->pklNext->pklPrev = pkl->pklPrev;
/*
* Unlock its pkf */ pkf = Unlock(&pkl->spkf); if (pkf && (pkf = Unlock(&pkl->spkfPrimary))) { DestroyKF(pkf); }
if (pkl->pspkfExtra) { UINT i;
for (i = 0; i < pkl->uNumTbl && pkl->pspkfExtra[i]; ++i) { pkf = Unlock(&pkl->pspkfExtra[i]); if (pkf) { DestroyKF(pkf); } } UserFreePool(pkl->pspkfExtra); }
if (pkl->piiex != NULL) { UserFreePool(pkl->piiex); }
if (pkl == gpKL) { /*
* Nuke gpKL. */ gpKL = NULL; }
/*
* Free the pkl itself. */ HMFreeObject(pkl); }
/***************************************************************************\
* CleanupKeyboardLayouts * * Frees the all keyboard layouts in this session. * \***************************************************************************/ VOID CleanupKeyboardLayouts() { /*
* Unlock the keyboard layouts */ if (gspklBaseLayout != NULL) {
PKL pkl; PKL pklNext;
pkl = gspklBaseLayout->pklNext;
while (pkl->pklNext != pkl) { pklNext = pkl->pklNext;
DestroyKL(pkl);
pkl = pklNext; }
UserAssert(pkl == gspklBaseLayout);
if (!HMIsMarkDestroy(gspklBaseLayout)) { HMMarkObjectDestroy(gspklBaseLayout); }
HYDRA_HINT(HH_KBDLYOUTGLOBALCLEANUP);
if (Unlock(&gspklBaseLayout)) { DestroyKL(pkl); } }
UserAssert(gpkfList == NULL); }
|