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