mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1001 lines
27 KiB
1001 lines
27 KiB
/**************************** Module Header ********************************\
|
|
* Copyright 1985-92, 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);
|
|
|
|
/*
|
|
* Note that this only works for sections < 64K
|
|
*/
|
|
#define FIXUP_PTR(p, pBase) ((p) ? (p) = (PVOID)((PBYTE)pBase + (WORD)(p)) : 0)
|
|
|
|
|
|
/****************************************************************************\
|
|
* HKLtoPKL
|
|
*
|
|
* Given HKL_NEXT or HKL_PREV
|
|
* Finds the the next/prev LOADED layout, NULL if none.
|
|
* (Starts from the current active layout, may return pklActive itself)
|
|
*
|
|
* Given Keyboard Layout handle:
|
|
* Find the kbd layout struct (loaded or not), NULL if no match found.
|
|
*
|
|
* History:
|
|
\****************************************************************************/
|
|
PKL HKLtoPKL(
|
|
HKL hkl)
|
|
{
|
|
PKL pklActive;
|
|
PKL pkl;
|
|
|
|
if ((pklActive = PtiCurrentShared()->spklActive) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pkl = pklActive;
|
|
|
|
if ((DWORD)hkl == HKL_PREV) {
|
|
do {
|
|
pkl = pkl->pklPrev;
|
|
if (!(pkl->dwFlags & KL_UNLOADED)) {
|
|
return pkl;
|
|
}
|
|
} while (pkl != pklActive);
|
|
return NULL;
|
|
} else if ((DWORD)hkl == HKL_NEXT) {
|
|
do {
|
|
pkl = pkl->pklNext;
|
|
if (!(pkl->dwFlags & KL_UNLOADED)) {
|
|
return pkl;
|
|
}
|
|
} while (pkl != pklActive);
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
if (pkl->hkl == 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.
|
|
\***************************************************************************/
|
|
|
|
PKBDTABLES ReadLayoutFile(
|
|
PKBDFILE pkf,
|
|
HANDLE hFile,
|
|
UINT offTable)
|
|
{
|
|
HANDLE hmap;
|
|
ULONG ulViewSize = 0;
|
|
NTSTATUS Status;
|
|
PIMAGE_DOS_HEADER DosHdr;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_SECTION_HEADER SectionTableEntry;
|
|
ULONG NumberOfSubsections;
|
|
ULONG OffsetToSectionTable;
|
|
PBYTE pBaseDst, pBaseVirt;
|
|
PKBDTABLES pktNew = NULL;
|
|
DWORD dwDataSize;
|
|
|
|
/*
|
|
* Map the layout file into memory
|
|
*/
|
|
DosHdr = NULL;
|
|
Status = ZwCreateSection(&hmap, SECTION_ALL_ACCESS, NULL,
|
|
NULL, PAGE_READONLY, SEC_COMMIT, hFile);
|
|
if (!NT_SUCCESS(Status))
|
|
return NULL;
|
|
|
|
Status = ZwMapViewOfSection(hmap, NtCurrentProcess(), &DosHdr, 0, 0, NULL,
|
|
&ulViewSize, ViewShare, 0, PAGE_READONLY);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto exitread;
|
|
}
|
|
|
|
/*
|
|
* HACK Part 2! 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.
|
|
*/
|
|
NtHeader = (PIMAGE_NT_HEADERS)((ULONG)DosHdr + (ULONG)DosHdr->e_lfanew);
|
|
|
|
/*
|
|
* Build the next subsections.
|
|
*/
|
|
NumberOfSubsections = NtHeader->FileHeader.NumberOfSections;
|
|
|
|
/*
|
|
* At this point the object table is read in (if it was not
|
|
* already read in) and may displace the image header.
|
|
*/
|
|
OffsetToSectionTable = sizeof(ULONG) +
|
|
sizeof(IMAGE_FILE_HEADER) +
|
|
NtHeader->FileHeader.SizeOfOptionalHeader;
|
|
SectionTableEntry = (PIMAGE_SECTION_HEADER)((ULONG)NtHeader +
|
|
OffsetToSectionTable);
|
|
|
|
while (NumberOfSubsections > 0) {
|
|
if (strcmp(SectionTableEntry->Name, ".data") == 0)
|
|
break;
|
|
|
|
SectionTableEntry++;
|
|
NumberOfSubsections--;
|
|
}
|
|
if (NumberOfSubsections == 0) {
|
|
goto exitread;
|
|
}
|
|
|
|
/*
|
|
* We found the section, now compute starting offset and the table size.
|
|
*/
|
|
offTable -= SectionTableEntry->VirtualAddress;
|
|
dwDataSize = SectionTableEntry->Misc.VirtualSize;
|
|
|
|
/*
|
|
* Allocate layout table and copy from file.
|
|
*/
|
|
pBaseDst = UserAllocPool(dwDataSize, TAG_KBDTABLE);
|
|
if (pBaseDst != NULL) {
|
|
VK_TO_WCHAR_TABLE *pVkToWcharTable;
|
|
VSC_LPWSTR *pKeyName;
|
|
LPWSTR *lpDeadKey;
|
|
|
|
pkf->hBase = (HANDLE)pBaseDst;
|
|
RtlMoveMemory(pBaseDst, (PBYTE)DosHdr +
|
|
SectionTableEntry->PointerToRawData, dwDataSize);
|
|
|
|
/*
|
|
* 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, pBaseVirt);
|
|
FIXUP_PTR(pktNew->pCharModifiers->pVkToBit, pBaseVirt);
|
|
if (FIXUP_PTR(pktNew->pVkToWcharTable, pBaseVirt)) {
|
|
for (pVkToWcharTable = pktNew->pVkToWcharTable;
|
|
pVkToWcharTable->pVkToWchars != NULL; pVkToWcharTable++)
|
|
FIXUP_PTR(pVkToWcharTable->pVkToWchars, pBaseVirt);
|
|
}
|
|
FIXUP_PTR(pktNew->pDeadKey, pBaseVirt);
|
|
if (FIXUP_PTR(pktNew->pKeyNames, pBaseVirt)) {
|
|
for (pKeyName = pktNew->pKeyNames; pKeyName->vsc != 0; pKeyName++)
|
|
FIXUP_PTR(pKeyName->pwsz, pBaseVirt);
|
|
}
|
|
if (FIXUP_PTR(pktNew->pKeyNamesExt, pBaseVirt)) {
|
|
for (pKeyName = pktNew->pKeyNamesExt; pKeyName->vsc != 0; pKeyName++)
|
|
FIXUP_PTR(pKeyName->pwsz, pBaseVirt);
|
|
}
|
|
if (FIXUP_PTR(pktNew->pKeyNamesDead, pBaseVirt)) {
|
|
for (lpDeadKey = pktNew->pKeyNamesDead; *lpDeadKey != NULL;
|
|
lpDeadKey++)
|
|
FIXUP_PTR(*lpDeadKey, pBaseVirt);
|
|
}
|
|
FIXUP_PTR(pktNew->pusVSCtoVK, pBaseVirt);
|
|
FIXUP_PTR(pktNew->pVSCtoVK_E0, pBaseVirt);
|
|
FIXUP_PTR(pktNew->pVSCtoVK_E1, pBaseVirt);
|
|
}
|
|
|
|
exitread:
|
|
|
|
/*
|
|
* Unmap and release the mapped section.
|
|
*/
|
|
ZwUnmapViewOfSection(NtCurrentProcess(), DosHdr);
|
|
ZwClose(hmap);
|
|
|
|
return pktNew;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LoadKeyboardLayoutFile
|
|
*
|
|
* History:
|
|
* 10-29-95 GregoryW Created.
|
|
\***************************************************************************/
|
|
|
|
PKBDFILE LoadKeyboardLayoutFile(
|
|
HANDLE hFile,
|
|
UINT offTable,
|
|
LPCWSTR pwszKLID)
|
|
{
|
|
PKBDFILE pkf = gpkfList;
|
|
|
|
if (pkf) {
|
|
int iCmp;
|
|
|
|
do {
|
|
iCmp = wcscmp(pkf->awchKF, pwszKLID);
|
|
if (iCmp == 0) {
|
|
|
|
/*
|
|
* The layout is already loaded.
|
|
*/
|
|
return pkf;
|
|
}
|
|
pkf = pkf->pkfNext;
|
|
} while (pkf);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
pkf->pKbdTbl = ReadLayoutFile(pkf, hFile, offTable);
|
|
if (pkf->pKbdTbl == NULL) {
|
|
HMFreeObject(pkf);
|
|
return (PKBDFILE)NULL;
|
|
}
|
|
wcsncpycch(pkf->awchKF, pwszKLID, sizeof(pkf->awchKF) / sizeof(WCHAR));
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
VOID SetPKLinThreads(
|
|
PKL pklCurrent,
|
|
PKL pklReplace)
|
|
{
|
|
PTHREADINFO ptiT;
|
|
PEPROCESS pEProcess;
|
|
PETHREAD pEThread;
|
|
PLIST_ENTRY ProcessHead, NextProcess, NextThread;
|
|
|
|
/*
|
|
* Update pkl entries in the THREADINFO structures.
|
|
* gpepCSRSS might still be NULL if csrss process hasn't started yet.
|
|
*/
|
|
pEProcess = gpepCSRSS;
|
|
if (pEProcess != NULL) {
|
|
ProcessHead = pEProcess->ActiveProcessLinks.Flink;
|
|
NextProcess = ProcessHead;
|
|
do {
|
|
pEProcess = CONTAINING_RECORD(NextProcess, EPROCESS, ActiveProcessLinks);
|
|
if (pEProcess->Pcb.Header.Type != ProcessObject) {
|
|
/*
|
|
* We've come across PsActiveProcessHead...skip it.
|
|
*/
|
|
NextProcess = NextProcess->Flink;
|
|
continue;
|
|
}
|
|
NextProcess = pEProcess->ActiveProcessLinks.Flink;
|
|
if (pEProcess->Win32Process == NULL) {
|
|
continue;
|
|
}
|
|
NextThread = pEProcess->Pcb.ThreadListHead.Flink;
|
|
while (NextThread != &pEProcess->Pcb.ThreadListHead) {
|
|
pEThread = (PETHREAD)CONTAINING_RECORD(NextThread, KTHREAD, ThreadListEntry);
|
|
NextThread = ((PKTHREAD)pEThread)->ThreadListEntry.Flink;
|
|
ptiT = PtiFromThread(pEThread);
|
|
if (ptiT == NULL) {
|
|
continue;
|
|
}
|
|
if (!pklReplace) {
|
|
Lock(&ptiT->spklActive, pklCurrent);
|
|
} else if (pklReplace->hkl == ptiT->spklActive->hkl) {
|
|
Lock(&ptiT->spklActive, pklCurrent);
|
|
}
|
|
}
|
|
} while (NextProcess != ProcessHead);
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "gpepCSRSS not yet initialized");
|
|
}
|
|
|
|
/*
|
|
* 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 (pklReplace) {
|
|
if (pklReplace->pklNext == pklCurrent) {
|
|
/*
|
|
* 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.
|
|
*/
|
|
pklCurrent->pklPrev->pklNext = pklCurrent->pklNext;
|
|
pklCurrent->pklNext->pklPrev = pklCurrent->pklPrev;
|
|
|
|
pklCurrent->pklNext = pklReplace->pklNext;
|
|
pklCurrent->pklPrev = pklReplace;
|
|
|
|
pklReplace->pklNext->pklPrev = pklCurrent;
|
|
pklReplace->pklNext = pklCurrent;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxLoadKeyboardLayout
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
HKL xxxLoadKeyboardLayoutEx(
|
|
PWINDOWSTATION pwinsta,
|
|
HANDLE hFile,
|
|
HKL hklReplace,
|
|
UINT offTable,
|
|
LPCWSTR pwszKLID,
|
|
UINT KbdInputLocale,
|
|
UINT Flags)
|
|
{
|
|
PKL pkl, pklFirst, pklReplace;
|
|
PKBDFILE pkf;
|
|
CHARSETINFO cs;
|
|
#ifdef FE_IME
|
|
PIMEINFOEX piiex = NULL;
|
|
#endif
|
|
|
|
/*
|
|
* If the windowstation does not do I/O, don't load the
|
|
* layout.
|
|
*/
|
|
if (pwinsta->dwFlags & WSF_NOIO) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If hklReplace is non-NULL make sure it's valid.
|
|
* NOTE: may want to verify they're not passing HKL_NEXT or HKL_PREV.
|
|
*/
|
|
if (hklReplace && !(pklReplace = HKLtoPKL(hklReplace))) {
|
|
return NULL;
|
|
}
|
|
if (KbdInputLocale == (UINT)hklReplace) {
|
|
/*
|
|
* Replacing a layout/lang pair with itself. Nothing to do.
|
|
*/
|
|
return pklReplace->hkl;
|
|
}
|
|
|
|
/*
|
|
* LATER - should not allow KLF_RESET for just any thread [ianja]
|
|
*/
|
|
if (Flags & KLF_RESET) {
|
|
xxxFreeKeyboardLayouts(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)
|
|
*/
|
|
Lock(&pwinsta->spklList, gspklBaseLayout);
|
|
}
|
|
|
|
/*
|
|
* Does this hkl already exist?
|
|
*/
|
|
pkl = pklFirst = pwinsta->spklList;
|
|
if (pkl) {
|
|
do {
|
|
if (pkl->hkl == (HKL)KbdInputLocale) {
|
|
/*
|
|
* The hkl already exists.
|
|
*/
|
|
|
|
/*
|
|
* If it is unloaded (but not yet destroyed because it is
|
|
* still is use), recover it.
|
|
*/
|
|
if (pkl->dwFlags & 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;
|
|
#ifdef DEBUG
|
|
phe->bFlags &= ~HANDLEF_MARKED_OK;
|
|
#endif
|
|
pkl->dwFlags &= ~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);
|
|
}
|
|
|
|
/*
|
|
* Keyboard Layout Handle object does not exist. Load keyboard layout file,
|
|
* if not already loaded.
|
|
*/
|
|
if (!(pkf = LoadKeyboardLayoutFile(hFile, offTable, pwszKLID))) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Link to itself in case we have to DestroyKL
|
|
*/
|
|
pkl->pklNext = pkl;
|
|
pkl->pklPrev = pkl;
|
|
|
|
/*
|
|
* Init KL
|
|
*/
|
|
pkl->dwFlags = 0;
|
|
pkl->hkl = (HKL)KbdInputLocale;
|
|
Lock(&pkl->spkf, pkf);
|
|
|
|
#ifdef FE_IME
|
|
if (IS_IME_KBDLAYOUT((HKL)KbdInputLocale)) {
|
|
/*
|
|
* This is an IME keyboard layout, do a callback
|
|
* to read the extended IME information structure.
|
|
*/
|
|
piiex = xxxImmLoadLayout((HKL)KbdInputLocale);
|
|
if (!piiex) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Keyboard Layout: xxxImmLoadLayout(%lx) failed", KbdInputLocale);
|
|
DestroyKL(pkl);
|
|
return NULL;
|
|
}
|
|
}
|
|
pkl->piiex = piiex;
|
|
#endif
|
|
|
|
if (xxxClientGetCharsetInfo(HIWORD(KbdInputLocale), &cs)) {
|
|
pkl->CodePage = cs.ciACP;
|
|
pkl->bCharsets = cs.fs.fsCsb[0]; // Windows charset bitfield. These are FS_xxx values
|
|
pkl->iBaseCharset = cs.ciCharset; // charset value
|
|
} else {
|
|
pkl->CodePage = CP_ACP;
|
|
pkl->bCharsets = FS_LATIN1;
|
|
pkl->iBaseCharset = ANSI_CHARSET;
|
|
}
|
|
|
|
/*
|
|
* Get the system's codepage bitfield. These are 64-bit FS_xxx values,
|
|
* but we only need ANSI ones, so gSystemCPB is just a DWORD.
|
|
* gSystemCPB is consulted when posting WM_INPUTLANGCHANGEREQUEST (input.c)
|
|
*/
|
|
if (gSystemCPB == 0) {
|
|
LCID lcid;
|
|
|
|
ZwQueryDefaultLocale(FALSE, &lcid);
|
|
if (xxxClientGetCharsetInfo(lcid, &cs)) {
|
|
gSystemCPB = cs.fs.fsCsb[0];
|
|
} else {
|
|
gSystemCPB = 0xFFFF;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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:
|
|
if (hklReplace) {
|
|
SetPKLinThreads(pkl, pklReplace);
|
|
xxxInternalUnloadKeyboardLayout(pwinsta, pklReplace, KLF_INITTIME);
|
|
}
|
|
|
|
if (Flags & KLF_REORDER) {
|
|
ReorderKeyboardLayouts(pwinsta, pkl);
|
|
}
|
|
|
|
if (!(Flags & KLF_NOTELLSHELL) && IsHooked(PtiCurrent(), WHF_SHELL)) {
|
|
xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)0, WH_SHELL);
|
|
LCIDSentToShell = 0;
|
|
}
|
|
|
|
if (Flags & KLF_ACTIVATE) {
|
|
TL tlPKL;
|
|
ThreadLockAlways(pkl, &tlPKL);
|
|
xxxInternalActivateKeyboardLayout(pkl, Flags);
|
|
ThreadUnlock(&tlPKL);
|
|
}
|
|
|
|
if (Flags & KLF_RESET) {
|
|
Lock(&gspklBaseLayout, pkl);
|
|
SetPKLinThreads(pkl, NULL);
|
|
}
|
|
|
|
/*
|
|
* Use the hkl as the layout handle
|
|
*/
|
|
return (HANDLE)pkl->hkl;
|
|
}
|
|
|
|
HKL xxxActivateKeyboardLayout(
|
|
PWINDOWSTATION pwinsta,
|
|
HKL hkl,
|
|
UINT Flags)
|
|
{
|
|
PKL pkl;
|
|
TL tlPKL;
|
|
HKL hklRet;
|
|
|
|
pkl = HKLtoPKL(hkl);
|
|
if (pkl == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (Flags & KLF_REORDER) {
|
|
ReorderKeyboardLayouts(pwinsta, pkl);
|
|
}
|
|
|
|
ThreadLockAlways(pkl, &tlPKL);
|
|
hklRet = xxxInternalActivateKeyboardLayout(pkl, Flags);
|
|
ThreadUnlock(&tlPKL);
|
|
return hklRet;
|
|
}
|
|
|
|
VOID ReorderKeyboardLayouts(
|
|
PWINDOWSTATION pwinsta,
|
|
PKL pkl)
|
|
{
|
|
PKL pklFirst = pwinsta->spklList;
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
HKL xxxInternalActivateKeyboardLayout(
|
|
PKL pkl,
|
|
UINT Flags)
|
|
{
|
|
HKL hklPrev;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
#if 0
|
|
TL tlpwndFocus;
|
|
#endif
|
|
CheckLock(pkl);
|
|
|
|
/*
|
|
* Remember what is about to become the "previously" active hkl
|
|
* for the return value.
|
|
*/
|
|
if (ptiCurrent->spklActive != (PKL)NULL) {
|
|
hklPrev = ptiCurrent->spklActive->hkl;
|
|
} else {
|
|
hklPrev = (HKL)0;
|
|
}
|
|
|
|
/*
|
|
* Early out
|
|
*/
|
|
if (!(Flags & KLF_SETFORPROCESS) && (pkl == ptiCurrent->spklActive)) {
|
|
return hklPrev;
|
|
}
|
|
|
|
/*
|
|
* Update the active layout in the pti. KLF_SETFORPROCESS will always be set
|
|
* when the keyboard layout switch is initiated by the keyboard hotkey.
|
|
*/
|
|
#ifdef FE_IME
|
|
/*
|
|
* For 16 bit app., only the calling thread will have its active layout updated.
|
|
*/
|
|
if ((Flags & KLF_SETFORPROCESS) && !(ptiCurrent->TIF_flags & TIF_16BIT)) {
|
|
if (!xxxImmActivateThreadsLayout(ptiCurrent->ppi->ptiList, NULL, pkl))
|
|
return hklPrev;
|
|
} else {
|
|
xxxImmActivateLayout(ptiCurrent, pkl);
|
|
}
|
|
#else
|
|
if (Flags & KLF_SETFORPROCESS) {
|
|
PTHREADINFO ptiT;
|
|
BOOL fKLChanged = FALSE;
|
|
|
|
for (ptiT = ptiCurrent->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
|
|
if (ptiT->spklActive != pkl) {
|
|
Lock(&ptiT->spklActive, pkl);
|
|
fKLChanged = TRUE;
|
|
}
|
|
}
|
|
if (!fKLChanged) {
|
|
return hklPrev;
|
|
}
|
|
} else {
|
|
Lock(&ptiCurrent->spklActive, pkl);
|
|
}
|
|
#endif
|
|
|
|
UserAssert(ptiCurrent->pClientInfo != NULL);
|
|
|
|
ptiCurrent->pClientInfo->CodePage = pkl->CodePage;
|
|
|
|
if (ptiCurrent->pq && ptiCurrent->pq->spwndFocus) {
|
|
PWND pwndT;
|
|
TL tlpwndT;
|
|
|
|
if ((pwndT = GetTopLevelWindow(ptiCurrent->pq->spwndFocus)) != NULL) {
|
|
ThreadLockAlwaysWithPti( ptiCurrent, pwndT, &tlpwndT);
|
|
xxxSendMessage(pwndT, WM_INPUTLANGCHANGE, (WPARAM)pkl->iBaseCharset, (LPARAM)pkl->hkl);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
}
|
|
|
|
if (gptiForeground && (gptiForeground->ppi == ptiCurrent->ppi)) {
|
|
/*
|
|
* Set gpKbdTbl so foreground thread processes AltGr appropriately
|
|
*/
|
|
gpKbdTbl = pkl->spkf->pKbdTbl;
|
|
|
|
/*
|
|
* 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 (LCIDSentToShell != pkl->hkl && (ptiCurrent != gptiRit)) {
|
|
if (IsHooked(ptiCurrent, WHF_SHELL)) {
|
|
xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)pkl->hkl, WH_SHELL);
|
|
LCIDSentToShell = pkl->hkl;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(hkl);
|
|
if (pkl == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
return xxxInternalUnloadKeyboardLayout(pwinsta, pkl, 0);
|
|
}
|
|
|
|
BOOL _GetKeyboardLayoutName(
|
|
PUNICODE_STRING pstrKL)
|
|
{
|
|
PTHREADINFO ptiCurrent = PtiCurrentShared();
|
|
PKL pklActive;
|
|
|
|
pklActive = ptiCurrent->spklActive;
|
|
|
|
if (pklActive == NULL) {
|
|
return FALSE;
|
|
}
|
|
wcsncpycch(pstrKL->Buffer, pklActive->spkf->awchKF, pstrKL->MaximumLength / sizeof(WCHAR));
|
|
return TRUE;
|
|
}
|
|
|
|
HKL _GetKeyboardLayout(
|
|
DWORD idThread)
|
|
{
|
|
PTHREADINFO ptiT;
|
|
PLIST_ENTRY pHead, pEntry;
|
|
|
|
/*
|
|
* If idThread is NULL return hkl of the current thread
|
|
*/
|
|
if (idThread == 0) {
|
|
return PtiCurrentShared()->spklActive->hkl;
|
|
}
|
|
/*
|
|
* Look for idThread
|
|
*/
|
|
pHead = &PtiCurrent()->rpdesk->PtiList;
|
|
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
|
|
ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
|
|
if (ptiT->Thread->Cid.UniqueThread == (HANDLE)idThread) {
|
|
return ptiT->spklActive->hkl;
|
|
}
|
|
}
|
|
/*
|
|
* idThread doesn't exist
|
|
*/
|
|
return (HKL)0;
|
|
}
|
|
|
|
UINT _GetKeyboardLayoutList(
|
|
PWINDOWSTATION pwinsta,
|
|
UINT nItems,
|
|
HKL *lpBuff)
|
|
{
|
|
UINT nHKL = 0;
|
|
PKL pkl, pklFirst;
|
|
|
|
|
|
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 lpBuff == NULL
|
|
*/
|
|
pklFirst = pkl;
|
|
if (nItems) {
|
|
do {
|
|
if (!(pkl->dwFlags & KL_UNLOADED)) {
|
|
if (nItems-- == 0) {
|
|
break;
|
|
}
|
|
nHKL++;
|
|
*lpBuff++ = pkl->hkl;
|
|
}
|
|
pkl = pkl->pklNext;
|
|
} while (pkl != pklFirst);
|
|
} else do {
|
|
if (!(pkl->dwFlags & 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;
|
|
|
|
/*
|
|
* 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->dwFlags |= 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((HKL)HKL_NEXT);
|
|
if (pklNext != NULL) {
|
|
TL tlPKL;
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pklNext, &tlPKL);
|
|
xxxInternalActivateKeyboardLayout(pklNext, Flags);
|
|
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);
|
|
LCIDSentToShell = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID xxxFreeKeyboardLayouts(
|
|
PWINDOWSTATION pwinsta)
|
|
{
|
|
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((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).
|
|
*/
|
|
Unlock(&pwinsta->spklList);
|
|
}
|
|
|
|
VOID DestroyKL(
|
|
PKL pkl)
|
|
{
|
|
/*
|
|
* 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
|
|
*/
|
|
HMMarkObjectDestroy(pkl->spkf);
|
|
Unlock(&pkl->spkf);
|
|
|
|
/*
|
|
* Free the pkl itself.
|
|
*/
|
|
HMFreeObject(pkl);
|
|
}
|