- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / kbdlayout.c
1
2 /*
3 * PROJECT: ReactOS Kernel
4 * LICENSE: GPL - See COPYING in the top level directory
5 * FILE: subsystems/win32/win32k/ntuser/kbdlayout.c
6 * PURPOSE: Keyboard layout management
7 * COPYRIGHT: Copyright 2007 Saveliy Tretiakov
8 *
9 */
10
11
12 /* INCLUDES ******************************************************************/
13
14 #include <w32k.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 PKBL KBLList = NULL; // Keyboard layout list.
20
21 typedef PVOID (*KbdLayerDescriptor)(VOID);
22 NTSTATUS STDCALL LdrGetProcedureAddress(PVOID module,
23 PANSI_STRING import_name,
24 DWORD flags,
25 PVOID *func_addr);
26
27
28
29 /* PRIVATE FUNCTIONS ******************************************************/
30
31
32 /*
33 * Utility function to read a value from the registry more easily.
34 *
35 * IN PUNICODE_STRING KeyName -> Name of key to open
36 * IN PUNICODE_STRING ValueName -> Name of value to open
37 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
38 *
39 * Returns NTSTATUS
40 */
41
42 static NTSTATUS NTAPI ReadRegistryValue( PUNICODE_STRING KeyName,
43 PUNICODE_STRING ValueName,
44 PUNICODE_STRING ReturnedValue )
45 {
46 NTSTATUS Status;
47 HANDLE KeyHandle;
48 OBJECT_ATTRIBUTES KeyAttributes;
49 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
50 ULONG Length = 0;
51 ULONG ResLength = 0;
52 PWCHAR ReturnBuffer;
53
54 InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
55 NULL, NULL);
56 Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &KeyAttributes);
57 if( !NT_SUCCESS(Status) )
58 {
59 return Status;
60 }
61
62 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
63 0,
64 0,
65 &ResLength);
66
67 if( Status != STATUS_BUFFER_TOO_SMALL )
68 {
69 NtClose(KeyHandle);
70 return Status;
71 }
72
73 ResLength += sizeof( *KeyValuePartialInfo );
74 KeyValuePartialInfo =
75 ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);
76 Length = ResLength;
77
78 if( !KeyValuePartialInfo )
79 {
80 NtClose(KeyHandle);
81 return STATUS_NO_MEMORY;
82 }
83
84 Status = ZwQueryValueKey(KeyHandle,
85 ValueName,
86 KeyValuePartialInformation,
87 (PVOID)KeyValuePartialInfo,
88 Length,
89 &ResLength);
90
91 if( !NT_SUCCESS(Status) )
92 {
93 NtClose(KeyHandle);
94 ExFreePool(KeyValuePartialInfo);
95 return Status;
96 }
97
98 /* At this point, KeyValuePartialInfo->Data contains the key data */
99 ReturnBuffer = ExAllocatePoolWithTag(PagedPool,
100 KeyValuePartialInfo->DataLength,
101 TAG_STRING);
102
103 if(!ReturnBuffer)
104 {
105 NtClose(KeyHandle);
106 ExFreePool(KeyValuePartialInfo);
107 return STATUS_NO_MEMORY;
108 }
109
110 RtlCopyMemory(ReturnBuffer,
111 KeyValuePartialInfo->Data,
112 KeyValuePartialInfo->DataLength);
113 RtlInitUnicodeString(ReturnedValue, ReturnBuffer);
114
115 ExFreePool(KeyValuePartialInfo);
116 NtClose(KeyHandle);
117
118 return Status;
119 }
120
121 static BOOL UserLoadKbdDll(WCHAR *wsKLID,
122 HANDLE *phModule,
123 PKBDTABLES *pKbdTables)
124 {
125 NTSTATUS Status;
126 KbdLayerDescriptor layerDescGetFn;
127 ANSI_STRING kbdProcedureName;
128 UNICODE_STRING LayoutKeyName;
129 UNICODE_STRING LayoutValueName;
130 UNICODE_STRING LayoutFile;
131 UNICODE_STRING FullLayoutPath;
132 UNICODE_STRING klid;
133 WCHAR LayoutPathBuffer[MAX_PATH] = L"\\SystemRoot\\System32\\";
134 WCHAR KeyNameBuffer[MAX_PATH] = L"\\REGISTRY\\Machine\\SYSTEM\\"
135 L"CurrentControlSet\\Control\\KeyboardLayouts\\";
136
137 RtlInitUnicodeString(&klid, wsKLID);
138 RtlInitUnicodeString(&LayoutValueName,L"Layout File");
139 RtlInitUnicodeString(&LayoutKeyName, KeyNameBuffer);
140 LayoutKeyName.MaximumLength = sizeof(KeyNameBuffer);
141
142 RtlAppendUnicodeStringToString(&LayoutKeyName, &klid);
143 Status = ReadRegistryValue(&LayoutKeyName, &LayoutValueName, &LayoutFile);
144
145 if(!NT_SUCCESS(Status))
146 {
147 DPRINT1("Can't get layout filename for %wZ. (%08lx)\n", klid, Status);
148 return FALSE;
149 }
150
151 DPRINT("Read registry and got %wZ\n", &LayoutFile);
152 RtlInitUnicodeString(&FullLayoutPath, LayoutPathBuffer);
153 FullLayoutPath.MaximumLength = sizeof(LayoutPathBuffer);
154 RtlAppendUnicodeStringToString(&FullLayoutPath, &LayoutFile);
155 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
156 RtlFreeUnicodeString(&LayoutFile);
157
158 *phModule = EngLoadImage(FullLayoutPath.Buffer);
159
160 if(*phModule)
161 {
162 DPRINT("Loaded %wZ\n", &FullLayoutPath);
163
164 RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
165 LdrGetProcedureAddress((PVOID)*phModule,
166 &kbdProcedureName,
167 0,
168 (PVOID*)&layerDescGetFn);
169
170 if(layerDescGetFn)
171 {
172 *pKbdTables = layerDescGetFn();
173 }
174 else
175 {
176 DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath);
177 }
178
179 if(!layerDescGetFn || !*pKbdTables)
180 {
181 DPRINT1("Failed to load the keyboard layout.\n");
182 EngUnloadImage(*phModule);
183 return FALSE;
184 }
185 }
186 else
187 {
188 DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath);
189 return FALSE;
190 }
191
192 return TRUE;
193 }
194
195 static PKBL UserLoadDllAndCreateKbl(DWORD LocaleId)
196 {
197 PKBL NewKbl;
198 ULONG hKl;
199 LANGID langid;
200
201 NewKbl = ExAllocatePool(PagedPool, sizeof(KBL));
202
203 if(!NewKbl)
204 {
205 DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__);
206 return NULL;
207 }
208
209 swprintf(NewKbl->Name, L"%08lx", LocaleId);
210
211 if(!UserLoadKbdDll(NewKbl->Name, &NewKbl->hModule, &NewKbl->KBTables))
212 {
213 DPRINT1("%s: failed to load %x dll!\n", __FUNCTION__, LocaleId);
214 ExFreePool(NewKbl);
215 return NULL;
216 }
217
218 /* Microsoft Office expects this value to be something specific
219 * for Japanese and Korean Windows with an IME the value is 0xe001
220 * We should probably check to see if an IME exists and if so then
221 * set this word properly.
222 */
223 langid = PRIMARYLANGID(LANGIDFROMLCID(LocaleId));
224 hKl = LocaleId;
225
226 if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
227 hKl |= 0xe001 << 16; /* FIXME */
228 else hKl |= hKl << 16;
229
230 NewKbl->hkl = (HKL) hKl;
231 NewKbl->klid = LocaleId;
232 NewKbl->Flags = 0;
233 NewKbl->RefCount = 0;
234
235 return NewKbl;
236 }
237
238 BOOL UserInitDefaultKeyboardLayout()
239 {
240 LCID LocaleId;
241 NTSTATUS Status;
242
243 Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
244 if (!NT_SUCCESS(Status))
245 {
246 DPRINT1("Could not get default locale (%08lx).\n", Status);
247 }
248 else
249 {
250 DPRINT("DefaultLocale = %08lx\n", LocaleId);
251 }
252
253 if(!NT_SUCCESS(Status) || !(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
254 {
255 DPRINT1("Trying to load US Keyboard Layout.\n");
256 LocaleId = 0x409;
257
258 if(!(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
259 {
260 DPRINT1("Failed to load any Keyboard Layout\n");
261 return FALSE;
262 }
263 }
264
265 InitializeListHead(&KBLList->List);
266 return TRUE;
267 }
268
269 PKBL W32kGetDefaultKeyLayout(VOID)
270 {
271 LCID LocaleId;
272 NTSTATUS Status;
273 PKBL pKbl;
274
275 // This is probably wrong...
276 // I need to do more research.
277 Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
278 if (!NT_SUCCESS(Status))
279 {
280 DPRINT1("Could not get default locale (%08lx).\n", Status);
281 DPRINT1("Assuming default locale = 0x409 (US).\n");
282 LocaleId = 0x409;
283 }
284
285 pKbl = KBLList;
286 do
287 {
288 if(pKbl->klid == LocaleId)
289 {
290 return pKbl;
291 }
292
293 pKbl = (PKBL) pKbl->List.Flink;
294 } while(pKbl != KBLList);
295
296 DPRINT("Loading new default keyboard layout.\n");
297 pKbl = UserLoadDllAndCreateKbl(LocaleId);
298
299 if(!pKbl)
300 {
301 DPRINT1("Failed to load %x!!! Returning any availableKL.\n", LocaleId);
302 return KBLList;
303 }
304
305 InsertTailList(&KBLList->List, &pKbl->List);
306 return pKbl;
307 }
308
309 static PKBL UserHklToKbl(HKL hKl)
310 {
311 PKBL pKbl = KBLList;
312 do
313 {
314 if(pKbl->hkl == hKl) return pKbl;
315 pKbl = (PKBL) pKbl->List.Flink;
316 } while(pKbl != KBLList);
317
318 return NULL;
319 }
320
321 static PKBL co_UserActivateKbl(PW32THREAD Thread, PKBL pKbl)
322 {
323 PKBL Prev;
324
325 Prev = Thread->KeyboardLayout;
326 Prev->RefCount--;
327 Thread->KeyboardLayout = pKbl;
328 pKbl->RefCount++;
329
330 // Send WM_INPUTLANGCHANGE to thread's focus window
331 co_IntSendMessage(Thread->MessageQueue->FocusWindow,
332 WM_INPUTLANGCHANGE,
333 0, // FIXME: put charset here (what is this?)
334 (LPARAM)pKbl->hkl); //klid
335
336 return Prev;
337 }
338
339 /* EXPORTS *******************************************************************/
340
341 HKL FASTCALL
342 UserGetKeyboardLayout(
343 DWORD dwThreadId)
344 {
345 NTSTATUS Status;
346 PETHREAD Thread;
347 PW32THREAD W32Thread;
348 HKL Ret;
349
350 if(!dwThreadId)
351 {
352 W32Thread = PsGetCurrentThreadWin32Thread();
353 return W32Thread->KeyboardLayout->hkl;
354 }
355
356 Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);
357 if(!NT_SUCCESS(Status))
358 {
359 SetLastWin32Error(ERROR_INVALID_PARAMETER);
360 return NULL;
361 }
362
363 W32Thread = PsGetThreadWin32Thread(Thread);
364 Ret = W32Thread->KeyboardLayout->hkl;
365 ObDereferenceObject(Thread);
366 return Ret;
367 }
368
369 UINT
370 STDCALL
371 NtUserGetKeyboardLayoutList(
372 INT nItems,
373 HKL* pHklBuff)
374 {
375 UINT Ret = 0;
376 PKBL pKbl;
377
378 UserEnterShared();
379 pKbl = KBLList;
380
381 if(nItems == 0)
382 {
383 do
384 {
385 Ret++;
386 pKbl = (PKBL) pKbl->List.Flink;
387 } while(pKbl != KBLList);
388 }
389 else
390 {
391 _SEH_TRY
392 {
393 ProbeForWrite(pHklBuff, nItems*sizeof(HKL), 4);
394
395 while(Ret < nItems)
396 {
397 pHklBuff[Ret] = pKbl->hkl;
398 Ret++;
399 pKbl = (PKBL) pKbl->List.Flink;
400 if(pKbl == KBLList) break;
401 }
402
403 }
404 _SEH_HANDLE
405 {
406 SetLastNtError(_SEH_GetExceptionCode());
407 Ret = 0;
408 }
409 _SEH_END;
410 }
411
412 UserLeave();
413 return Ret;
414 }
415
416 BOOL
417 STDCALL
418 NtUserGetKeyboardLayoutName(
419 LPWSTR lpszName)
420 {
421 BOOL ret = FALSE;
422 PKBL pKbl;
423
424 UserEnterShared();
425
426 _SEH_TRY
427 {
428 ProbeForWrite(lpszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
429 pKbl = PsGetCurrentThreadWin32Thread()->KeyboardLayout;
430 RtlCopyMemory(lpszName, pKbl->Name, KL_NAMELENGTH*sizeof(WCHAR));
431 ret = TRUE;
432 }
433 _SEH_HANDLE
434 {
435 SetLastNtError(_SEH_GetExceptionCode());
436 ret = FALSE;
437 }
438 _SEH_END;
439
440 UserLeave();
441 return ret;
442 }
443
444
445 HKL
446 STDCALL
447 NtUserLoadKeyboardLayoutEx(
448 IN DWORD dwKLID,
449 IN UINT Flags,
450 IN DWORD Unused1,
451 IN DWORD Unused2,
452 IN DWORD Unused3,
453 IN DWORD Unused4)
454 {
455 HKL Ret = NULL;
456 PKBL pKbl = NULL, Cur;
457
458 UserEnterExclusive();
459
460 Cur = KBLList;
461 do
462 {
463 if(Cur->klid == dwKLID)
464 {
465 pKbl = Cur;
466 break;
467 }
468
469 Cur = (PKBL) Cur->List.Flink;
470 } while(Cur != KBLList);
471
472 if(!pKbl)
473 {
474 pKbl = UserLoadDllAndCreateKbl(dwKLID);
475
476 if(!pKbl)
477 {
478 goto the_end;
479 }
480
481 InsertTailList(&KBLList->List, &pKbl->List);
482 }
483
484 if(Flags & KLF_REORDER) KBLList = pKbl;
485
486 if(Flags & KLF_ACTIVATE)
487 {
488 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl);
489 }
490
491 Ret = pKbl->hkl;
492
493 //FIXME: Respect Flags!
494 // KLF_NOTELLSHELL KLF_SETFORPROCESS
495 // KLF_REPLACELANG KLF_SUBSTITUTE_OK
496
497 the_end:
498 UserLeave();
499 return Ret;
500 }
501
502 HKL
503 STDCALL
504 NtUserActivateKeyboardLayout(
505 HKL hKl,
506 ULONG Flags)
507 {
508 PKBL pKbl;
509 HKL Ret = NULL;
510 PW32THREAD pWThread;
511
512 UserEnterExclusive();
513
514 pWThread = PsGetCurrentThreadWin32Thread();
515
516 if(pWThread->KeyboardLayout->hkl == hKl)
517 {
518 Ret = hKl;
519 goto the_end;
520 }
521
522 if(hKl == (HKL)HKL_NEXT)
523 {
524 pKbl = (PKBL)pWThread->KeyboardLayout->List.Flink;
525 }
526 else if(hKl == (HKL)HKL_PREV)
527 {
528 pKbl = (PKBL)pWThread->KeyboardLayout->List.Blink;
529 }
530 else pKbl = UserHklToKbl(hKl);
531
532 //FIXME: KLF_RESET, KLF_SHIFTLOCK
533 //FIXME: KLF_SETFORPROCESS
534
535 if(pKbl)
536 {
537 if(Flags & KLF_REORDER)
538 KBLList = pKbl;
539
540 if(pKbl == pWThread->KeyboardLayout)
541 {
542 Ret = pKbl->hkl;
543 }
544 else
545 {
546 pKbl = co_UserActivateKbl(pWThread, pKbl);
547 Ret = pKbl->hkl;
548 }
549 }
550
551 the_end:
552 UserLeave();
553 return Ret;
554 }
555
556
557 /* EOF */