1 /* 2 * PROJECT: input.dll 3 * FILE: dll/cpl/input/input_list.c 4 * PURPOSE: input.dll 5 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) 6 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 */ 8 9 #include "input_list.h" 10 11 typedef struct 12 { 13 PWCHAR FontName; 14 PWCHAR SubFontName; 15 } MUI_SUBFONT; 16 17 #include "../../../base/setup/lib/muifonts.h" 18 19 BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes) 20 { 21 DWORD cbData; 22 HKEY hKey; 23 static const WCHAR pszKey[] = 24 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; 25 26 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) 27 return FALSE; 28 29 /* Overwrite only */ 30 for (; pSubstitutes->FontName; ++pSubstitutes) 31 { 32 cbData = (lstrlenW(pSubstitutes->SubFontName) + 1) * sizeof(WCHAR); 33 RegSetValueExW(hKey, pSubstitutes->FontName, 0, 34 REG_SZ, (LPBYTE)pSubstitutes->SubFontName, cbData); 35 } 36 37 RegCloseKey(hKey); 38 return TRUE; 39 } 40 41 VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName) 42 { 43 WCHAR szSysDir[MAX_PATH]; 44 GetSystemDirectoryW(szSysDir, ARRAYSIZE(szSysDir)); 45 StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName); 46 } 47 48 BOOL 49 InputList_SetFontSubstitutes(LCID dwLocaleId) 50 { 51 MUI_SUBFONT *pSubstitutes; 52 WORD wLangID, wPrimaryLangID, wSubLangID; 53 54 wLangID = LANGIDFROMLCID(dwLocaleId); 55 wPrimaryLangID = PRIMARYLANGID(wLangID); 56 wSubLangID = SUBLANGID(wLangID); 57 58 /* FIXME: Add more if necessary */ 59 switch (wPrimaryLangID) 60 { 61 default: 62 pSubstitutes = LatinFonts; 63 break; 64 case LANG_AZERI: 65 case LANG_BELARUSIAN: 66 case LANG_BULGARIAN: 67 case LANG_KAZAK: 68 case LANG_RUSSIAN: 69 case LANG_SERBIAN: 70 case LANG_TATAR: 71 case LANG_UKRAINIAN: 72 case LANG_UZBEK: 73 pSubstitutes = CyrillicFonts; 74 break; 75 case LANG_GREEK: 76 pSubstitutes = GreekFonts; 77 break; 78 case LANG_HEBREW: 79 pSubstitutes = HebrewFonts; 80 break; 81 case LANG_CHINESE: 82 switch (wSubLangID) 83 { 84 case SUBLANG_CHINESE_SIMPLIFIED: 85 case SUBLANG_CHINESE_SINGAPORE: 86 pSubstitutes = ChineseSimplifiedFonts; 87 break; 88 case SUBLANG_CHINESE_TRADITIONAL: 89 case SUBLANG_CHINESE_HONGKONG: 90 case SUBLANG_CHINESE_MACAU: 91 pSubstitutes = ChineseTraditionalFonts; 92 break; 93 default: 94 pSubstitutes = NULL; 95 DebugBreak(); 96 break; 97 } 98 break; 99 case LANG_JAPANESE: 100 pSubstitutes = JapaneseFonts; 101 break; 102 case LANG_KOREAN: 103 pSubstitutes = KoreanFonts; 104 break; 105 case LANG_ARABIC: 106 case LANG_ARMENIAN: 107 case LANG_BENGALI: 108 case LANG_FARSI: 109 case LANG_GEORGIAN: 110 case LANG_GUJARATI: 111 case LANG_HINDI: 112 case LANG_KONKANI: 113 case LANG_MARATHI: 114 case LANG_PUNJABI: 115 case LANG_SANSKRIT: 116 case LANG_TAMIL: 117 case LANG_TELUGU: 118 case LANG_THAI: 119 case LANG_URDU: 120 case LANG_VIETNAMESE: 121 pSubstitutes = UnicodeFonts; 122 break; 123 } 124 125 if (pSubstitutes) 126 { 127 UpdateRegistryForFontSubstitutes(pSubstitutes); 128 return TRUE; 129 } 130 return FALSE; 131 } 132 133 static INPUT_LIST_NODE *_InputList = NULL; 134 135 136 static INPUT_LIST_NODE* 137 InputList_AppendNode(VOID) 138 { 139 INPUT_LIST_NODE *pCurrent; 140 INPUT_LIST_NODE *pNew; 141 142 pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE)); 143 if (pNew == NULL) 144 return NULL; 145 146 ZeroMemory(pNew, sizeof(INPUT_LIST_NODE)); 147 148 if (_InputList == NULL) /* Empty? */ 149 { 150 _InputList = pNew; 151 return pNew; 152 } 153 154 /* Find last node */ 155 for (pCurrent = _InputList; pCurrent->pNext; pCurrent = pCurrent->pNext) 156 { 157 ; 158 } 159 160 /* Add to the end */ 161 pCurrent->pNext = pNew; 162 pNew->pPrev = pCurrent; 163 164 return pNew; 165 } 166 167 168 static VOID 169 InputList_RemoveNode(INPUT_LIST_NODE *pNode) 170 { 171 INPUT_LIST_NODE *pCurrent = pNode; 172 173 if (_InputList == NULL) 174 return; 175 176 if (pCurrent != NULL) 177 { 178 INPUT_LIST_NODE *pNext = pCurrent->pNext; 179 INPUT_LIST_NODE *pPrev = pCurrent->pPrev; 180 181 free(pCurrent->pszIndicator); 182 free(pCurrent); 183 184 if (pNext != NULL) 185 pNext->pPrev = pPrev; 186 187 if (pPrev != NULL) 188 pPrev->pNext = pNext; 189 else 190 _InputList = pNext; 191 } 192 } 193 194 195 VOID 196 InputList_Destroy(VOID) 197 { 198 INPUT_LIST_NODE *pCurrent; 199 INPUT_LIST_NODE *pNext; 200 201 if (_InputList == NULL) 202 return; 203 204 for (pCurrent = _InputList; pCurrent; pCurrent = pNext) 205 { 206 pNext = pCurrent->pNext; 207 208 free(pCurrent->pszIndicator); 209 free(pCurrent); 210 } 211 212 _InputList = NULL; 213 } 214 215 216 static BOOL 217 InputList_PrepareUserRegistry(PHKEY phPreloadKey, PHKEY phSubstKey) 218 { 219 BOOL bResult = FALSE; 220 HKEY hKey; 221 222 *phPreloadKey = *phSubstKey = NULL; 223 224 if (RegOpenKeyExW(HKEY_CURRENT_USER, 225 L"Keyboard Layout", 226 0, 227 KEY_ALL_ACCESS, 228 &hKey) == ERROR_SUCCESS) 229 { 230 RegDeleteKeyW(hKey, L"Preload"); 231 RegDeleteKeyW(hKey, L"Substitutes"); 232 233 RegCloseKey(hKey); 234 } 235 236 if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) == ERROR_SUCCESS && 237 RegCreateKeyW(hKey, L"Preload", phPreloadKey) == ERROR_SUCCESS && 238 RegCreateKeyW(hKey, L"Substitutes", phSubstKey) == ERROR_SUCCESS) 239 { 240 bResult = TRUE; 241 } 242 243 if (hKey) 244 RegCloseKey(hKey); 245 246 return bResult; 247 } 248 249 static BOOL 250 InputList_FindPreloadKLID(HKEY hPreloadKey, DWORD dwKLID) 251 { 252 DWORD dwNumber, dwType, cbValue; 253 WCHAR szNumber[16], szValue[KL_NAMELENGTH], szKLID[KL_NAMELENGTH]; 254 255 StringCchPrintfW(szKLID, ARRAYSIZE(szKLID), L"%08x", dwKLID); 256 257 for (dwNumber = 1; dwNumber <= 1000; ++dwNumber) 258 { 259 StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%u", dwNumber); 260 261 cbValue = ARRAYSIZE(szValue) * sizeof(WCHAR); 262 if (RegQueryValueExW(hPreloadKey, szNumber, NULL, &dwType, 263 (LPBYTE)szValue, &cbValue) != ERROR_SUCCESS) 264 { 265 break; 266 } 267 268 if (dwType != REG_SZ) 269 continue; 270 271 szValue[ARRAYSIZE(szValue) - 1] = 0; 272 if (_wcsicmp(szKLID, szValue) == 0) 273 return TRUE; 274 } 275 276 return FALSE; 277 } 278 279 static BOOL 280 InputList_WriteSubst(HKEY hSubstKey, DWORD dwPhysicalKLID, DWORD dwLogicalKLID) 281 { 282 DWORD cbValue; 283 WCHAR szLogicalKLID[KL_NAMELENGTH], szPhysicalKLID[KL_NAMELENGTH]; 284 285 StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID); 286 StringCchPrintfW(szPhysicalKLID, ARRAYSIZE(szPhysicalKLID), L"%08x", dwPhysicalKLID); 287 288 cbValue = (wcslen(szPhysicalKLID) + 1) * sizeof(WCHAR); 289 return RegSetValueExW(hSubstKey, szLogicalKLID, 0, REG_SZ, (LPBYTE)szPhysicalKLID, 290 cbValue) == ERROR_SUCCESS; 291 } 292 293 static DWORD 294 InputList_DoSubst(HKEY hPreloadKey, HKEY hSubstKey, 295 DWORD dwPhysicalKLID, DWORD dwLogicalKLID) 296 { 297 DWORD iTrial; 298 BOOL bSubstNeeded = (dwPhysicalKLID != dwLogicalKLID) || (HIWORD(dwPhysicalKLID) != 0); 299 300 for (iTrial = 1; iTrial <= 1000; ++iTrial) 301 { 302 if (!InputList_FindPreloadKLID(hPreloadKey, dwLogicalKLID)) /* Not found? */ 303 { 304 if (bSubstNeeded) 305 { 306 /* Write now */ 307 InputList_WriteSubst(hSubstKey, dwPhysicalKLID, dwLogicalKLID); 308 } 309 return dwLogicalKLID; 310 } 311 312 bSubstNeeded = TRUE; 313 314 /* Calculate the next logical KLID */ 315 if (!IS_SUBST_KLID(dwLogicalKLID)) 316 { 317 dwLogicalKLID |= SUBST_MASK; 318 } 319 else 320 { 321 WORD wLow = LOWORD(dwLogicalKLID); 322 WORD wHigh = HIWORD(dwLogicalKLID); 323 dwLogicalKLID = MAKELONG(wLow, wHigh + 1); 324 } 325 } 326 327 return 0; 328 } 329 330 static VOID 331 InputList_AddInputMethodToUserRegistry( 332 HKEY hPreloadKey, 333 HKEY hSubstKey, 334 DWORD dwNumber, 335 INPUT_LIST_NODE *pNode) 336 { 337 WCHAR szNumber[32], szLogicalKLID[KL_NAMELENGTH]; 338 DWORD dwPhysicalKLID, dwLogicalKLID, cbValue; 339 HKL hKL = pNode->hkl; 340 341 if (IS_IME_HKL(hKL)) /* IME? */ 342 { 343 /* Do not substitute the IME KLID */ 344 dwLogicalKLID = dwPhysicalKLID = HandleToUlong(hKL); 345 } 346 else 347 { 348 /* Substitute the KLID if necessary */ 349 dwPhysicalKLID = pNode->pLayout->dwKLID; 350 dwLogicalKLID = pNode->pLocale->dwId; 351 dwLogicalKLID = InputList_DoSubst(hPreloadKey, hSubstKey, dwPhysicalKLID, dwLogicalKLID); 352 } 353 354 /* Write the Preload value (number |--> logical KLID) */ 355 StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%lu", dwNumber); 356 StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID); 357 cbValue = (wcslen(szLogicalKLID) + 1) * sizeof(WCHAR); 358 RegSetValueExW(hPreloadKey, 359 szNumber, 360 0, 361 REG_SZ, 362 (LPBYTE)szLogicalKLID, 363 cbValue); 364 365 if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) || 366 (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) 367 { 368 UINT uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL; 369 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 370 uFlags |= KLF_REPLACELANG; 371 372 pNode->hkl = LoadKeyboardLayoutW(szLogicalKLID, uFlags); 373 } 374 } 375 376 377 /* 378 * Writes any changes in input methods to the registry 379 */ 380 BOOL 381 InputList_Process(VOID) 382 { 383 INPUT_LIST_NODE *pCurrent; 384 DWORD dwNumber; 385 BOOL bRet = FALSE; 386 HKEY hPreloadKey, hSubstKey; 387 388 if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey)) 389 { 390 if (hPreloadKey) 391 RegCloseKey(hPreloadKey); 392 if (hSubstKey) 393 RegCloseKey(hSubstKey); 394 return FALSE; 395 } 396 397 /* Process DELETED and EDITED entries */ 398 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 399 { 400 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) || 401 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED)) 402 { 403 /* Only unload the DELETED and EDITED entries */ 404 if (UnloadKeyboardLayout(pCurrent->hkl)) 405 { 406 /* But the EDITED entries are used later */ 407 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 408 { 409 InputList_RemoveNode(pCurrent); 410 } 411 } 412 } 413 } 414 415 /* Add the DEFAULT entry and set font substitutes */ 416 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 417 { 418 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 419 continue; 420 421 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 422 { 423 bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId); 424 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent); 425 break; 426 } 427 } 428 429 /* Add entries except DEFAULT to registry */ 430 dwNumber = 2; 431 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 432 { 433 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 434 continue; 435 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 436 continue; 437 438 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent); 439 440 ++dwNumber; 441 } 442 443 /* Remove ADDED and EDITED flags */ 444 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 445 { 446 pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED); 447 } 448 449 /* Change the default keyboard language */ 450 if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &pCurrent->hkl, 0)) 451 { 452 DWORD dwRecipients = BSM_ALLCOMPONENTS | BSM_ALLDESKTOPS; 453 454 BroadcastSystemMessageW(BSF_POSTMESSAGE, 455 &dwRecipients, 456 WM_INPUTLANGCHANGEREQUEST, 457 0, 458 (LPARAM)pCurrent->hkl); 459 } 460 461 /* Retry to delete (in case of failure to delete the default keyboard) */ 462 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 463 { 464 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 465 { 466 UnloadKeyboardLayout(pCurrent->hkl); 467 InputList_RemoveNode(pCurrent); 468 } 469 } 470 471 RegCloseKey(hPreloadKey); 472 RegCloseKey(hSubstKey); 473 return bRet; 474 } 475 476 477 BOOL 478 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout) 479 { 480 WCHAR szIndicator[MAX_STR_LEN]; 481 INPUT_LIST_NODE *pInput = NULL; 482 483 if (pLocale == NULL || pLayout == NULL) 484 { 485 return FALSE; 486 } 487 488 for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext) 489 { 490 if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 491 continue; 492 493 if (pInput->pLocale == pLocale && pInput->pLayout == pLayout) 494 { 495 return FALSE; /* Already exists */ 496 } 497 } 498 499 pInput = InputList_AppendNode(); 500 pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED; 501 pInput->pLocale = pLocale; 502 pInput->pLayout = pLayout; 503 504 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 505 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 506 szIndicator, 507 ARRAYSIZE(szIndicator))) 508 { 509 size_t len = wcslen(szIndicator); 510 511 if (len > 0) 512 { 513 szIndicator[len - 1] = 0; 514 pInput->pszIndicator = _wcsdup(szIndicator); 515 } 516 } 517 518 return TRUE; 519 } 520 521 522 VOID 523 InputList_SetDefault(INPUT_LIST_NODE *pNode) 524 { 525 INPUT_LIST_NODE *pCurrent; 526 527 if (pNode == NULL) 528 return; 529 530 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext) 531 { 532 if (pCurrent == pNode) 533 { 534 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 535 } 536 else 537 { 538 pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 539 } 540 } 541 } 542 543 INPUT_LIST_NODE * 544 InputList_FindNextDefault(INPUT_LIST_NODE *pNode) 545 { 546 INPUT_LIST_NODE *pCurrent; 547 548 for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext) 549 { 550 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 551 continue; 552 553 return pCurrent; 554 } 555 556 for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev) 557 { 558 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) 559 continue; 560 561 return pCurrent; 562 } 563 564 return NULL; 565 } 566 567 /* 568 * It marks the input method for deletion, but does not delete it directly. 569 * To apply the changes using InputList_Process() 570 */ 571 VOID 572 InputList_Remove(INPUT_LIST_NODE *pNode) 573 { 574 BOOL bRemoveNode = FALSE; 575 576 if (pNode == NULL) 577 return; 578 579 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) 580 { 581 /* 582 * If the input method has been added to the list, but not yet written 583 * in the registry, then simply remove it from the list 584 */ 585 bRemoveNode = TRUE; 586 } 587 else 588 { 589 pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED; 590 } 591 592 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT) 593 { 594 INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode); 595 if (pCurrent) 596 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 597 598 pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT; 599 } 600 601 if (bRemoveNode) 602 { 603 InputList_RemoveNode(pNode); 604 } 605 } 606 607 608 VOID 609 InputList_Create(VOID) 610 { 611 INT iLayoutCount, iIndex; 612 WCHAR szIndicator[MAX_STR_LEN]; 613 INPUT_LIST_NODE *pInput; 614 HKL *pLayoutList, hklDefault; 615 616 SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0); 617 618 iLayoutCount = GetKeyboardLayoutList(0, NULL); 619 pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL)); 620 621 if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0) 622 { 623 free(pLayoutList); 624 return; 625 } 626 627 for (iIndex = 0; iIndex < iLayoutCount; ++iIndex) 628 { 629 HKL hKL = pLayoutList[iIndex]; 630 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL); 631 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL); 632 if (!pLocale || !pLayout) 633 continue; 634 635 pInput = InputList_AppendNode(); 636 pInput->pLocale = pLocale; 637 pInput->pLayout = pLayout; 638 pInput->hkl = hKL; 639 640 if (pInput->hkl == hklDefault) /* Default HKL? */ 641 { 642 pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT; 643 hklDefault = NULL; /* No more default item */ 644 } 645 646 /* Get abbrev language name */ 647 szIndicator[0] = 0; 648 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId), 649 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 650 szIndicator, 651 ARRAYSIZE(szIndicator))) 652 { 653 size_t len = wcslen(szIndicator); 654 if (len > 0) 655 { 656 szIndicator[len - 1] = 0; 657 pInput->pszIndicator = _wcsdup(szIndicator); 658 } 659 } 660 } 661 662 free(pLayoutList); 663 } 664 665 666 INPUT_LIST_NODE* 667 InputList_GetFirst(VOID) 668 { 669 return _InputList; 670 } 671