update for HEAD-2003091401
[reactos.git] / subsys / win32k / ntuser / keyboard.c
1 /*
2  *  ReactOS W32 Subsystem
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * COPYRIGHT:        See COPYING in the top level directory
22  * PROJECT:          ReactOS kernel
23  * PURPOSE:          Messages
24  * FILE:             subsys/win32k/ntuser/keyboard.c
25  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
26  * REVISION HISTORY:
27  *       06-06-2001  CSH  Created
28  */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <win32k/win32k.h>
34 #include <internal/safe.h>
35 #include <internal/kbd.h>
36 #include <include/guicheck.h>
37 #include <include/msgqueue.h>
38 #include <include/window.h>
39 #include <include/class.h>
40 #include <include/error.h>
41 #include <include/object.h>
42 #include <include/winsta.h>
43
44 #define NDEBUG
45 #include <debug.h>
46
47 DWORD ModBits = 0;
48 BYTE QueueKeyStateTable[256];
49 static PVOID pkKeyboardLayout = 0;
50
51 /* arty -- These should be phased out for the general kbdxx.dll tables */
52
53 struct accent_char
54 {
55     BYTE ac_accent;
56     BYTE ac_char;
57     BYTE ac_result;
58 };
59
60 static const struct accent_char accent_chars[] =
61 {
62 /* A good idea should be to read /usr/X11/lib/X11/locale/iso8859-x/Compose */
63     {'`', 'A', '\300'},  {'`', 'a', '\340'},
64     {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
65     {'^', 'A', '\302'},  {'^', 'a', '\342'},
66     {'~', 'A', '\303'},  {'~', 'a', '\343'},
67     {'"', 'A', '\304'},  {'"', 'a', '\344'},
68     {'O', 'A', '\305'},  {'o', 'a', '\345'},
69     {'0', 'A', '\305'},  {'0', 'a', '\345'},
70     {'A', 'A', '\305'},  {'a', 'a', '\345'},
71     {'A', 'E', '\306'},  {'a', 'e', '\346'},
72     {',', 'C', '\307'},  {',', 'c', '\347'},
73     {'`', 'E', '\310'},  {'`', 'e', '\350'},
74     {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
75     {'^', 'E', '\312'},  {'^', 'e', '\352'},
76     {'"', 'E', '\313'},  {'"', 'e', '\353'},
77     {'`', 'I', '\314'},  {'`', 'i', '\354'},
78     {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
79     {'^', 'I', '\316'},  {'^', 'i', '\356'},
80     {'"', 'I', '\317'},  {'"', 'i', '\357'},
81     {'-', 'D', '\320'},  {'-', 'd', '\360'},
82     {'~', 'N', '\321'},  {'~', 'n', '\361'},
83     {'`', 'O', '\322'},  {'`', 'o', '\362'},
84     {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
85     {'^', 'O', '\324'},  {'^', 'o', '\364'},
86     {'~', 'O', '\325'},  {'~', 'o', '\365'},
87     {'"', 'O', '\326'},  {'"', 'o', '\366'},
88     {'/', 'O', '\330'},  {'/', 'o', '\370'},
89     {'`', 'U', '\331'},  {'`', 'u', '\371'},
90     {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
91     {'^', 'U', '\333'},  {'^', 'u', '\373'},
92     {'"', 'U', '\334'},  {'"', 'u', '\374'},
93     {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
94     {'T', 'H', '\336'},  {'t', 'h', '\376'},
95     {'s', 's', '\337'},  {'"', 'y', '\377'},
96     {'s', 'z', '\337'},  {'i', 'j', '\377'},
97         /* iso-8859-2 uses this */
98     {'<', 'L', '\245'},  {'<', 'l', '\265'},    /* caron */
99     {'<', 'S', '\251'},  {'<', 's', '\271'},
100     {'<', 'T', '\253'},  {'<', 't', '\273'},
101     {'<', 'Z', '\256'},  {'<', 'z', '\276'},
102     {'<', 'C', '\310'},  {'<', 'c', '\350'},
103     {'<', 'E', '\314'},  {'<', 'e', '\354'},
104     {'<', 'D', '\317'},  {'<', 'd', '\357'},
105     {'<', 'N', '\322'},  {'<', 'n', '\362'},
106     {'<', 'R', '\330'},  {'<', 'r', '\370'},
107     {';', 'A', '\241'},  {';', 'a', '\261'},    /* ogonek */
108     {';', 'E', '\312'},  {';', 'e', '\332'},
109     {'\'', 'Z', '\254'}, {'\'', 'z', '\274'},   /* acute */
110     {'\'', 'R', '\300'}, {'\'', 'r', '\340'},
111     {'\'', 'L', '\305'}, {'\'', 'l', '\345'},
112     {'\'', 'C', '\306'}, {'\'', 'c', '\346'},
113     {'\'', 'N', '\321'}, {'\'', 'n', '\361'},
114 /*  collision whith S, from iso-8859-9 !!! */
115     {',', 'S', '\252'},  {',', 's', '\272'},    /* cedilla */
116     {',', 'T', '\336'},  {',', 't', '\376'},
117     {'.', 'Z', '\257'},  {'.', 'z', '\277'},    /* dot above */
118     {'/', 'L', '\243'},  {'/', 'l', '\263'},    /* slash */
119     {'/', 'D', '\320'},  {'/', 'd', '\360'},
120     {'(', 'A', '\303'},  {'(', 'a', '\343'},    /* breve */
121     {'\275', 'O', '\325'}, {'\275', 'o', '\365'},       /* double acute */
122     {'\275', 'U', '\334'}, {'\275', 'u', '\374'},
123     {'0', 'U', '\332'},  {'0', 'u', '\372'},    /* ring above */
124         /* iso-8859-3 uses this */
125     {'/', 'H', '\241'},  {'/', 'h', '\261'},    /* slash */
126     {'>', 'H', '\246'},  {'>', 'h', '\266'},    /* circumflex */
127     {'>', 'J', '\254'},  {'>', 'j', '\274'},
128     {'>', 'C', '\306'},  {'>', 'c', '\346'},
129     {'>', 'G', '\330'},  {'>', 'g', '\370'},
130     {'>', 'S', '\336'},  {'>', 's', '\376'},
131 /*  collision whith G( from iso-8859-9 !!!   */
132     {'(', 'G', '\253'},  {'(', 'g', '\273'},    /* breve */
133     {'(', 'U', '\335'},  {'(', 'u', '\375'},
134 /*  collision whith I. from iso-8859-3 !!!   */
135     {'.', 'I', '\251'},  {'.', 'i', '\271'},    /* dot above */
136     {'.', 'C', '\305'},  {'.', 'c', '\345'},
137     {'.', 'G', '\325'},  {'.', 'g', '\365'},
138         /* iso-8859-4 uses this */
139     {',', 'R', '\243'},  {',', 'r', '\263'},    /* cedilla */
140     {',', 'L', '\246'},  {',', 'l', '\266'},
141     {',', 'G', '\253'},  {',', 'g', '\273'},
142     {',', 'N', '\321'},  {',', 'n', '\361'},
143     {',', 'K', '\323'},  {',', 'k', '\363'},
144     {'~', 'I', '\245'},  {'~', 'i', '\265'},    /* tilde */
145     {'-', 'E', '\252'},  {'-', 'e', '\272'},    /* macron */
146     {'-', 'A', '\300'},  {'-', 'a', '\340'},
147     {'-', 'I', '\317'},  {'-', 'i', '\357'},
148     {'-', 'O', '\322'},  {'-', 'o', '\362'},
149     {'-', 'U', '\336'},  {'-', 'u', '\376'},
150     {'/', 'T', '\254'},  {'/', 't', '\274'},    /* slash */
151     {'.', 'E', '\314'},  {'.', 'e', '\344'},    /* dot above */
152     {';', 'I', '\307'},  {';', 'i', '\347'},    /* ogonek */
153     {';', 'U', '\331'},  {';', 'u', '\371'},
154         /* iso-8859-9 uses this */
155         /* iso-8859-9 has really bad choosen G( S, and I. as they collide
156          * whith the same letters on other iso-8859-x (that is they are on
157          * different places :-( ), if you use turkish uncomment these and
158          * comment out the lines in iso-8859-2 and iso-8859-3 sections
159          * FIXME: should be dynamic according to chosen language
160          *        if/when Wine has turkish support.  
161          */ 
162 /*  collision whith G( from iso-8859-3 !!!   */
163 /*  {'(', 'G', '\320'},  {'(', 'g', '\360'}, */ /* breve */
164 /*  collision whith S, from iso-8859-2 !!! */
165 /*  {',', 'S', '\336'},  {',', 's', '\376'}, */ /* cedilla */
166 /*  collision whith I. from iso-8859-3 !!!   */
167 /*  {'.', 'I', '\335'},  {'.', 'i', '\375'}, */ /* dot above */
168 };
169
170 /* FUNCTIONS *****************************************************************/
171
172 /*** Statics used by TranslateMessage ***/
173
174 static VOID STDCALL SetKeyState(DWORD key, BOOL down) {
175   if( key >= 'a' && key <= 'z' ) key += 'A' - 'a';
176   QueueKeyStateTable[key] = down;
177 }
178
179 static BOOL SetModKey( PKBDTABLES pkKT, WORD wVK, BOOL down ) {
180   int i;
181   
182   for( i = 0; pkKT->pCharModifiers->pVkToBit[i].Vk; i++ ) {
183     DbgPrint( "vk[%d] = { %04x, %x }\n", i, 
184         pkKT->pCharModifiers->pVkToBit[i].Vk,
185         pkKT->pCharModifiers->pVkToBit[i].ModBits );
186     if( pkKT->pCharModifiers->pVkToBit[i].Vk == wVK ) {
187       if( down ) ModBits |= pkKT->pCharModifiers->pVkToBit[i].ModBits;
188       else ModBits &= ~pkKT->pCharModifiers->pVkToBit[i].ModBits;
189       DbgPrint( "ModBits: %x\n", ModBits );
190       return TRUE;
191     }
192   }
193
194   return FALSE;
195 }
196
197 static BOOL TryToTranslateChar( WORD wVirtKey,
198                                 PVK_TO_WCHAR_TABLE vtwTbl, 
199                                 DWORD ModBits,
200                                 PBOOL pbDead,
201                                 PBOOL pbLigature,
202                                 PWCHAR pwcTranslatedChar ) {
203   int i,j;
204   size_t size_this_entry = vtwTbl->cbSize;
205   int nStates = vtwTbl->nModifications;
206   PVK_TO_WCHARS10 vkPtr;
207
208   for( i = 0;; i++ ) {
209     vkPtr = (PVK_TO_WCHARS10)
210       (((BYTE *)vtwTbl->pVkToWchars) + i * size_this_entry);
211
212     if( !vkPtr->VirtualKey ) return FALSE;
213     if( wVirtKey == vkPtr->VirtualKey ) {
214       for( j = 0; j < nStates; j++ ) {
215         if( j == (int) ModBits ) { /* OK, we found a wchar with the correct
216                                 shift state and vk */
217           *pbDead = vkPtr->wch[j] == WCH_DEAD;
218           *pbLigature = vkPtr->wch[j] == WCH_LGTR;
219           *pwcTranslatedChar = vkPtr->wch[j];
220           if( *pbDead ) {
221             i++;
222             vkPtr = (PVK_TO_WCHARS10)
223               (((BYTE *)vtwTbl->pVkToWchars) + i * size_this_entry);
224             if( vkPtr->VirtualKey != 0xff ) {
225               DPRINT( "Found dead key with no trailer in the table.\n" );
226               DPRINT( "VK: %04x, ADDR: %08x\n", wVirtKey, (int)vkPtr );
227               return FALSE;
228             }
229             *pwcTranslatedChar = vkPtr->wch[j];
230           }
231           return TRUE;
232         }
233       }
234     }
235   }
236 }
237
238 static
239 int STDCALL
240 ToUnicodeInner(UINT wVirtKey,
241                UINT wScanCode,
242                PBYTE lpKeyState,
243                LPWSTR pwszBuff,
244                int cchBuff,
245                UINT wFlags,
246                DWORD ModBits,
247                PKBDTABLES pkKT)
248 {
249   int i;
250
251   DbgPrint("wVirtKey=%08x, wScanCode=%08x, lpKeyState=[], "
252            "pwszBuff=%S, cchBuff=%d, wFlags=%x\n",
253            wVirtKey, wScanCode, /* lpKeyState, */ pwszBuff,
254            cchBuff, wFlags );
255
256   for( i = 0; pkKT->pVkToWcharTable[i].nModifications; i++ ) {
257     WCHAR wcTranslatedChar;
258     BOOL bDead;
259     BOOL bLigature;
260
261     if( TryToTranslateChar( wVirtKey,
262                             &pkKT->pVkToWcharTable[i], 
263                             ModBits,
264                             &bDead,
265                             &bLigature,
266                             &wcTranslatedChar ) ) {
267       if( bLigature ) {
268         DPRINT("Not handling ligature (yet)\n" );
269         return 0;
270       }
271
272       if( cchBuff > 0 ) pwszBuff[0] = wcTranslatedChar;
273
274       if( bDead ) return -1;
275       else return 1;
276     }
277   }
278
279   return 0;
280 }
281
282 DWORD
283 STDCALL
284 NtUserGetKeyState(
285   DWORD key)
286 {
287   DWORD ret;
288
289     if (key >= 'a' && key <= 'z') key += 'A' - 'a';
290     ret = ((DWORD)(QueueKeyStateTable[key] & 0x80) << 8 ) |
291               (QueueKeyStateTable[key] & 0x80) |
292               (QueueKeyStateTable[key] & 0x01);
293     return ret;
294 }
295
296 int STDCALL ToUnicode( UINT wVirtKey,
297                        UINT wScanCode,
298                        PBYTE lpKeyState,
299                        LPWSTR pwszBuff,
300                        int cchBuff,
301                        UINT wFlags ) {
302   return ToUnicodeInner( wVirtKey,
303                          wScanCode,
304                          QueueKeyStateTable,
305                          pwszBuff,
306                          cchBuff,
307                          wFlags,
308                          ModBits,
309                          pkKeyboardLayout );
310 }
311
312 typedef PVOID (*KbdLayerDescriptor)(VOID);
313 NTSTATUS STDCALL LdrGetProcedureAddress(PVOID module,
314                                         PANSI_STRING import_name,
315                                         DWORD flags,
316                                         PVOID *func_addr);
317
318 void InitKbdLayout( PVOID *pkKeyboardLayout ) {
319   HMODULE kbModule = 0;
320   ANSI_STRING kbdProcedureName;
321   //NTSTATUS Status;
322
323   KbdLayerDescriptor layerDescGetFn;
324
325   kbModule = EngLoadImage(L"\\SystemRoot\\system32\\kbdus.dll");
326
327   if( !kbModule ) {
328     DbgPrint( "Foo: No kbdus.dll\n" );
329     return;
330   }
331
332   RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
333   LdrGetProcedureAddress((PVOID)kbModule,
334                          &kbdProcedureName,
335                          0,
336                          (PVOID*)&layerDescGetFn);
337   if( layerDescGetFn ) {
338     *pkKeyboardLayout = layerDescGetFn();
339   }
340 }
341
342 BOOL STDCALL
343 NtUserTranslateMessage(LPMSG lpMsg,
344                        DWORD Unknown1) /* Used to pass the kbd layout */
345 {
346   static INT dead_char = 0;
347   LONG UState = 0;
348   WCHAR wp[2] = { 0 };
349   MSG NewMsg = { 0 };
350   PUSER_MESSAGE UMsg;
351
352   /* FIXME: Should pass current keyboard layout for this thread. */
353   /* At the moment, the keyboard layout is global. */
354   /* Also, we're fixed at kbdus.dll ... */
355   if( !pkKeyboardLayout ) InitKbdLayout( &pkKeyboardLayout );
356   if( !pkKeyboardLayout ) {
357     DbgPrint( "Not Translating due to empty layout.\n" );
358     return FALSE;
359   }
360
361   if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
362     {
363       if (lpMsg->message == WM_KEYUP) {
364         DbgPrint( "About to SetKeyState( %04x, FALSE );\n", lpMsg->wParam );
365         SetKeyState( lpMsg->wParam, FALSE ); /* Release key */
366         DbgPrint( "About to SetModKey();\n" );
367         SetModKey( pkKeyboardLayout, lpMsg->wParam, FALSE );
368         /* Release Mod if any */
369         DbgPrint( "Done with keys.\n" );
370       }
371       return(FALSE);
372     }
373
374   DbgPrint( "About to SetKeyState( %04x, TRUE );\n", lpMsg->wParam );
375   SetKeyState( lpMsg->wParam, TRUE ); /* Strike key */
376
377   /* Pass 1: Search for modifiers */
378   DbgPrint( "About to SetModKey();\n" );
379   if( SetModKey( pkKeyboardLayout, lpMsg->wParam, TRUE ) ) return TRUE;
380   DbgPrint( "Done with keys.\n" );
381
382   /* Pass 2: Get Unicode Character */
383   DbgPrint( "Calling ToUnicodeString()\n" );
384   UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam),
385                           QueueKeyStateTable, wp, 2, 0, ModBits,
386                           pkKeyboardLayout);
387
388   DbgPrint( "UState is %d after key %04x\n", UState, wp[0] );
389   if (UState == 1)
390     {
391       NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
392       if (dead_char)
393         {
394           ULONG i;
395           
396           if (wp[0] == ' ') wp[0] =  dead_char;
397           if (dead_char == 0xa2) dead_char = '(';
398           else if (dead_char == 0xa8) dead_char = '"';
399           else if (dead_char == 0xb2) dead_char = ';';
400           else if (dead_char == 0xb4) dead_char = '\'';
401           else if (dead_char == 0xb7) dead_char = '<';
402           else if (dead_char == 0xb8) dead_char = ',';
403           else if (dead_char == 0xff) dead_char = '.';
404           for (i = 0; i < sizeof(accent_chars)/sizeof(accent_chars[0]); i++)
405             {
406               if ((accent_chars[i].ac_accent == dead_char) &&
407                   (accent_chars[i].ac_char == wp[0]))
408                 {
409                   wp[0] = accent_chars[i].ac_result;
410                   break;
411                 }
412               dead_char = 0;
413             }
414         }
415       NewMsg.hwnd = lpMsg->hwnd;
416       NewMsg.wParam = wp[0];
417       NewMsg.lParam = lpMsg->lParam;
418       UMsg = MsqCreateMessage(&NewMsg);
419       DbgPrint( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
420       MsqPostMessage(PsGetWin32Thread()->MessageQueue, UMsg);
421       return(TRUE);
422     }
423   else if (UState == -1)
424     {
425       NewMsg.message = 
426         (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
427       NewMsg.hwnd = lpMsg->hwnd;
428       NewMsg.wParam = wp[0];
429       NewMsg.lParam = lpMsg->lParam;
430       dead_char = wp[0];
431       UMsg = MsqCreateMessage(&NewMsg);
432       MsqPostMessage(PsGetWin32Thread()->MessageQueue, UMsg);
433       return(TRUE);
434     }
435   return(FALSE);
436 }
437
438 HWND STDCALL
439 NtUserSetFocus(HWND hWnd)
440 {
441   return IntSetFocusWindow(hWnd);
442 }
443
444 DWORD
445 STDCALL
446 NtUserGetKeyboardState(
447   LPBYTE lpKeyState)
448 {
449   if (lpKeyState) {
450         if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, QueueKeyStateTable, 256)))
451                 return FALSE;
452   }
453   return TRUE;
454 }
455
456 DWORD
457 STDCALL
458 NtUserSetKeyboardState(
459   LPBYTE lpKeyState)
460 {
461         if (lpKeyState) {
462                 if(! NT_SUCCESS(MmCopyFromCaller(QueueKeyStateTable, lpKeyState, 256)))
463                         return FALSE;
464         }
465     return TRUE;
466 }
467
468
469 /* EOF */