xref: /reactos/dll/cpl/input/input_list.c (revision f7a4c263)
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