xref: /reactos/dll/cpl/input/input_list.c (revision 44323e61)
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     hKey = NULL;
27     RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey);
28     if (hKey == NULL)
29         return FALSE;
30 
31     /* Overwrite only */
32     for (; pSubstitutes->FontName; ++pSubstitutes)
33     {
34         cbData = (lstrlenW(pSubstitutes->SubFontName) + 1) * sizeof(WCHAR);
35         RegSetValueExW(hKey, pSubstitutes->FontName, 0,
36             REG_SZ, (LPBYTE)pSubstitutes->SubFontName, cbData);
37     }
38 
39     RegCloseKey(hKey);
40 
41     return TRUE;
42 }
43 
44 BOOL
45 InputList_SetFontSubstitutes(LCID dwLocaleId)
46 {
47     MUI_SUBFONT *pSubstitutes;
48     WORD wLangID, wPrimaryLangID, wSubLangID;
49 
50     wLangID = LANGIDFROMLCID(dwLocaleId);
51     wPrimaryLangID = PRIMARYLANGID(wLangID);
52     wSubLangID = SUBLANGID(wLangID);
53 
54     /* FIXME: Add more if necessary */
55     switch (wPrimaryLangID)
56     {
57         default:
58             pSubstitutes = LatinFonts;
59             break;
60         case LANG_AZERI:
61         case LANG_BELARUSIAN:
62         case LANG_BULGARIAN:
63         case LANG_KAZAK:
64         case LANG_RUSSIAN:
65         case LANG_SERBIAN:
66         case LANG_TATAR:
67         case LANG_UKRAINIAN:
68         case LANG_UZBEK:
69             pSubstitutes = CyrillicFonts;
70             break;
71         case LANG_GREEK:
72             pSubstitutes = GreekFonts;
73             break;
74         case LANG_HEBREW:
75             pSubstitutes = HebrewFonts;
76             break;
77         case LANG_CHINESE:
78             switch (wSubLangID)
79             {
80                 case SUBLANG_CHINESE_SIMPLIFIED:
81                 case SUBLANG_CHINESE_SINGAPORE:
82                 case SUBLANG_CHINESE_MACAU:
83                     pSubstitutes = ChineseSimplifiedFonts;
84                     break;
85                 case SUBLANG_CHINESE_TRADITIONAL:
86                 case SUBLANG_CHINESE_HONGKONG:
87                     pSubstitutes = ChineseTraditionalFonts;
88                     break;
89                 default:
90                     pSubstitutes = NULL;
91                     DebugBreak();
92                     break;
93             }
94             break;
95         case LANG_JAPANESE:
96             pSubstitutes = JapaneseFonts;
97             break;
98         case LANG_KOREAN:
99             pSubstitutes = KoreanFonts;
100             break;
101         case LANG_ARABIC:
102         case LANG_ARMENIAN:
103         case LANG_BENGALI:
104         case LANG_FARSI:
105         case LANG_GEORGIAN:
106         case LANG_GUJARATI:
107         case LANG_HINDI:
108         case LANG_KONKANI:
109         case LANG_MARATHI:
110         case LANG_PUNJABI:
111         case LANG_SANSKRIT:
112         case LANG_TAMIL:
113         case LANG_TELUGU:
114         case LANG_THAI:
115         case LANG_URDU:
116         case LANG_VIETNAMESE:
117             pSubstitutes = UnicodeFonts;
118             break;
119     }
120 
121     if (pSubstitutes)
122     {
123         UpdateRegistryForFontSubstitutes(pSubstitutes);
124         return TRUE;
125     }
126     return FALSE;
127 }
128 
129 static INPUT_LIST_NODE *_InputList = NULL;
130 
131 
132 static INPUT_LIST_NODE*
133 InputList_AppendNode(VOID)
134 {
135     INPUT_LIST_NODE *pCurrent;
136     INPUT_LIST_NODE *pNew;
137 
138     pCurrent = _InputList;
139 
140     pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE));
141     if (pNew == NULL)
142         return NULL;
143 
144     ZeroMemory(pNew, sizeof(INPUT_LIST_NODE));
145 
146     if (pCurrent == NULL)
147     {
148         _InputList = pNew;
149     }
150     else
151     {
152         while (pCurrent->pNext != NULL)
153         {
154             pCurrent = pCurrent->pNext;
155         }
156 
157         pNew->pPrev = pCurrent;
158         pCurrent->pNext = pNew;
159     }
160 
161     return pNew;
162 }
163 
164 
165 static VOID
166 InputList_RemoveNode(INPUT_LIST_NODE *pNode)
167 {
168     INPUT_LIST_NODE *pCurrent = pNode;
169 
170     if (_InputList == NULL)
171         return;
172 
173     if (pCurrent != NULL)
174     {
175         INPUT_LIST_NODE *pNext = pCurrent->pNext;
176         INPUT_LIST_NODE *pPrev = pCurrent->pPrev;
177 
178         free(pCurrent->pszIndicator);
179         free(pCurrent);
180 
181         if (pNext != NULL)
182             pNext->pPrev = pPrev;
183 
184         if (pPrev != NULL)
185             pPrev->pNext = pNext;
186         else
187             _InputList = pNext;
188     }
189 }
190 
191 
192 VOID
193 InputList_Destroy(VOID)
194 {
195     INPUT_LIST_NODE *pCurrent;
196 
197     if (_InputList == NULL)
198         return;
199 
200     pCurrent = _InputList;
201 
202     while (pCurrent != NULL)
203     {
204         INPUT_LIST_NODE *pNext = pCurrent->pNext;
205 
206         free(pCurrent->pszIndicator);
207         free(pCurrent);
208 
209         pCurrent = pNext;
210     }
211 
212     _InputList = NULL;
213 }
214 
215 
216 static BOOL
217 InputList_PrepareUserRegistry(VOID)
218 {
219     BOOL bResult = FALSE;
220     HKEY hTempKey = NULL;
221     HKEY hKey = NULL;
222 
223     if (RegOpenKeyExW(HKEY_CURRENT_USER,
224                       L"Keyboard Layout",
225                       0,
226                       KEY_ALL_ACCESS,
227                       &hKey) == ERROR_SUCCESS)
228     {
229         RegDeleteKeyW(hKey, L"Preload");
230         RegDeleteKeyW(hKey, L"Substitutes");
231 
232         RegCloseKey(hKey);
233     }
234 
235     if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) != ERROR_SUCCESS)
236     {
237         goto Cleanup;
238     }
239 
240     if (RegCreateKeyW(hKey, L"Preload", &hTempKey) != ERROR_SUCCESS)
241     {
242         goto Cleanup;
243     }
244 
245     RegCloseKey(hTempKey);
246 
247     if (RegCreateKeyW(hKey, L"Substitutes", &hTempKey) != ERROR_SUCCESS)
248     {
249         goto Cleanup;
250     }
251 
252     RegCloseKey(hTempKey);
253 
254     bResult = TRUE;
255 
256 Cleanup:
257     if (hTempKey != NULL)
258         RegCloseKey(hTempKey);
259     if (hKey != NULL)
260         RegCloseKey(hKey);
261 
262     return bResult;
263 }
264 
265 
266 static VOID
267 InputList_AddInputMethodToUserRegistry(DWORD dwIndex, INPUT_LIST_NODE *pNode)
268 {
269     WCHAR szMethodIndex[MAX_PATH];
270     WCHAR szPreload[MAX_PATH];
271     BOOL bIsImeMethod = FALSE;
272     HKEY hKey;
273 
274     StringCchPrintfW(szMethodIndex, ARRAYSIZE(szMethodIndex), L"%lu", dwIndex);
275 
276     /* Check is IME method */
277     if ((HIWORD(pNode->pLayout->dwId) & 0xF000) == 0xE000)
278     {
279         StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLayout->dwId);
280         bIsImeMethod = TRUE;
281     }
282     else
283     {
284         StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLocale->dwId);
285     }
286 
287     if (RegOpenKeyExW(HKEY_CURRENT_USER,
288                       L"Keyboard Layout\\Preload",
289                       0,
290                       KEY_SET_VALUE,
291                       &hKey) == ERROR_SUCCESS)
292     {
293         RegSetValueExW(hKey,
294                        szMethodIndex,
295                        0,
296                        REG_SZ,
297                        (LPBYTE)szPreload,
298                        (wcslen(szPreload) + 1) * sizeof(WCHAR));
299 
300         RegCloseKey(hKey);
301     }
302 
303     if (pNode->pLocale->dwId != pNode->pLayout->dwId && bIsImeMethod == FALSE)
304     {
305         if (RegOpenKeyExW(HKEY_CURRENT_USER,
306                           L"Keyboard Layout\\Substitutes",
307                           0,
308                           KEY_SET_VALUE,
309                           &hKey) == ERROR_SUCCESS)
310         {
311             WCHAR szSubstitutes[MAX_PATH];
312 
313             StringCchPrintfW(szSubstitutes, ARRAYSIZE(szSubstitutes), L"%08X", pNode->pLayout->dwId);
314 
315             RegSetValueExW(hKey,
316                            szPreload,
317                            0,
318                            REG_SZ,
319                            (LPBYTE)szSubstitutes,
320                            (wcslen(szSubstitutes) + 1) * sizeof(WCHAR));
321 
322             RegCloseKey(hKey);
323         }
324     }
325 
326     if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
327         (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
328     {
329         pNode->hkl = LoadKeyboardLayoutW(szPreload, KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL);
330     }
331 }
332 
333 
334 /*
335  * Writes any changes in input methods to the registry
336  */
337 BOOL
338 InputList_Process(VOID)
339 {
340     INPUT_LIST_NODE *pCurrent;
341     DWORD dwIndex;
342     BOOL bRet = FALSE;
343 
344     /* Process deleted and edited input methods */
345     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
346     {
347         if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
348             (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
349         {
350             if (UnloadKeyboardLayout(pCurrent->hkl))
351             {
352                 /* Only unload the edited input method, but does not delete it from the list */
353                 if (!(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
354                 {
355                     InputList_RemoveNode(pCurrent);
356                 }
357             }
358         }
359     }
360 
361     InputList_PrepareUserRegistry();
362 
363     /* Find default input method */
364     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
365     {
366         if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
367         {
368             bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
369             InputList_AddInputMethodToUserRegistry(1, pCurrent);
370             break;
371         }
372     }
373 
374     if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG,
375                               0,
376                               (LPVOID)((LPDWORD)&pCurrent->hkl),
377                               0))
378     {
379         DWORD dwRecipients;
380 
381         dwRecipients = BSM_ALLCOMPONENTS;
382 
383         BroadcastSystemMessageW(BSF_POSTMESSAGE,
384                                 &dwRecipients,
385                                 WM_INPUTLANGCHANGEREQUEST,
386                                 0,
387                                 (LPARAM)pCurrent->hkl);
388     }
389 
390     /* Add methods to registry */
391     dwIndex = 2;
392 
393     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
394     {
395         if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
396             continue;
397 
398         InputList_AddInputMethodToUserRegistry(dwIndex, pCurrent);
399 
400         dwIndex++;
401     }
402 
403     return bRet;
404 }
405 
406 
407 BOOL
408 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
409 {
410     WCHAR szIndicator[MAX_STR_LEN];
411     INPUT_LIST_NODE *pInput;
412 
413     if (pLocale == NULL || pLayout == NULL)
414     {
415         return FALSE;
416     }
417 
418     for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
419     {
420         if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
421         {
422             return FALSE;
423         }
424     }
425 
426     pInput = InputList_AppendNode();
427 
428     pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
429 
430     pInput->pLocale = pLocale;
431     pInput->pLayout = pLayout;
432 
433     if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
434                        LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
435                        szIndicator,
436                        ARRAYSIZE(szIndicator)))
437     {
438         size_t len = wcslen(szIndicator);
439 
440         if (len > 0)
441         {
442             szIndicator[len - 1] = 0;
443             pInput->pszIndicator = _wcsdup(szIndicator);
444         }
445     }
446 
447     return TRUE;
448 }
449 
450 
451 VOID
452 InputList_SetDefault(INPUT_LIST_NODE *pNode)
453 {
454     INPUT_LIST_NODE *pCurrent;
455 
456     if (pNode == NULL)
457         return;
458 
459     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
460     {
461         if (pCurrent == pNode)
462         {
463             pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
464         }
465         else
466         {
467             pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
468         }
469     }
470 }
471 
472 
473 /*
474  * It marks the input method for deletion, but does not delete it directly.
475  * To apply the changes using InputList_Process()
476  */
477 VOID
478 InputList_Remove(INPUT_LIST_NODE *pNode)
479 {
480     BOOL bRemoveNode = FALSE;
481 
482     if (pNode == NULL)
483         return;
484 
485     if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED)
486     {
487         /*
488          * If the input method has been added to the list, but not yet written
489          * in the registry, then simply remove it from the list
490          */
491         bRemoveNode = TRUE;
492     }
493     else
494     {
495         pNode->wFlags = INPUT_LIST_NODE_FLAG_DELETED;
496     }
497 
498     if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
499     {
500         if (pNode->pNext != NULL)
501         {
502             pNode->pNext->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
503         }
504         else if (pNode->pPrev != NULL)
505         {
506             pNode->pPrev->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
507         }
508     }
509 
510     if (bRemoveNode != FALSE)
511     {
512         InputList_RemoveNode(pNode);
513     }
514 }
515 
516 
517 VOID
518 InputList_Create(VOID)
519 {
520     INT iLayoutCount;
521     HKL *pLayoutList;
522 
523     iLayoutCount = GetKeyboardLayoutList(0, NULL);
524     pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
525 
526     if (pLayoutList != NULL)
527     {
528         if (GetKeyboardLayoutList(iLayoutCount, pLayoutList) > 0)
529         {
530             INT iIndex;
531 
532             for (iIndex = 0; iIndex < iLayoutCount; iIndex++)
533             {
534                 LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(pLayoutList[iIndex]);
535                 LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(pLayoutList[iIndex]);
536 
537                 if (pLocale != NULL && pLayout != NULL)
538                 {
539                     WCHAR szIndicator[MAX_STR_LEN] = { 0 };
540                     INPUT_LIST_NODE *pInput;
541                     HKL hklDefault;
542 
543                     pInput = InputList_AppendNode();
544 
545                     pInput->pLocale = pLocale;
546                     pInput->pLayout = pLayout;
547                     pInput->hkl     = pLayoutList[iIndex];
548 
549                     if (SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG,
550                                               0,
551                                               (LPVOID)((LPDWORD)&hklDefault),
552                                               0) == FALSE)
553                     {
554                         hklDefault = GetKeyboardLayout(0);
555                     }
556 
557                     if (pInput->hkl == hklDefault)
558                     {
559                         pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
560                     }
561 
562                     if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
563                                        LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
564                                        szIndicator,
565                                        ARRAYSIZE(szIndicator)))
566                     {
567                         size_t len = wcslen(szIndicator);
568 
569                         if (len > 0)
570                         {
571                             szIndicator[len - 1] = 0;
572                             pInput->pszIndicator = _wcsdup(szIndicator);
573                         }
574                     }
575                 }
576             }
577         }
578 
579         free(pLayoutList);
580     }
581 }
582 
583 
584 INPUT_LIST_NODE*
585 InputList_GetFirst(VOID)
586 {
587     return _InputList;
588 }
589