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
UpdateRegistryForFontSubstitutes(MUI_SUBFONT * pSubstitutes)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
GetSystemLibraryPath(LPWSTR pszPath,INT cchPath,LPCWSTR pszFileName)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
InputList_SetFontSubstitutes(LCID dwLocaleId)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*
InputList_AppendNode(VOID)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
InputList_RemoveNode(INPUT_LIST_NODE * pNode)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
InputList_Destroy(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
InputList_PrepareUserRegistry(PHKEY phPreloadKey,PHKEY phSubstKey)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
InputList_FindPreloadKLID(HKEY hPreloadKey,DWORD dwKLID)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
InputList_WriteSubst(HKEY hSubstKey,DWORD dwPhysicalKLID,DWORD dwLogicalKLID)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
InputList_DoSubst(HKEY hPreloadKey,HKEY hSubstKey,DWORD dwPhysicalKLID,DWORD dwLogicalKLID)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
InputList_AddInputMethodToUserRegistry(HKEY hPreloadKey,HKEY hSubstKey,DWORD dwNumber,INPUT_LIST_NODE * pNode)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
InputList_Process(VOID)382 InputList_Process(VOID)
383 {
384 INPUT_LIST_NODE *pCurrent;
385 DWORD dwNumber;
386 BOOL bRet = FALSE;
387 HKEY hPreloadKey, hSubstKey;
388 HKL hDefaultKL = NULL;
389
390 if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey))
391 {
392 if (hPreloadKey)
393 RegCloseKey(hPreloadKey);
394 if (hSubstKey)
395 RegCloseKey(hSubstKey);
396 return FALSE;
397 }
398
399 /* Find change in the IME HKLs */
400 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
401 {
402 if (!IS_IME_HKL(pCurrent->hkl))
403 continue;
404
405 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
406 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED) ||
407 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED))
408 {
409 bRet = TRUE; /* Reboot is needed */
410 break;
411 }
412 }
413
414 /* Process DELETED and EDITED entries */
415 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
416 {
417 if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
418 (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
419 {
420
421 /* Only unload the DELETED and EDITED entries */
422 if (UnloadKeyboardLayout(pCurrent->hkl))
423 {
424 /* But the EDITED entries are used later */
425 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
426 {
427 InputList_RemoveNode(pCurrent);
428 }
429 }
430 }
431 }
432
433 /* Add the DEFAULT entry and set font substitutes */
434 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
435 {
436 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
437 continue;
438
439 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
440 {
441 bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
442 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent);
443
444 /* Activate the DEFAULT entry */
445 ActivateKeyboardLayout(pCurrent->hkl, KLF_RESET);
446
447 /* Save it */
448 hDefaultKL = pCurrent->hkl;
449 break;
450 }
451 }
452
453 /* Add entries except DEFAULT to registry */
454 dwNumber = 2;
455 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
456 {
457 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
458 continue;
459 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
460 continue;
461
462 InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent);
463
464 ++dwNumber;
465 }
466
467 /* Remove ADDED and EDITED flags */
468 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
469 {
470 pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED);
471 }
472
473 /* Change the default keyboard language */
474 if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &hDefaultKL, 0))
475 {
476 DWORD dwRecipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
477
478 BroadcastSystemMessageW(BSF_POSTMESSAGE,
479 &dwRecipients,
480 WM_INPUTLANGCHANGEREQUEST,
481 INPUTLANGCHANGE_SYSCHARSET,
482 (LPARAM)hDefaultKL);
483 }
484
485 /* Retry to delete (in case of failure to delete the default keyboard) */
486 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
487 {
488 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
489 {
490 UnloadKeyboardLayout(pCurrent->hkl);
491 InputList_RemoveNode(pCurrent);
492 }
493 }
494
495 RegCloseKey(hPreloadKey);
496 RegCloseKey(hSubstKey);
497 return bRet;
498 }
499
500
501 BOOL
InputList_Add(LOCALE_LIST_NODE * pLocale,LAYOUT_LIST_NODE * pLayout)502 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
503 {
504 WCHAR szIndicator[MAX_STR_LEN];
505 INPUT_LIST_NODE *pInput = NULL;
506
507 if (pLocale == NULL || pLayout == NULL)
508 {
509 return FALSE;
510 }
511
512 for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
513 {
514 if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
515 continue;
516
517 if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
518 {
519 return FALSE; /* Already exists */
520 }
521 }
522
523 pInput = InputList_AppendNode();
524 pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
525 pInput->pLocale = pLocale;
526 pInput->pLayout = pLayout;
527
528 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
529 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
530 szIndicator,
531 ARRAYSIZE(szIndicator)))
532 {
533 size_t len = wcslen(szIndicator);
534
535 if (len > 0)
536 {
537 szIndicator[len - 1] = 0;
538 pInput->pszIndicator = _wcsdup(szIndicator);
539 }
540 }
541
542 return TRUE;
543 }
544
545
546 VOID
InputList_SetDefault(INPUT_LIST_NODE * pNode)547 InputList_SetDefault(INPUT_LIST_NODE *pNode)
548 {
549 INPUT_LIST_NODE *pCurrent;
550
551 if (pNode == NULL)
552 return;
553
554 for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
555 {
556 if (pCurrent == pNode)
557 {
558 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
559 }
560 else
561 {
562 pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
563 }
564 }
565 }
566
567 INPUT_LIST_NODE *
InputList_FindNextDefault(INPUT_LIST_NODE * pNode)568 InputList_FindNextDefault(INPUT_LIST_NODE *pNode)
569 {
570 INPUT_LIST_NODE *pCurrent;
571
572 for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext)
573 {
574 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
575 continue;
576
577 return pCurrent;
578 }
579
580 for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev)
581 {
582 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
583 continue;
584
585 return pCurrent;
586 }
587
588 return NULL;
589 }
590
591 /*
592 * It marks the input method for deletion, but does not delete it directly.
593 * To apply the changes using InputList_Process()
594 */
595 BOOL
InputList_Remove(INPUT_LIST_NODE * pNode)596 InputList_Remove(INPUT_LIST_NODE *pNode)
597 {
598 BOOL ret = FALSE;
599 BOOL bRemoveNode = FALSE;
600
601 if (pNode == NULL)
602 return FALSE;
603
604 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED)
605 {
606 /*
607 * If the input method has been added to the list, but not yet written
608 * in the registry, then simply remove it from the list
609 */
610 bRemoveNode = TRUE;
611 }
612 else
613 {
614 pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED;
615 }
616
617 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
618 {
619 INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode);
620 if (pCurrent)
621 pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
622
623 pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
624 ret = TRUE; /* default input is changed */
625 }
626
627 if (bRemoveNode)
628 {
629 InputList_RemoveNode(pNode);
630 }
631
632 return ret;
633 }
634
635 BOOL
InputList_RemoveByLang(LANGID wLangId)636 InputList_RemoveByLang(LANGID wLangId)
637 {
638 BOOL ret = FALSE;
639 INPUT_LIST_NODE *pCurrent;
640
641 Retry:
642 for (pCurrent = _InputList; pCurrent; pCurrent = pCurrent->pNext)
643 {
644 if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
645 continue;
646
647 if (LOWORD(pCurrent->pLocale->dwId) == wLangId)
648 {
649 if (InputList_Remove(pCurrent))
650 ret = TRUE; /* default input is changed */
651 goto Retry;
652 }
653 }
654
655 return ret;
656 }
657
658 VOID
InputList_Create(VOID)659 InputList_Create(VOID)
660 {
661 INT iLayoutCount, iIndex;
662 WCHAR szIndicator[MAX_STR_LEN];
663 INPUT_LIST_NODE *pInput;
664 HKL *pLayoutList, hklDefault;
665
666 SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0);
667
668 iLayoutCount = GetKeyboardLayoutList(0, NULL);
669 pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
670
671 if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0)
672 {
673 free(pLayoutList);
674 return;
675 }
676
677 for (iIndex = 0; iIndex < iLayoutCount; ++iIndex)
678 {
679 HKL hKL = pLayoutList[iIndex];
680 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL);
681 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL);
682 if (!pLocale || !pLayout)
683 continue;
684
685 pInput = InputList_AppendNode();
686 pInput->pLocale = pLocale;
687 pInput->pLayout = pLayout;
688 pInput->hkl = hKL;
689
690 if (pInput->hkl == hklDefault) /* Default HKL? */
691 {
692 pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
693 hklDefault = NULL; /* No more default item */
694 }
695
696 /* Get abbrev language name */
697 szIndicator[0] = 0;
698 if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
699 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
700 szIndicator,
701 ARRAYSIZE(szIndicator)))
702 {
703 size_t len = wcslen(szIndicator);
704 if (len > 0)
705 {
706 szIndicator[len - 1] = 0;
707 pInput->pszIndicator = _wcsdup(szIndicator);
708 }
709 }
710 }
711
712 free(pLayoutList);
713 }
714
InputList_Compare(INPUT_LIST_NODE * pNode1,INPUT_LIST_NODE * pNode2)715 static INT InputList_Compare(INPUT_LIST_NODE *pNode1, INPUT_LIST_NODE *pNode2)
716 {
717 INT nCompare = _wcsicmp(pNode1->pszIndicator, pNode2->pszIndicator);
718 if (nCompare != 0)
719 return nCompare;
720
721 return _wcsicmp(pNode1->pLayout->pszName, pNode2->pLayout->pszName);
722 }
723
InputList_Sort(VOID)724 VOID InputList_Sort(VOID)
725 {
726 INPUT_LIST_NODE *pList = _InputList;
727 INPUT_LIST_NODE *pNext, *pPrev;
728 INPUT_LIST_NODE *pMinimum, *pNode;
729
730 _InputList = NULL;
731
732 while (pList)
733 {
734 /* Find the minimum node */
735 pMinimum = NULL;
736 for (pNode = pList; pNode; pNode = pNext)
737 {
738 pNext = pNode->pNext;
739
740 if (pMinimum == NULL)
741 {
742 pMinimum = pNode;
743 }
744 else if (InputList_Compare(pNode, pMinimum) < 0)
745 {
746 pMinimum = pNode;
747 }
748 }
749
750 // Remove pMinimum from pList
751 pNext = pMinimum->pNext;
752 pPrev = pMinimum->pPrev;
753 if (pNext)
754 pNext->pPrev = pPrev;
755 if (pPrev)
756 pPrev->pNext = pNext;
757 else
758 pList = pNext;
759
760 // Append pMinimum to _InputList
761 if (!_InputList)
762 {
763 pMinimum->pPrev = pMinimum->pNext = NULL;
764 _InputList = pMinimum;
765 }
766 else
767 {
768 /* Find last node */
769 for (pNode = _InputList; pNode->pNext; pNode = pNode->pNext)
770 {
771 NOTHING;
772 }
773
774 /* Add to the end */
775 pNode->pNext = pMinimum;
776 pMinimum->pPrev = pNode;
777 pMinimum->pNext = NULL;
778 }
779 }
780 }
781
782 INT
InputList_GetAliveCount(VOID)783 InputList_GetAliveCount(VOID)
784 {
785 INPUT_LIST_NODE *pNode;
786 INT nCount = 0;
787
788 for (pNode = _InputList; pNode; pNode = pNode->pNext)
789 {
790 if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
791 continue;
792
793 ++nCount;
794 }
795
796 return nCount;
797 }
798
799 INPUT_LIST_NODE*
InputList_GetFirst(VOID)800 InputList_GetFirst(VOID)
801 {
802 return _InputList;
803 }
804