1 /* 2 * PROJECT: ReactOS Win32k subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: win32ss/user/ntuser/kbdlayout.c 5 * PURPOSE: Keyboard layout management 6 * COPYRIGHT: Copyright 2007 Saveliy Tretiakov 7 * Copyright 2008 Colin Finck 8 * Copyright 2011 Rafal Harabien 9 * Copyright 2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 10 */ 11 12 #include <win32k.h> 13 #include <ddk/immdev.h> 14 15 // Was included only because of CP_ACP and required the 16 // definition of SYSTEMTIME in ndk\rtltypes.h 17 //#include <winnls.h> 18 #define CP_ACP 0 19 20 DBG_DEFAULT_CHANNEL(UserKbdLayout); 21 22 PKL gspklBaseLayout = NULL; /* FIXME: Please move this to pWinSta->spklList */ 23 PKBDFILE gpkfList = NULL; 24 DWORD gSystemFS = 0; 25 UINT gSystemCPCharSet = 0; 26 DWORD gLCIDSentToShell = 0; 27 28 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID); 29 30 /* PRIVATE FUNCTIONS ******************************************************/ 31 32 /* 33 * Retrieves a PKL by an input locale identifier (HKL). 34 * @implemented 35 * Win: HKLtoPKL 36 */ 37 PKL FASTCALL IntHKLtoPKL(_Inout_ PTHREADINFO pti, _In_ HKL hKL) 38 { 39 PKL pFirstKL, pKL; 40 41 pFirstKL = pti->KeyboardLayout; 42 if (!pFirstKL) 43 return NULL; 44 45 pKL = pFirstKL; 46 47 /* hKL can have special value HKL_NEXT or HKL_PREV */ 48 if (hKL == (HKL)(ULONG_PTR)HKL_NEXT) /* Looking forward */ 49 { 50 do 51 { 52 pKL = pKL->pklNext; 53 if (!(pKL->dwKL_Flags & KLF_UNLOAD)) 54 return pKL; 55 } while (pKL != pFirstKL); 56 } 57 else if (hKL == (HKL)(ULONG_PTR)HKL_PREV) /* Looking backward */ 58 { 59 do 60 { 61 pKL = pKL->pklPrev; 62 if (!(pKL->dwKL_Flags & KLF_UNLOAD)) 63 return pKL; 64 } while (pKL != pFirstKL); 65 } 66 else if (HIWORD(hKL)) /* hKL is a full input locale identifier */ 67 { 68 /* No KLF_UNLOAD check */ 69 do 70 { 71 if (pKL->hkl == hKL) 72 return pKL; 73 74 pKL = pKL->pklNext; 75 } while (pKL != pFirstKL); 76 } 77 else /* Language only specified */ 78 { 79 /* No KLF_UNLOAD check */ 80 do 81 { 82 if (LOWORD(pKL->hkl) == LOWORD(hKL)) /* Low word is language ID */ 83 return pKL; 84 85 pKL = pKL->pklNext; 86 } while (pKL != pFirstKL); 87 } 88 89 return NULL; 90 } 91 92 /* 93 * A helper function for NtUserGetKeyboardLayoutList. 94 * @implemented 95 * Win: _GetKeyboardLayoutList 96 */ 97 static UINT APIENTRY 98 IntGetKeyboardLayoutList( 99 _Inout_ PWINSTATION_OBJECT pWinSta, 100 _In_ ULONG nBuff, 101 _Out_ HKL *pHklBuff) 102 { 103 UINT ret = 0; 104 PKL pKL, pFirstKL; 105 106 pFirstKL = gspklBaseLayout; /* FIXME: Use pWinSta->spklList instead */ 107 if (!pWinSta || !pFirstKL) 108 return 0; 109 110 pKL = pFirstKL; 111 112 if (nBuff == 0) 113 { 114 /* Count the effective PKLs */ 115 do 116 { 117 if (!(pKL->dwKL_Flags & KLF_UNLOAD)) 118 ++ret; 119 pKL = pKL->pklNext; 120 } while (pKL != pFirstKL); 121 } 122 else 123 { 124 /* Copy the effective HKLs to pHklBuff */ 125 do 126 { 127 if (!(pKL->dwKL_Flags & KLF_UNLOAD)) 128 { 129 *pHklBuff = pKL->hkl; 130 ++pHklBuff; 131 ++ret; 132 --nBuff; 133 134 if (nBuff == 0) 135 break; 136 } 137 pKL = pKL->pklNext; 138 } while (pKL != pFirstKL); 139 } 140 141 return ret; 142 } 143 144 #if 0 && DBG 145 146 static VOID 147 DumpKbdLayout( 148 IN PKBDTABLES pKbdTbl) 149 { 150 PVK_TO_BIT pVkToBit; 151 PVK_TO_WCHAR_TABLE pVkToWchTbl; 152 PVSC_VK pVscVk; 153 ULONG i; 154 155 DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", 156 pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK); 157 DbgPrint("wMaxModBits %x\n", 158 pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits 159 : 0); 160 161 if (pKbdTbl->pCharModifiers) 162 { 163 pVkToBit = pKbdTbl->pCharModifiers->pVkToBit; 164 if (pVkToBit) 165 { 166 for (; pVkToBit->Vk; ++pVkToBit) 167 { 168 DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits); 169 } 170 } 171 172 for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i) 173 { 174 DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]); 175 } 176 } 177 178 pVkToWchTbl = pKbdTbl->pVkToWcharTable; 179 if (pVkToWchTbl) 180 { 181 for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl) 182 { 183 PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars; 184 185 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", 186 pVkToWchTbl->nModifications, pVkToWchTbl->cbSize); 187 if (pVkToWch) 188 { 189 while (pVkToWch->VirtualKey) 190 { 191 DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ", 192 pVkToWch->VirtualKey, pVkToWch->Attributes); 193 for (i = 0; i < pVkToWchTbl->nModifications; ++i) 194 { 195 DbgPrint("%x ", pVkToWch->wch[i]); 196 } 197 DbgPrint("}\n"); 198 pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize); 199 } 200 } 201 } 202 } 203 204 // TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead 205 206 DbgPrint("pusVSCtoVK: { "); 207 if (pKbdTbl->pusVSCtoVK) 208 { 209 for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i) 210 { 211 DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]); 212 } 213 } 214 DbgPrint("}\n"); 215 216 DbgPrint("pVSCtoVK_E0: { "); 217 pVscVk = pKbdTbl->pVSCtoVK_E0; 218 if (pVscVk) 219 { 220 for (; pVscVk->Vsc; ++pVscVk) 221 { 222 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 223 } 224 } 225 DbgPrint("}\n"); 226 227 DbgPrint("pVSCtoVK_E1: { "); 228 pVscVk = pKbdTbl->pVSCtoVK_E1; 229 if (pVscVk) 230 { 231 for (; pVscVk->Vsc; ++pVscVk) 232 { 233 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 234 } 235 } 236 DbgPrint("}\n"); 237 238 // TODO: Ligatures 239 } 240 241 #endif // DBG 242 243 244 /* 245 * UserLoadKbdDll 246 * 247 * Loads keyboard layout DLL and gets address to KbdTables 248 */ 249 static BOOL 250 UserLoadKbdDll(WCHAR *pwszLayoutPath, 251 HANDLE *phModule, 252 PKBDTABLES *pKbdTables) 253 { 254 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor; 255 256 /* Load keyboard layout DLL */ 257 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath); 258 *phModule = EngLoadImage(pwszLayoutPath); 259 if (!(*phModule)) 260 { 261 ERR("Failed to load dll %ws\n", pwszLayoutPath); 262 return FALSE; 263 } 264 265 /* Find KbdLayerDescriptor function and get layout tables */ 266 TRACE("Loaded %ws\n", pwszLayoutPath); 267 pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor"); 268 269 /* FIXME: Windows reads file instead of executing! 270 It's not safe to kbdlayout DLL in kernel mode! */ 271 272 if (pfnKbdLayerDescriptor) 273 *pKbdTables = pfnKbdLayerDescriptor(); 274 else 275 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath); 276 277 if (!pfnKbdLayerDescriptor || !*pKbdTables) 278 { 279 ERR("Failed to load the keyboard layout.\n"); 280 EngUnloadImage(*phModule); 281 return FALSE; 282 } 283 284 #if 0 && DBG 285 /* Dump keyboard layout */ 286 DumpKbdLayout(*pKbdTables); 287 #endif 288 289 return TRUE; 290 } 291 292 /* 293 * UserLoadKbdFile 294 * 295 * Loads keyboard layout DLL and creates KBDFILE object 296 */ 297 static PKBDFILE 298 UserLoadKbdFile(PUNICODE_STRING pwszKLID) 299 { 300 PKBDFILE pkf, pRet = NULL; 301 NTSTATUS Status; 302 ULONG cbSize; 303 HKEY hKey = NULL; 304 WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\"; 305 WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\" 306 L"Control\\Keyboard Layouts\\"; 307 308 /* Create keyboard layout file object */ 309 pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); 310 if (!pkf) 311 { 312 ERR("Failed to create object!\n"); 313 return NULL; 314 } 315 316 /* Set keyboard layout name */ 317 swprintf(pkf->awchKF, L"%wZ", pwszKLID); 318 319 /* Open layout registry key */ 320 RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF); 321 Status = RegOpenKey(wszLayoutRegKey, &hKey); 322 if (!NT_SUCCESS(Status)) 323 { 324 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status); 325 goto cleanup; 326 } 327 328 /* Read filename of layout DLL */ 329 cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR)); 330 Status = RegQueryValue(hKey, 331 L"Layout File", 332 REG_SZ, 333 wszLayoutPath + wcslen(wszLayoutPath), 334 &cbSize); 335 336 if (!NT_SUCCESS(Status)) 337 { 338 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status); 339 goto cleanup; 340 } 341 342 /* Load keyboard file now */ 343 if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl)) 344 { 345 ERR("Failed to load %ws dll!\n", wszLayoutPath); 346 goto cleanup; 347 } 348 349 /* Update next field */ 350 pkf->pkfNext = gpkfList; 351 gpkfList = pkf; 352 353 /* Return keyboard file */ 354 pRet = pkf; 355 356 cleanup: 357 if (hKey) 358 ZwClose(hKey); 359 if (pkf) 360 UserDereferenceObject(pkf); // we dont need ptr anymore 361 if (!pRet) 362 { 363 /* We have failed - destroy created object */ 364 if (pkf) 365 UserDeleteObject(pkf->head.h, TYPE_KBDFILE); 366 } 367 368 return pRet; 369 } 370 371 /* 372 * UserLoadKbdLayout 373 * 374 * Loads keyboard layout and creates KL object 375 */ 376 static PKL 377 UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) 378 { 379 LCID lCid; 380 CHARSETINFO cs; 381 PKL pKl; 382 383 /* Create keyboard layout object */ 384 pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); 385 if (!pKl) 386 { 387 ERR("Failed to create object!\n"); 388 return NULL; 389 } 390 391 pKl->hkl = hKL; 392 pKl->spkf = UserLoadKbdFile(pustrKLID); 393 394 /* Dereference keyboard layout */ 395 UserDereferenceObject(pKl); 396 397 /* If we failed, remove KL object */ 398 if (!pKl->spkf) 399 { 400 ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID); 401 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 402 return NULL; 403 } 404 405 // Up to Language Identifiers.. 406 if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid))) 407 { 408 ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID); 409 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 410 return NULL; 411 } 412 413 TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid); 414 if (co_IntGetCharsetInfo(lCid, &cs)) 415 { 416 pKl->iBaseCharset = cs.ciCharset; 417 pKl->dwFontSigs = cs.fs.fsCsb[0]; 418 pKl->CodePage = (USHORT)cs.ciACP; 419 TRACE("Charset %u Font Sig %lu CodePage %u\n", 420 pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage); 421 } 422 else 423 { 424 pKl->iBaseCharset = ANSI_CHARSET; 425 pKl->dwFontSigs = FS_LATIN1; 426 pKl->CodePage = CP_ACP; 427 } 428 429 // Set initial system character set and font signature. 430 if (gSystemFS == 0) 431 { 432 gSystemCPCharSet = pKl->iBaseCharset; 433 gSystemFS = pKl->dwFontSigs; 434 } 435 436 return pKl; 437 } 438 439 /* 440 * UnloadKbdFile 441 * 442 * Destroys specified Keyboard File object 443 */ 444 static 445 VOID 446 UnloadKbdFile(_In_ PKBDFILE pkf) 447 { 448 PKBDFILE *ppkfLink = &gpkfList; 449 NT_ASSERT(pkf != NULL); 450 451 /* Find previous object */ 452 while (*ppkfLink) 453 { 454 if (*ppkfLink == pkf) 455 break; 456 457 ppkfLink = &(*ppkfLink)->pkfNext; 458 } 459 460 if (*ppkfLink == pkf) 461 *ppkfLink = pkf->pkfNext; 462 463 EngUnloadImage(pkf->hBase); 464 UserDeleteObject(pkf->head.h, TYPE_KBDFILE); 465 } 466 467 /* 468 * UserUnloadKbl 469 * 470 * Unloads specified Keyboard Layout if possible 471 */ 472 BOOL 473 UserUnloadKbl(PKL pKl) 474 { 475 /* According to msdn, UnloadKeyboardLayout can fail 476 if the keyboard layout identifier was preloaded. */ 477 if (pKl == gspklBaseLayout) 478 { 479 if (pKl->pklNext == pKl->pklPrev) 480 { 481 /* There is only one layout */ 482 return FALSE; 483 } 484 485 /* Set next layout as default */ 486 gspklBaseLayout = pKl->pklNext; 487 } 488 489 if (pKl->head.cLockObj > 1) 490 { 491 /* Layout is used by other threads */ 492 pKl->dwKL_Flags |= KLF_UNLOAD; 493 return FALSE; 494 } 495 496 /* Unload the layout */ 497 pKl->pklPrev->pklNext = pKl->pklNext; 498 pKl->pklNext->pklPrev = pKl->pklPrev; 499 UnloadKbdFile(pKl->spkf); 500 if (pKl->piiex) 501 { 502 ExFreePoolWithTag(pKl->piiex, USERTAG_IME); 503 } 504 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 505 return TRUE; 506 } 507 508 /* 509 * W32kGetDefaultKeyLayout 510 * 511 * Returns default layout for new threads 512 */ 513 PKL 514 W32kGetDefaultKeyLayout(VOID) 515 { 516 PKL pKl = gspklBaseLayout; 517 518 if (!pKl) 519 return NULL; 520 521 /* Return not unloaded layout */ 522 do 523 { 524 if (!(pKl->dwKL_Flags & KLF_UNLOAD)) 525 return pKl; 526 527 pKl = pKl->pklPrev; /* Confirmed on Win2k */ 528 } while(pKl != gspklBaseLayout); 529 530 /* We have not found proper KL */ 531 return NULL; 532 } 533 534 /* 535 * UserHklToKbl 536 * 537 * Gets KL object from hkl value 538 */ 539 PKL 540 NTAPI 541 UserHklToKbl(HKL hKl) 542 { 543 PKL pKl = gspklBaseLayout; 544 545 if (!gspklBaseLayout) 546 return NULL; 547 548 do 549 { 550 if (pKl->hkl == hKl) 551 return pKl; 552 553 pKl = pKl->pklNext; 554 } while (pKl != gspklBaseLayout); 555 556 return NULL; 557 } 558 559 // Win: ReorderKeyboardLayouts 560 VOID FASTCALL 561 IntReorderKeyboardLayouts( 562 _Inout_ PWINSTATION_OBJECT pWinSta, 563 _Inout_ PKL pNewKL) 564 { 565 PKL pOldKL = gspklBaseLayout; 566 567 if ((pWinSta->Flags & WSS_NOIO) || pNewKL == pOldKL) 568 return; 569 570 pNewKL->pklPrev->pklNext = pNewKL->pklNext; 571 pNewKL->pklNext->pklPrev = pNewKL->pklPrev; 572 pNewKL->pklNext = pOldKL; 573 pNewKL->pklPrev = pOldKL->pklPrev; 574 pOldKL->pklPrev->pklNext = pNewKL; 575 pOldKL->pklPrev = pNewKL; 576 gspklBaseLayout = pNewKL; /* Should we use UserAssignmentLock? */ 577 } 578 579 /* 580 * UserSetDefaultInputLang 581 * 582 * Sets default kyboard layout for system. Called from UserSystemParametersInfo. 583 */ 584 BOOL 585 NTAPI 586 UserSetDefaultInputLang(HKL hKl) 587 { 588 PKL pKl; 589 590 pKl = UserHklToKbl(hKl); 591 if (!pKl) 592 return FALSE; 593 594 IntReorderKeyboardLayouts(IntGetProcessWindowStation(NULL), pKl); 595 return TRUE; 596 } 597 598 /* 599 * co_UserActivateKbl 600 * 601 * Activates given layout in specified thread 602 */ 603 static PKL 604 co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags) 605 { 606 PKL pklPrev; 607 PWND pWnd; 608 609 pklPrev = pti->KeyboardLayout; 610 611 UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKl); 612 pti->pClientInfo->hKL = pKl->hkl; 613 614 if (Flags & KLF_SETFORPROCESS) 615 { 616 FIXME("KLF_SETFORPROCESS\n"); 617 } 618 619 if (!(pWnd = pti->MessageQueue->spwndFocus)) 620 { 621 pWnd = pti->MessageQueue->spwndActive; 622 } 623 624 // Send WM_INPUTLANGCHANGE to thread's focus window 625 co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0, 626 WM_INPUTLANGCHANGE, 627 (WPARAM)pKl->iBaseCharset, // FIXME: How to set it? 628 (LPARAM)pKl->hkl); // hkl 629 630 return pklPrev; 631 } 632 633 // Win: xxxImmActivateLayout 634 VOID APIENTRY 635 IntImmActivateLayout( 636 _Inout_ PTHREADINFO pti, 637 _Inout_ PKL pKL) 638 { 639 PWND pImeWnd; 640 HWND hImeWnd; 641 USER_REFERENCE_ENTRY Ref; 642 643 if (pti->KeyboardLayout == pKL) 644 return; 645 646 pImeWnd = pti->spwndDefaultIme; 647 if (pImeWnd) 648 { 649 UserRefObjectCo(pImeWnd, &Ref); 650 hImeWnd = UserHMGetHandle(pImeWnd); 651 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl); 652 UserDerefObjectCo(pImeWnd); 653 } 654 655 UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL); 656 pti->pClientInfo->hKL = pKL->hkl; 657 } 658 659 /* Win: xxxInternalActivateKeyboardLayout */ 660 HKL APIENTRY 661 co_UserActivateKeyboardLayout( 662 _Inout_ PKL pKL, 663 _In_ ULONG uFlags, 664 _Inout_ PWND pWnd) 665 { 666 HKL hOldKL = NULL; 667 PKL pOldKL = NULL; 668 PTHREADINFO pti = GetW32ThreadInfo(); 669 PWND pTargetWnd, pImeWnd; 670 HWND hTargetWnd, hImeWnd; 671 USER_REFERENCE_ENTRY Ref1, Ref2; 672 PCLIENTINFO ClientInfo = pti->pClientInfo; 673 674 if (pti->KeyboardLayout) 675 { 676 pOldKL = pti->KeyboardLayout; 677 if (pOldKL) 678 hOldKL = pOldKL->hkl; 679 } 680 681 if (uFlags & KLF_RESET) 682 { 683 FIXME("KLF_RESET\n"); 684 } 685 686 if (!(uFlags & KLF_SETFORPROCESS) && pKL == pti->KeyboardLayout) 687 return hOldKL; 688 689 pKL->wchDiacritic = 0; 690 691 if (pOldKL) 692 UserRefObjectCo(pOldKL, &Ref1); 693 694 if (pti->TIF_flags & TIF_CSRSSTHREAD) 695 { 696 UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); 697 ClientInfo->CodePage = pKL->CodePage; 698 ClientInfo->hKL = pKL->hkl; 699 } 700 else if (uFlags & KLF_SETFORPROCESS) 701 { 702 FIXME("KLF_SETFORPROCESS\n"); 703 } 704 else 705 { 706 if (IS_IMM_MODE()) 707 IntImmActivateLayout(pti, pKL); 708 else 709 UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); 710 711 ClientInfo->CodePage = pKL->CodePage; 712 ClientInfo->hKL = pKL->hkl; 713 } 714 715 if (gptiForeground && (gptiForeground->ppi == pti->ppi)) 716 { 717 /* Send shell message */ 718 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl); 719 } 720 721 if (pti->MessageQueue) 722 { 723 /* Determine the target window */ 724 pTargetWnd = pti->MessageQueue->spwndFocus; 725 if (!pTargetWnd) 726 { 727 pTargetWnd = pti->MessageQueue->spwndActive; 728 if (!pTargetWnd) 729 pTargetWnd = pWnd; 730 } 731 732 /* Send WM_INPUTLANGCHANGE message */ 733 if (pTargetWnd) 734 { 735 UserRefObjectCo(pTargetWnd, &Ref2); 736 hTargetWnd = UserHMGetHandle(pTargetWnd); 737 co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl); 738 UserDerefObjectCo(pTargetWnd); 739 } 740 } 741 742 /* Send WM_IME_SYSTEM:IMS_SENDNOTIFICATION message if necessary */ 743 if (pti && !(pti->TIF_flags & TIF_CSRSSTHREAD)) 744 { 745 if (IS_IME_HKL(pKL->hkl) || IS_CICERO_MODE()) 746 { 747 pImeWnd = pti->spwndDefaultIme; 748 if (pImeWnd) 749 { 750 UserRefObjectCo(pImeWnd, &Ref2); 751 BOOL bProcess = !!(pti->TIF_flags & KLF_SETFORPROCESS); 752 hImeWnd = UserHMGetHandle(pImeWnd); 753 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bProcess); 754 UserDerefObjectCo(pImeWnd); 755 } 756 } 757 } 758 759 if (pOldKL) 760 UserDerefObjectCo(pOldKL); 761 return hOldKL; 762 } 763 764 /* Win: xxxActivateKeyboardLayout */ 765 HKL APIENTRY 766 co_IntActivateKeyboardLayout( 767 _Inout_ PWINSTATION_OBJECT pWinSta, 768 _In_ HKL hKL, 769 _In_ ULONG uFlags, 770 _Inout_ PWND pWnd) 771 { 772 PKL pKL; 773 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 774 775 pKL = IntHKLtoPKL(pti, hKL); 776 if (!pKL) 777 { 778 ERR("Invalid HKL %p!\n", hKL); 779 return NULL; 780 } 781 782 if (uFlags & KLF_REORDER) 783 IntReorderKeyboardLayouts(pWinSta, pKL); 784 785 return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd); 786 } 787 788 // Win: xxxInternalUnloadKeyboardLayout 789 static BOOL APIENTRY 790 co_IntUnloadKeyboardLayoutEx( 791 _Inout_ PWINSTATION_OBJECT pWinSta, 792 _Inout_ PKL pKL, 793 _In_ DWORD dwFlags) 794 { 795 PKL pNextKL; 796 USER_REFERENCE_ENTRY Ref1, Ref2; 797 PTHREADINFO pti = gptiCurrent; 798 799 if (pKL == gspklBaseLayout && !(dwFlags & 0x80000000)) 800 return FALSE; 801 802 UserRefObjectCo(pKL, &Ref1); /* Add reference */ 803 804 /* Regard as unloaded */ 805 UserMarkObjectDestroy(pKL); 806 pKL->dwKL_Flags |= KLF_UNLOAD; 807 808 if (!(dwFlags & 0x80000000) && pti->KeyboardLayout == pKL) 809 { 810 pNextKL = IntHKLtoPKL(pti, (HKL)(ULONG_PTR)HKL_NEXT); 811 if (pNextKL) 812 { 813 UserRefObjectCo(pNextKL, &Ref2); /* Add reference */ 814 co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL); 815 UserDerefObjectCo(pNextKL); /* Release reference */ 816 } 817 } 818 819 if (gspklBaseLayout == pKL && pKL != pKL->pklNext) 820 { 821 /* Set next layout as default (FIXME: Use UserAssignmentLock?) */ 822 gspklBaseLayout = pKL->pklNext; 823 } 824 825 UserDerefObjectCo(pKL); /* Release reference */ 826 827 if (pti->pDeskInfo->fsHooks) 828 { 829 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0); 830 gLCIDSentToShell = 0; 831 } 832 833 return TRUE; 834 } 835 836 // Win: xxxUnloadKeyboardLayout 837 static BOOL APIENTRY 838 IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL) 839 { 840 PKL pKL = IntHKLtoPKL(gptiCurrent, hKL); 841 if (!pKL) 842 { 843 ERR("Invalid HKL %p!\n", hKL); 844 return FALSE; 845 } 846 return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0); 847 } 848 849 /* EXPORTS *******************************************************************/ 850 851 /* 852 * UserGetKeyboardLayout 853 * 854 * Returns hkl of given thread keyboard layout 855 */ 856 HKL FASTCALL 857 UserGetKeyboardLayout( 858 DWORD dwThreadId) 859 { 860 PTHREADINFO pti; 861 PLIST_ENTRY ListEntry; 862 PKL pKl; 863 864 pti = PsGetCurrentThreadWin32Thread(); 865 866 if (!dwThreadId) 867 { 868 pKl = pti->KeyboardLayout; 869 return pKl ? pKl->hkl : NULL; 870 } 871 872 ListEntry = pti->rpdesk->PtiList.Flink; 873 874 // 875 // Search the Desktop Thread list for related Desktop active Threads. 876 // 877 while(ListEntry != &pti->rpdesk->PtiList) 878 { 879 pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); 880 881 if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId)) 882 { 883 pKl = pti->KeyboardLayout; 884 return pKl ? pKl->hkl : NULL; 885 } 886 887 ListEntry = ListEntry->Flink; 888 } 889 890 return NULL; 891 } 892 893 /* 894 * NtUserGetKeyboardLayoutList 895 * 896 * Returns list of loaded keyboard layouts in system 897 */ 898 UINT 899 APIENTRY 900 NtUserGetKeyboardLayoutList( 901 ULONG nBuff, 902 HKL *pHklBuff) 903 { 904 UINT ret = 0; 905 PWINSTATION_OBJECT pWinSta; 906 907 if (!pHklBuff) 908 nBuff = 0; 909 910 UserEnterShared(); 911 912 if (nBuff > MAXULONG / sizeof(HKL)) 913 { 914 SetLastNtError(ERROR_INVALID_PARAMETER); 915 goto Quit; 916 } 917 918 _SEH2_TRY 919 { 920 ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1); 921 } 922 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 923 { 924 SetLastNtError(_SEH2_GetExceptionCode()); 925 goto Quit; 926 } 927 _SEH2_END; 928 929 pWinSta = IntGetProcessWindowStation(NULL); 930 931 _SEH2_TRY 932 { 933 ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff); 934 } 935 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 936 { 937 SetLastNtError(_SEH2_GetExceptionCode()); 938 goto Quit; 939 } 940 _SEH2_END; 941 942 Quit: 943 UserLeave(); 944 return ret; 945 } 946 947 /* 948 * NtUserGetKeyboardLayoutName 949 * 950 * Returns KLID of current thread keyboard layout 951 */ 952 BOOL 953 APIENTRY 954 NtUserGetKeyboardLayoutName( 955 _Inout_ PUNICODE_STRING pustrName) 956 { 957 BOOL bRet = FALSE; 958 PKL pKl; 959 PTHREADINFO pti; 960 UNICODE_STRING ustrNameSafe; 961 NTSTATUS Status; 962 963 UserEnterShared(); 964 965 pti = PsGetCurrentThreadWin32Thread(); 966 pKl = pti->KeyboardLayout; 967 968 if (!pKl) 969 goto cleanup; 970 971 _SEH2_TRY 972 { 973 ProbeForWriteUnicodeString(pustrName); 974 ustrNameSafe = *pustrName; 975 976 ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1); 977 978 if (IS_IME_HKL(pKl->hkl)) 979 { 980 Status = RtlIntegerToUnicodeString((ULONG)(ULONG_PTR)pKl->hkl, 16, &ustrNameSafe); 981 } 982 else 983 { 984 if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR)) 985 { 986 EngSetLastError(ERROR_INVALID_PARAMETER); 987 goto cleanup; 988 } 989 990 /* FIXME: Do not use awchKF */ 991 ustrNameSafe.Length = 0; 992 Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF); 993 } 994 995 if (NT_SUCCESS(Status)) 996 { 997 *pustrName = ustrNameSafe; 998 bRet = TRUE; 999 } 1000 } 1001 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1002 { 1003 SetLastNtError(_SEH2_GetExceptionCode()); 1004 } 1005 _SEH2_END; 1006 1007 cleanup: 1008 UserLeave(); 1009 return bRet; 1010 } 1011 1012 /* Win: xxxImmLoadLayout */ 1013 PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL) 1014 { 1015 PIMEINFOEX piiex; 1016 1017 if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE()) 1018 return NULL; 1019 1020 piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME); 1021 if (!piiex) 1022 return NULL; 1023 1024 if (!co_ClientImmLoadLayout(hKL, piiex)) 1025 { 1026 ExFreePoolWithTag(piiex, USERTAG_IME); 1027 return NULL; 1028 } 1029 1030 return piiex; 1031 } 1032 1033 /* 1034 * NtUserLoadKeyboardLayoutEx 1035 * 1036 * Loads keyboard layout with given locale id 1037 * 1038 * NOTE: We adopt a different design from Microsoft's one for security reason. 1039 * We don't use the 1st and 3rd parameters of NtUserLoadKeyboardLayoutEx. 1040 */ 1041 HKL 1042 APIENTRY 1043 NtUserLoadKeyboardLayoutEx( 1044 IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c) 1045 IN DWORD offTable, // Offset to KbdTables 1046 IN PUNICODE_STRING puszKeyboardName, // Not used? 1047 IN HKL hklUnload, 1048 IN PUNICODE_STRING pustrKLID, 1049 IN DWORD hkl, 1050 IN UINT Flags) 1051 { 1052 HKL hklRet = NULL; 1053 PKL pKl = NULL, pklLast; 1054 WCHAR Buffer[KL_NAMELENGTH]; 1055 UNICODE_STRING ustrSafeKLID; 1056 1057 if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG| 1058 KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS| 1059 KLF_RESET|KLF_SHIFTLOCK)) 1060 { 1061 ERR("Invalid flags: %x\n", Flags); 1062 EngSetLastError(ERROR_INVALID_FLAGS); 1063 return NULL; 1064 } 1065 1066 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ 1067 1068 RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer)); 1069 _SEH2_TRY 1070 { 1071 ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1); 1072 ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1); 1073 RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID); 1074 } 1075 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1076 { 1077 SetLastNtError(_SEH2_GetExceptionCode()); 1078 _SEH2_YIELD(return NULL); 1079 } 1080 _SEH2_END; 1081 1082 UserEnterExclusive(); 1083 1084 /* If hklUnload is specified, unload it and load new layput as default */ 1085 if (hklUnload && (hklUnload != UlongToHandle(hkl))) 1086 { 1087 pKl = UserHklToKbl(hklUnload); 1088 if (pKl) 1089 UserUnloadKbl(pKl); 1090 } 1091 1092 /* Let's see if layout was already loaded. */ 1093 pKl = UserHklToKbl(UlongToHandle(hkl)); 1094 if (!pKl) 1095 { 1096 /* It wasn't, so load it. */ 1097 pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl)); 1098 if (!pKl) 1099 goto cleanup; 1100 1101 if (gspklBaseLayout) 1102 { 1103 /* Find last not unloaded layout */ 1104 pklLast = gspklBaseLayout->pklPrev; 1105 while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD) 1106 pklLast = pklLast->pklPrev; 1107 1108 /* Add new layout to the list */ 1109 pKl->pklNext = pklLast->pklNext; 1110 pKl->pklPrev = pklLast; 1111 pKl->pklNext->pklPrev = pKl; 1112 pKl->pklPrev->pklNext = pKl; 1113 } 1114 else 1115 { 1116 /* This is the first layout */ 1117 pKl->pklNext = pKl; 1118 pKl->pklPrev = pKl; 1119 gspklBaseLayout = pKl; 1120 } 1121 1122 pKl->piiex = co_UserImmLoadLayout(UlongToHandle(hkl)); 1123 } 1124 1125 /* If this layout was prepared to unload, undo it */ 1126 pKl->dwKL_Flags &= ~KLF_UNLOAD; 1127 1128 /* Activate this layout in current thread */ 1129 if (Flags & KLF_ACTIVATE) 1130 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags); 1131 1132 /* Send shell message */ 1133 if (!(Flags & KLF_NOTELLSHELL)) 1134 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl); 1135 1136 /* Return hkl on success */ 1137 hklRet = UlongToHandle(hkl); 1138 1139 /* FIXME: KLF_REPLACELANG 1140 KLF_REORDER */ 1141 1142 cleanup: 1143 UserLeave(); 1144 return hklRet; 1145 } 1146 1147 /* 1148 * NtUserActivateKeyboardLayout 1149 * 1150 * Activates specified layout for thread or process 1151 */ 1152 HKL 1153 NTAPI 1154 NtUserActivateKeyboardLayout( 1155 HKL hKL, 1156 ULONG Flags) 1157 { 1158 PWINSTATION_OBJECT pWinSta; 1159 HKL hOldKL; 1160 1161 UserEnterExclusive(); 1162 1163 /* FIXME */ 1164 1165 pWinSta = IntGetProcessWindowStation(NULL); 1166 hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL); 1167 UserLeave(); 1168 1169 return hOldKL; 1170 } 1171 1172 /* 1173 * NtUserUnloadKeyboardLayout 1174 * 1175 * Unloads keyboard layout with specified hkl value 1176 */ 1177 BOOL 1178 APIENTRY 1179 NtUserUnloadKeyboardLayout( 1180 HKL hKl) 1181 { 1182 BOOL ret; 1183 PWINSTATION_OBJECT pWinSta; 1184 1185 UserEnterExclusive(); 1186 1187 pWinSta = IntGetProcessWindowStation(NULL); 1188 ret = IntUnloadKeyboardLayout(pWinSta, hKl); 1189 1190 UserLeave(); 1191 return ret; 1192 } 1193 1194 /* EOF */ 1195