xref: /reactos/dll/cpl/input/layout_list.c (revision 47f3a4e1)
1 /*
2  * PROJECT:         input.dll
3  * FILE:            dll/cpl/input/layout_list.c
4  * PURPOSE:         input.dll
5  * PROGRAMMER:      Dmitry Chapyshev (dmitry@reactos.org)
6  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7  */
8 
9 #include "layout_list.h"
10 
11 static LAYOUT_LIST_NODE *_LayoutList = NULL;
12 
13 static LAYOUT_LIST_NODE*
14 LayoutList_AppendNode(DWORD dwKLID, WORD wSpecialId, LPCWSTR pszFile, LPCWSTR pszName,
15                       LPCWSTR pszImeFile)
16 {
17     LAYOUT_LIST_NODE *pCurrent;
18     LAYOUT_LIST_NODE *pNew;
19 
20     if (pszName == NULL)
21         return NULL;
22 
23     pCurrent = _LayoutList;
24 
25     pNew = (LAYOUT_LIST_NODE*)malloc(sizeof(LAYOUT_LIST_NODE));
26     if (pNew == NULL)
27         return NULL;
28 
29     ZeroMemory(pNew, sizeof(LAYOUT_LIST_NODE));
30 
31     pNew->dwKLID = dwKLID;
32     pNew->wSpecialId = wSpecialId;
33 
34     pNew->pszName = _wcsdup(pszName);
35     pNew->pszFile = _wcsdup(pszFile);
36     pNew->pszImeFile = _wcsdup(pszImeFile);
37     if (pNew->pszName == NULL || pNew->pszFile == NULL ||
38         (pszImeFile && pNew->pszImeFile == NULL))
39     {
40         free(pNew->pszName);
41         free(pNew->pszFile);
42         free(pNew->pszImeFile);
43         free(pNew);
44         return NULL;
45     }
46 
47     if (pCurrent == NULL)
48     {
49         _LayoutList = pNew;
50     }
51     else
52     {
53         while (pCurrent->pNext != NULL)
54         {
55             pCurrent = pCurrent->pNext;
56         }
57 
58         pNew->pPrev = pCurrent;
59         pCurrent->pNext = pNew;
60     }
61 
62     return pNew;
63 }
64 
65 
66 VOID
67 LayoutList_Destroy(VOID)
68 {
69     LAYOUT_LIST_NODE *pCurrent;
70     LAYOUT_LIST_NODE *pNext;
71 
72     if (_LayoutList == NULL)
73         return;
74 
75     for (pCurrent = _LayoutList; pCurrent; pCurrent = pNext)
76     {
77         pNext = pCurrent->pNext;
78 
79         free(pCurrent->pszName);
80         free(pCurrent->pszFile);
81         free(pCurrent->pszImeFile);
82         free(pCurrent);
83     }
84 
85     _LayoutList = NULL;
86 }
87 
88 typedef HRESULT (WINAPI *FN_SHLoadRegUIStringW)(HKEY, LPCWSTR, LPWSTR, DWORD);
89 
90 /* FIXME: Use shlwapi!SHLoadRegUIStringW instead when it is fully implemented */
91 HRESULT FakeSHLoadRegUIStringW(HKEY hkey, LPCWSTR value, LPWSTR buf, DWORD size)
92 {
93 #if 1
94     PWCHAR pBuffer, pIndex;
95     WCHAR szDllPath[MAX_PATH];
96     DWORD dwSize;
97     HINSTANCE hDllInst;
98     INT iIndex, iLength;
99 
100     dwSize = size * sizeof(WCHAR);
101     if (RegQueryValueExW(hkey, value, NULL, NULL, (LPBYTE)buf, &dwSize) != ERROR_SUCCESS)
102         return E_FAIL;
103 
104     if (buf[0] != L'@')
105         return S_OK;
106 
107     /* Move to the position after the character "@" */
108     pBuffer = buf + 1;
109 
110     /* Get a pointer to the beginning ",-" */
111     pIndex = wcsstr(pBuffer, L",-");
112     if (!pIndex)
113         return E_FAIL;
114 
115     /* Convert the number in the string after the ",-" */
116     iIndex = _wtoi(pIndex + 2);
117 
118     *pIndex = 0; /* Cut the string */
119 
120     if (ExpandEnvironmentStringsW(pBuffer, szDllPath, ARRAYSIZE(szDllPath)) == 0)
121         return E_FAIL;
122 
123     hDllInst = LoadLibraryW(szDllPath);
124     if (!hDllInst)
125         return E_FAIL;
126 
127     iLength = LoadStringW(hDllInst, iIndex, buf, size);
128     FreeLibrary(hDllInst);
129 
130     if (iLength <= 0)
131         return E_FAIL;
132 
133     return S_OK;
134 #else
135     HRESULT hr = E_FAIL;
136     HINSTANCE hSHLWAPI = LoadLibraryW(L"shlwapi");
137     FN_SHLoadRegUIStringW fn;
138     fn = (FN_SHLoadRegUIStringW)GetProcAddress(hSHLWAPI, (LPCSTR)(INT_PTR)439);
139     if (fn)
140         hr = fn(hkey, value, buf, size);
141     FreeLibrary(hSHLWAPI);
142     return hr;
143 #endif
144 }
145 
146 static BOOL
147 LayoutList_ReadLayout(HKEY hLayoutKey, LPCWSTR szKLID, LPCWSTR szSystemDirectory)
148 {
149     WCHAR szFile[80], szImeFile[80], szBuffer[MAX_PATH], szFilePath[MAX_PATH];
150     DWORD dwSize, dwKLID = DWORDfromString(szKLID);
151     WORD wSpecialId = 0;
152     LPWSTR pszImeFile = NULL;
153 
154     dwSize = sizeof(szFile);
155     if (RegQueryValueExW(hLayoutKey, L"Layout File", NULL, NULL,
156                          (LPBYTE)szFile, &dwSize) != ERROR_SUCCESS)
157     {
158         return FALSE; /* No "Layout File" value */
159     }
160 
161     if (IS_IME_KLID(dwKLID))
162     {
163         WCHAR szPath[MAX_PATH];
164         dwSize = sizeof(szImeFile);
165         if (RegQueryValueExW(hLayoutKey, L"IME File", NULL, NULL,
166                              (LPBYTE)szImeFile, &dwSize) != ERROR_SUCCESS)
167         {
168             return FALSE; /* No "IME File" value */
169         }
170 
171         if (wcschr(szImeFile, L'\\') != NULL)
172             return FALSE; /* Invalid character */
173 
174         GetSystemLibraryPath(szPath, ARRAYSIZE(szPath), szImeFile);
175         if (GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES)
176             return FALSE; /* Does not exist */
177 
178         pszImeFile = szImeFile;
179     }
180 
181     /* Build the "Layout File" full path and check existence */
182     StringCchPrintfW(szFilePath, ARRAYSIZE(szFilePath), L"%s\\%s", szSystemDirectory, szFile);
183     if (GetFileAttributesW(szFilePath) == INVALID_FILE_ATTRIBUTES)
184         return FALSE; /* No layout file found */
185 
186     /* Get the special ID */
187     dwSize = sizeof(szBuffer);
188     if (RegQueryValueExW(hLayoutKey, L"Layout Id", NULL, NULL,
189                          (LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS)
190     {
191         wSpecialId = LOWORD(DWORDfromString(szBuffer));
192     }
193 
194     /* If there is a valid "Layout Display Name", then use it as the entry name */
195     if (FakeSHLoadRegUIStringW(hLayoutKey, L"Layout Display Name",
196                                szBuffer, ARRAYSIZE(szBuffer)) == S_OK)
197     {
198         LayoutList_AppendNode(dwKLID, wSpecialId, szFile, szBuffer, pszImeFile);
199         return TRUE;
200     }
201 
202     /* Otherwise, use "Layout Text" value as the entry name */
203     dwSize = sizeof(szBuffer);
204     if (RegQueryValueExW(hLayoutKey, L"Layout Text", NULL, NULL,
205                          (LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS)
206     {
207         LayoutList_AppendNode(dwKLID, wSpecialId, szFile, szBuffer, pszImeFile);
208         return TRUE;
209     }
210 
211     return FALSE;
212 }
213 
214 VOID
215 LayoutList_Create(VOID)
216 {
217     WCHAR szSystemDirectory[MAX_PATH], szKLID[KL_NAMELENGTH];
218     DWORD dwSize, dwIndex;
219     HKEY hKey, hLayoutKey;
220 
221     if (!GetSystemDirectoryW(szSystemDirectory, ARRAYSIZE(szSystemDirectory)))
222         return;
223 
224     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
225                       0, KEY_READ, &hKey) != ERROR_SUCCESS)
226     {
227         return;
228     }
229 
230     for (dwIndex = 0; ; ++dwIndex)
231     {
232         dwSize = ARRAYSIZE(szKLID);
233         if (RegEnumKeyExW(hKey, dwIndex, szKLID, &dwSize, NULL, NULL,
234                           NULL, NULL) != ERROR_SUCCESS)
235         {
236             break;
237         }
238 
239         if (RegOpenKeyExW(hKey, szKLID, 0, KEY_QUERY_VALUE, &hLayoutKey) == ERROR_SUCCESS)
240         {
241             LayoutList_ReadLayout(hLayoutKey, szKLID, szSystemDirectory);
242             RegCloseKey(hLayoutKey);
243         }
244     }
245 
246     RegCloseKey(hKey);
247 }
248 
249 
250 LAYOUT_LIST_NODE*
251 LayoutList_GetByHkl(HKL hkl)
252 {
253     LAYOUT_LIST_NODE *pCurrent;
254 
255     if (IS_SPECIAL_HKL(hkl))
256     {
257         WORD wSpecialId = SPECIALIDFROMHKL(hkl);
258 
259         for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
260         {
261             if (wSpecialId == pCurrent->wSpecialId)
262             {
263                 return pCurrent;
264             }
265         }
266     }
267     else if (IS_IME_HKL(hkl))
268     {
269         for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
270         {
271             if (hkl == UlongToHandle(pCurrent->dwKLID))
272             {
273                 return pCurrent;
274             }
275         }
276     }
277     else
278     {
279         for (pCurrent = _LayoutList; pCurrent != NULL; pCurrent = pCurrent->pNext)
280         {
281             if (HIWORD(hkl) == LOWORD(pCurrent->dwKLID))
282             {
283                 return pCurrent;
284             }
285         }
286     }
287 
288     return NULL;
289 }
290 
291 
292 LAYOUT_LIST_NODE*
293 LayoutList_GetFirst(VOID)
294 {
295     return _LayoutList;
296 }
297