Yet another batch of French resources updates.
[wine] / windows / input.c
1 /*
2  * USER Input processing
3  *
4  * Copyright 1993 Bob Amstadt
5  * Copyright 1996 Albrecht Kleine
6  * Copyright 1997 David Faure
7  * Copyright 1998 Morten Welinder
8  * Copyright 1998 Ulrich Weigand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <assert.h>
31
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "wine/winbase16.h"
40 #include "wine/winuser16.h"
41 #include "wine/server.h"
42 #include "win.h"
43 #include "message.h"
44 #include "winternl.h"
45 #include "wine/debug.h"
46 #include "winerror.h"
47
48 WINE_DECLARE_DEBUG_CHANNEL(key);
49 WINE_DECLARE_DEBUG_CHANNEL(keyboard);
50 WINE_DECLARE_DEBUG_CHANNEL(win);
51 WINE_DEFAULT_DEBUG_CHANNEL(event);
52
53 static BOOL InputEnabled = TRUE;
54 static BOOL SwappedButtons;
55
56 BYTE InputKeyStateTable[256];
57 BYTE AsyncKeyStateTable[256];
58
59 /* Storage for the USER-maintained mouse positions */
60 static DWORD PosX, PosY;
61
62 typedef union
63 {
64     struct
65     {
66         unsigned long count : 16;
67         unsigned long code : 8;
68         unsigned long extended : 1;
69         unsigned long unused : 2;
70         unsigned long win_internal : 2;
71         unsigned long context : 1;
72         unsigned long previous : 1;
73         unsigned long transition : 1;
74     } lp1;
75     unsigned long lp2;
76 } KEYLP;
77
78
79 /***********************************************************************
80  *           get_key_state
81  */
82 static WORD get_key_state(void)
83 {
84     WORD ret = 0;
85
86     if (SwappedButtons)
87     {
88         if (InputKeyStateTable[VK_RBUTTON] & 0x80) ret |= MK_LBUTTON;
89         if (InputKeyStateTable[VK_LBUTTON] & 0x80) ret |= MK_RBUTTON;
90     }
91     else
92     {
93         if (InputKeyStateTable[VK_LBUTTON] & 0x80) ret |= MK_LBUTTON;
94         if (InputKeyStateTable[VK_RBUTTON] & 0x80) ret |= MK_RBUTTON;
95     }
96     if (InputKeyStateTable[VK_MBUTTON] & 0x80)  ret |= MK_MBUTTON;
97     if (InputKeyStateTable[VK_SHIFT] & 0x80)    ret |= MK_SHIFT;
98     if (InputKeyStateTable[VK_CONTROL] & 0x80)  ret |= MK_CONTROL;
99     if (InputKeyStateTable[VK_XBUTTON1] & 0x80) ret |= MK_XBUTTON1;
100     if (InputKeyStateTable[VK_XBUTTON2] & 0x80) ret |= MK_XBUTTON2;
101     return ret;
102 }
103
104
105 /***********************************************************************
106  *           queue_hardware_message
107  *
108  * Add a message to the hardware queue.
109  * Note: the position is relative to the desktop window.
110  */
111 static void queue_hardware_message( UINT message, HWND hwnd, WPARAM wParam, LPARAM lParam,
112                                     int xPos, int yPos, DWORD time, ULONG_PTR extraInfo )
113 {
114     SERVER_START_REQ( send_message )
115     {
116         req->id       = GetCurrentThreadId();
117         req->type     = MSG_HARDWARE;
118         req->flags    = 0;
119         req->win      = hwnd;
120         req->msg      = message;
121         req->wparam   = wParam;
122         req->lparam   = lParam;
123         req->x        = xPos;
124         req->y        = yPos;
125         req->time     = time;
126         req->info     = extraInfo;
127         req->timeout  = -1;
128         req->callback = NULL;
129         wine_server_call( req );
130     }
131     SERVER_END_REQ;
132 }
133
134
135 /***********************************************************************
136  *           queue_kbd_event
137  *
138  * Put a keyboard event into a thread queue
139  */
140 static void queue_kbd_event( const KEYBDINPUT *ki, UINT injected_flags )
141 {
142     UINT message;
143     KEYLP keylp;
144     KBDLLHOOKSTRUCT hook;
145
146     keylp.lp2 = 0;
147     keylp.lp1.count = 1;
148     keylp.lp1.code = ki->wScan;
149     keylp.lp1.extended = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0;
150     keylp.lp1.win_internal = 0; /* this has something to do with dialogs,
151                                 * don't remember where I read it - AK */
152                                 /* it's '1' under windows, when a dialog box appears
153                                  * and you press one of the underlined keys - DF*/
154
155     if (ki->dwFlags & KEYEVENTF_KEYUP )
156     {
157         BOOL sysKey = (InputKeyStateTable[VK_MENU] & 0x80) &&
158                       !(InputKeyStateTable[VK_CONTROL] & 0x80);
159         InputKeyStateTable[ki->wVk] &= ~0x80;
160         keylp.lp1.previous = 1;
161         keylp.lp1.transition = 1;
162         message = sysKey ? WM_SYSKEYUP : WM_KEYUP;
163     }
164     else
165     {
166         keylp.lp1.previous = (InputKeyStateTable[ki->wVk] & 0x80) != 0;
167         keylp.lp1.transition = 0;
168         if (!(InputKeyStateTable[ki->wVk] & 0x80)) InputKeyStateTable[ki->wVk] ^= 0x01;
169         InputKeyStateTable[ki->wVk] |= 0x80;
170         AsyncKeyStateTable[ki->wVk] |= 0x80;
171
172         message = (InputKeyStateTable[VK_MENU] & 0x80) && !(InputKeyStateTable[VK_CONTROL] & 0x80)
173               ? WM_SYSKEYDOWN : WM_KEYDOWN;
174     }
175
176     keylp.lp1.context = (InputKeyStateTable[VK_MENU] & 0x80) != 0; /* 1 if alt */
177
178     TRACE_(key)(" wParam=%04x, lParam=%08lx, InputKeyState=%x\n",
179                 ki->wVk, keylp.lp2, InputKeyStateTable[ki->wVk] );
180
181     hook.vkCode      = ki->wVk;
182     hook.scanCode    = ki->wScan;
183     hook.flags       = (keylp.lp2 >> 24) | injected_flags;
184     hook.time        = ki->time;
185     hook.dwExtraInfo = ki->dwExtraInfo;
186     if (!HOOK_CallHooks( WH_KEYBOARD_LL, HC_ACTION, message, (LPARAM)&hook, TRUE ))
187         queue_hardware_message( message, 0, ki->wVk, keylp.lp2,
188                                 PosX, PosY, ki->time, ki->dwExtraInfo );
189 }
190
191
192 /***********************************************************************
193  *           queue_raw_mouse_message
194  */
195 static void queue_raw_mouse_message( UINT message, UINT flags, INT x, INT y, const MOUSEINPUT *mi )
196 {
197     MSLLHOOKSTRUCT hook;
198
199     hook.pt.x        = x;
200     hook.pt.y        = y;
201     hook.mouseData   = MAKELONG( 0, mi->mouseData );
202     hook.flags       = flags;
203     hook.time        = mi->time;
204     hook.dwExtraInfo = mi->dwExtraInfo;
205
206     if (!HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, message, (LPARAM)&hook, TRUE ))
207         queue_hardware_message( message, (HWND)mi->dwExtraInfo /*FIXME*/,
208                                 MAKEWPARAM( get_key_state(), mi->mouseData ),
209                                 0, x, y, mi->time, mi->dwExtraInfo );
210 }
211
212
213 /***********************************************************************
214  *              queue_mouse_event
215  */
216 static void queue_mouse_event( const MOUSEINPUT *mi, UINT flags )
217 {
218     if (mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
219     {
220         PosX = (mi->dx * GetSystemMetrics(SM_CXSCREEN)) >> 16;
221         PosY = (mi->dy * GetSystemMetrics(SM_CYSCREEN)) >> 16;
222     }
223     else if (mi->dwFlags & MOUSEEVENTF_MOVE)
224     {
225         int width  = GetSystemMetrics(SM_CXSCREEN);
226         int height = GetSystemMetrics(SM_CYSCREEN);
227         long posX = (long) PosX, posY = (long) PosY;
228         int accel[3];
229         int accelMult;
230
231         /* dx and dy can be negative numbers for relative movements */
232         SystemParametersInfoA(SPI_GETMOUSE, 0, accel, 0);
233
234         accelMult = 1;
235         if (mi->dx > accel[0] && accel[2] != 0)
236         {
237             accelMult = 2;
238             if ((mi->dx > accel[1]) && (accel[2] == 2))
239             {
240                 accelMult = 4;
241             }
242         }
243         posX += (long)mi->dx * accelMult;
244
245         accelMult = 1;
246         if (mi->dy > accel[0] && accel[2] != 0)
247         {
248             accelMult = 2;
249             if ((mi->dy > accel[1]) && (accel[2] == 2))
250             {
251                 accelMult = 4;
252             }
253         }
254         posY += (long)mi->dy * accelMult;
255
256         /* Clip to the current screen size */
257         if (posX < 0) PosX = 0;
258         else if (posX >= width) PosX = width - 1;
259         else PosX = posX;
260
261         if (posY < 0) PosY = 0;
262         else if (posY >= height) PosY = height - 1;
263         else PosY = posY;
264     }
265
266     if (mi->dwFlags & MOUSEEVENTF_MOVE)
267     {
268         queue_raw_mouse_message( WM_MOUSEMOVE, flags, PosX, PosY, mi );
269     }
270     if (mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
271     {
272         InputKeyStateTable[VK_LBUTTON] |= 0x80;
273         AsyncKeyStateTable[VK_LBUTTON] |= 0x80;
274         queue_raw_mouse_message( SwappedButtons ? WM_RBUTTONDOWN : WM_LBUTTONDOWN,
275                                  flags, PosX, PosY, mi );
276     }
277     if (mi->dwFlags & MOUSEEVENTF_LEFTUP)
278     {
279         InputKeyStateTable[VK_LBUTTON] &= ~0x80;
280         queue_raw_mouse_message( SwappedButtons ? WM_RBUTTONUP : WM_LBUTTONUP,
281                                  flags, PosX, PosY, mi );
282     }
283     if (mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
284     {
285         InputKeyStateTable[VK_RBUTTON] |= 0x80;
286         AsyncKeyStateTable[VK_RBUTTON] |= 0x80;
287         queue_raw_mouse_message( SwappedButtons ? WM_LBUTTONDOWN : WM_RBUTTONDOWN,
288                                  flags, PosX, PosY, mi );
289     }
290     if (mi->dwFlags & MOUSEEVENTF_RIGHTUP)
291     {
292         InputKeyStateTable[VK_RBUTTON] &= ~0x80;
293         queue_raw_mouse_message( SwappedButtons ? WM_LBUTTONUP : WM_RBUTTONUP,
294                                  flags, PosX, PosY, mi );
295     }
296     if (mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
297     {
298         InputKeyStateTable[VK_MBUTTON] |= 0x80;
299         AsyncKeyStateTable[VK_MBUTTON] |= 0x80;
300         queue_raw_mouse_message( WM_MBUTTONDOWN, flags, PosX, PosY, mi );
301     }
302     if (mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
303     {
304         InputKeyStateTable[VK_MBUTTON] &= ~0x80;
305         queue_raw_mouse_message( WM_MBUTTONUP, flags, PosX, PosY, mi );
306     }
307     if (mi->dwFlags & MOUSEEVENTF_WHEEL)
308     {
309         queue_raw_mouse_message( WM_MOUSEWHEEL, flags, PosX, PosY, mi );
310     }
311     if (flags & LLMHF_INJECTED)  /* we have to actually move the cursor */
312         SetCursorPos( PosX, PosY );
313 }
314
315
316 /***********************************************************************
317  *              SendInput  (USER32.@)
318  */
319 UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size )
320 {
321     UINT i;
322
323     if (!InputEnabled) return 0;
324
325     for (i = 0; i < count; i++, inputs++)
326     {
327         switch(inputs->type)
328         {
329         case INPUT_MOUSE:
330             queue_mouse_event( &inputs->u.mi, LLMHF_INJECTED );
331             break;
332         case WINE_INTERNAL_INPUT_MOUSE:
333             queue_mouse_event( &inputs->u.mi, 0 );
334             break;
335         case INPUT_KEYBOARD:
336             queue_kbd_event( &inputs->u.ki, LLKHF_INJECTED );
337             break;
338         case WINE_INTERNAL_INPUT_KEYBOARD:
339             queue_kbd_event( &inputs->u.ki, 0 );
340             break;
341         case INPUT_HARDWARE:
342             FIXME( "INPUT_HARDWARE not supported\n" );
343             break;
344         }
345     }
346     return count;
347 }
348
349
350 /***********************************************************************
351  *              keybd_event (USER32.@)
352  */
353 void WINAPI keybd_event( BYTE bVk, BYTE bScan,
354                          DWORD dwFlags, ULONG_PTR dwExtraInfo )
355 {
356     INPUT input;
357
358     input.type = INPUT_KEYBOARD;
359     input.u.ki.wVk = bVk;
360     input.u.ki.wScan = bScan;
361     input.u.ki.dwFlags = dwFlags;
362     input.u.ki.time = GetTickCount();
363     input.u.ki.dwExtraInfo = dwExtraInfo;
364     SendInput( 1, &input, sizeof(input) );
365 }
366
367
368 /***********************************************************************
369  *              keybd_event (USER.289)
370  */
371 void WINAPI keybd_event16( CONTEXT86 *context )
372 {
373     DWORD dwFlags = 0;
374
375     if (HIBYTE(context->Eax) & 0x80) dwFlags |= KEYEVENTF_KEYUP;
376     if (HIBYTE(context->Ebx) & 0x01) dwFlags |= KEYEVENTF_EXTENDEDKEY;
377
378     keybd_event( LOBYTE(context->Eax), LOBYTE(context->Ebx),
379                  dwFlags, MAKELONG(LOWORD(context->Esi), LOWORD(context->Edi)) );
380 }
381
382
383 /***********************************************************************
384  *              mouse_event (USER32.@)
385  */
386 void WINAPI mouse_event( DWORD dwFlags, DWORD dx, DWORD dy,
387                          DWORD dwData, ULONG_PTR dwExtraInfo )
388 {
389     INPUT input;
390
391     input.type = INPUT_MOUSE;
392     input.u.mi.dx = dx;
393     input.u.mi.dy = dy;
394     input.u.mi.mouseData = dwData;
395     input.u.mi.dwFlags = dwFlags;
396     input.u.mi.time = GetCurrentTime();
397     input.u.mi.dwExtraInfo = dwExtraInfo;
398     SendInput( 1, &input, sizeof(input) );
399 }
400
401
402 /***********************************************************************
403  *              mouse_event (USER.299)
404  */
405 void WINAPI mouse_event16( CONTEXT86 *context )
406 {
407     mouse_event( LOWORD(context->Eax), LOWORD(context->Ebx), LOWORD(context->Ecx),
408                  LOWORD(context->Edx), MAKELONG(context->Esi, context->Edi) );
409 }
410
411 /***********************************************************************
412  *              GetMouseEventProc (USER.337)
413  */
414 FARPROC16 WINAPI GetMouseEventProc16(void)
415 {
416     HMODULE16 hmodule = GetModuleHandle16("USER");
417     return GetProcAddress16( hmodule, "mouse_event" );
418 }
419
420
421 /**********************************************************************
422  *              EnableHardwareInput (USER.331)
423  */
424 BOOL16 WINAPI EnableHardwareInput16(BOOL16 bEnable)
425 {
426   BOOL16 bOldState = InputEnabled;
427   FIXME_(event)("(%d) - stub\n", bEnable);
428   InputEnabled = bEnable;
429   return bOldState;
430 }
431
432
433 /***********************************************************************
434  *              SwapMouseButton (USER.186)
435  */
436 BOOL16 WINAPI SwapMouseButton16( BOOL16 fSwap )
437 {
438     BOOL16 ret = SwappedButtons;
439     SwappedButtons = fSwap;
440     return ret;
441 }
442
443
444 /***********************************************************************
445  *              SwapMouseButton (USER32.@)
446  */
447 BOOL WINAPI SwapMouseButton( BOOL fSwap )
448 {
449     BOOL ret = SwappedButtons;
450     SwappedButtons = fSwap;
451     return ret;
452 }
453
454
455 /***********************************************************************
456  *              GetCursorPos (USER.17)
457  */
458 BOOL16 WINAPI GetCursorPos16( POINT16 *pt )
459 {
460     POINT pos;
461     if (!pt) return 0;
462     GetCursorPos(&pos);
463     pt->x = pos.x;
464     pt->y = pos.y;
465     return 1;
466 }
467
468
469 /***********************************************************************
470  *              GetCursorPos (USER32.@)
471  */
472 BOOL WINAPI GetCursorPos( POINT *pt )
473 {
474     if (!pt) return 0;
475     pt->x = PosX;
476     pt->y = PosY;
477     if (USER_Driver.pGetCursorPos) USER_Driver.pGetCursorPos( pt );
478     return 1;
479 }
480
481
482 /***********************************************************************
483  *              GetCursorInfo (USER32.@)
484  */
485 BOOL WINAPI GetCursorInfo( PCURSORINFO pci )
486 {
487     MESSAGEQUEUE *queue = QUEUE_Current();
488
489     if (!pci) return 0;
490     if (queue->cursor_count >= 0) pci->flags = CURSOR_SHOWING;
491     else pci->flags = 0;
492     GetCursorPos(&pci->ptScreenPos);
493     return 1;
494 }
495
496
497 /***********************************************************************
498  *              SetCursorPos (USER.70)
499  */
500 void WINAPI SetCursorPos16( INT16 x, INT16 y )
501 {
502     SetCursorPos( x, y );
503 }
504
505
506 /***********************************************************************
507  *              SetCursorPos (USER32.@)
508  */
509 BOOL WINAPI SetCursorPos( INT x, INT y )
510 {
511     if (USER_Driver.pSetCursorPos) USER_Driver.pSetCursorPos( x, y );
512     PosX = x;
513     PosY = y;
514     return TRUE;
515 }
516
517
518 /**********************************************************************
519  *              SetCapture (USER32.@)
520  */
521 HWND WINAPI SetCapture( HWND hwnd )
522 {
523     HWND previous = 0;
524
525     SERVER_START_REQ( set_capture_window )
526     {
527         req->handle = hwnd;
528         req->flags  = 0;
529         if (!wine_server_call_err( req ))
530         {
531             previous = reply->previous;
532             hwnd = reply->full_handle;
533         }
534     }
535     SERVER_END_REQ;
536
537     if (previous && previous != hwnd)
538         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
539     return previous;
540 }
541
542
543 /**********************************************************************
544  *              ReleaseCapture (USER32.@)
545  */
546 BOOL WINAPI ReleaseCapture(void)
547 {
548     return (SetCapture(0) != 0);
549 }
550
551
552 /**********************************************************************
553  *              GetCapture (USER32.@)
554  */
555 HWND WINAPI GetCapture(void)
556 {
557     HWND ret = 0;
558
559     SERVER_START_REQ( get_thread_input )
560     {
561         req->tid = GetCurrentThreadId();
562         if (!wine_server_call_err( req )) ret = reply->capture;
563     }
564     SERVER_END_REQ;
565     return ret;
566 }
567
568
569 /**********************************************************************
570  *              GetAsyncKeyState (USER32.@)
571  *
572  *      Determine if a key is or was pressed.  retval has high-order
573  * bit set to 1 if currently pressed, low-order bit set to 1 if key has
574  * been pressed.
575  *
576  *      This uses the variable AsyncMouseButtonsStates and
577  * AsyncKeyStateTable (set in event.c) which have the mouse button
578  * number or key number (whichever is applicable) set to true if the
579  * mouse or key had been depressed since the last call to
580  * GetAsyncKeyState.
581  */
582 SHORT WINAPI GetAsyncKeyState(INT nKey)
583 {
584     SHORT retval = ((AsyncKeyStateTable[nKey] & 0x80) ? 0x0001 : 0) |
585                    ((InputKeyStateTable[nKey] & 0x80) ? 0x8000 : 0);
586     AsyncKeyStateTable[nKey] = 0;
587     TRACE_(key)("(%x) -> %x\n", nKey, retval);
588     return retval;
589 }
590
591 /**********************************************************************
592  *              GetAsyncKeyState (USER.249)
593  */
594 INT16 WINAPI GetAsyncKeyState16(INT16 nKey)
595 {
596     return GetAsyncKeyState(nKey);
597 }
598
599 /***********************************************************************
600  *              IsUserIdle (USER.333)
601  */
602 BOOL16 WINAPI IsUserIdle16(void)
603 {
604     if ( GetAsyncKeyState( VK_LBUTTON ) & 0x8000 )
605         return FALSE;
606
607     if ( GetAsyncKeyState( VK_RBUTTON ) & 0x8000 )
608         return FALSE;
609
610     if ( GetAsyncKeyState( VK_MBUTTON ) & 0x8000 )
611         return FALSE;
612
613     /* Should check for screen saver activation here ... */
614
615     return TRUE;
616 }
617
618 /**********************************************************************
619  *              VkKeyScanA (USER32.@)
620  *
621  * VkKeyScan translates an ANSI character to a virtual-key and shift code
622  * for the current keyboard.
623  * high-order byte yields :
624  *      0       Unshifted
625  *      1       Shift
626  *      2       Ctrl
627  *      3-5     Shift-key combinations that are not used for characters
628  *      6       Ctrl-Alt
629  *      7       Ctrl-Alt-Shift
630  *      I.e. :  Shift = 1, Ctrl = 2, Alt = 4.
631  * FIXME : works ok except for dead chars :
632  * VkKeyScan '^'(0x5e, 94) ... got keycode 00 ... returning 00
633  * VkKeyScan '`'(0x60, 96) ... got keycode 00 ... returning 00
634  */
635 WORD WINAPI VkKeyScanA(CHAR cChar)
636 {
637     return USER_Driver.pVkKeyScan( cChar );
638 }
639
640 /******************************************************************************
641  *              VkKeyScanW (USER32.@)
642  */
643 WORD WINAPI VkKeyScanW(WCHAR cChar)
644 {
645         return VkKeyScanA((CHAR)cChar); /* FIXME: check unicode */
646 }
647
648 /**********************************************************************
649  *              VkKeyScanExA (USER32.@)
650  */
651 WORD WINAPI VkKeyScanExA(CHAR cChar, HKL dwhkl)
652 {
653     /* FIXME: complete workaround this is */
654     return VkKeyScanA(cChar);
655 }
656
657 /******************************************************************************
658  *              VkKeyScanExW (USER32.@)
659  */
660 WORD WINAPI VkKeyScanExW(WCHAR cChar, HKL dwhkl)
661 {
662     /* FIXME: complete workaround this is */
663     return VkKeyScanA((CHAR)cChar); /* FIXME: check unicode */
664 }
665
666 /******************************************************************************
667  *              GetKeyboardType (USER32.@)
668  */
669 INT WINAPI GetKeyboardType(INT nTypeFlag)
670 {
671     TRACE_(keyboard)("(%d)\n", nTypeFlag);
672     switch(nTypeFlag)
673     {
674     case 0:      /* Keyboard type */
675         return 4;    /* AT-101 */
676     case 1:      /* Keyboard Subtype */
677         return 0;    /* There are no defined subtypes */
678     case 2:      /* Number of F-keys */
679         return 12;   /* We're doing an 101 for now, so return 12 F-keys */
680     default:
681         WARN_(keyboard)("Unknown type\n");
682         return 0;    /* The book says 0 here, so 0 */
683     }
684 }
685
686 /******************************************************************************
687  *              MapVirtualKeyA (USER32.@)
688  */
689 UINT WINAPI MapVirtualKeyA(UINT code, UINT maptype)
690 {
691     return USER_Driver.pMapVirtualKey( code, maptype );
692 }
693
694 /******************************************************************************
695  *              MapVirtualKeyW (USER32.@)
696  */
697 UINT WINAPI MapVirtualKeyW(UINT code, UINT maptype)
698 {
699     return MapVirtualKeyA(code,maptype);
700 }
701
702 /******************************************************************************
703  *              MapVirtualKeyExA (USER32.@)
704  */
705 UINT WINAPI MapVirtualKeyExA(UINT code, UINT maptype, HKL hkl)
706 {
707     if (hkl)
708         FIXME_(keyboard)("(%d,%d,0x%08lx), hkl unhandled!\n",code,maptype,(DWORD)hkl);
709     return MapVirtualKeyA(code,maptype);
710 }
711
712 /******************************************************************************
713  *              MapVirtualKeyExW (USER32.@)
714  */
715 UINT WINAPI MapVirtualKeyExW(UINT code, UINT maptype, HKL hkl)
716 {
717     if (hkl)
718         FIXME_(keyboard)("(%d,%d,0x%08lx), hkl unhandled!\n",code,maptype,(DWORD)hkl);
719     return MapVirtualKeyA(code,maptype);
720 }
721
722 /****************************************************************************
723  *              GetKBCodePage (USER32.@)
724  */
725 UINT WINAPI GetKBCodePage(void)
726 {
727     return GetOEMCP();
728 }
729
730 /****************************************************************************
731  *              GetKeyboardLayoutName (USER.477)
732  */
733 INT16 WINAPI GetKeyboardLayoutName16(LPSTR pwszKLID)
734 {
735         return GetKeyboardLayoutNameA(pwszKLID);
736 }
737
738 /***********************************************************************
739  *              GetKeyboardLayout (USER32.@)
740  *
741  * FIXME: - device handle for keyboard layout defaulted to
742  *          the language id. This is the way Windows default works.
743  *        - the thread identifier (dwLayout) is also ignored.
744  */
745 HKL WINAPI GetKeyboardLayout(DWORD dwLayout)
746 {
747         UINT layout;
748         layout = GetSystemDefaultLCID(); /* FIXME */
749         layout |= (layout<<16);          /* FIXME */
750         TRACE_(keyboard)("returning %08x\n",layout);
751         return (HKL)layout;
752 }
753
754 /****************************************************************************
755  *              GetKeyboardLayoutNameA (USER32.@)
756  */
757 INT WINAPI GetKeyboardLayoutNameA(LPSTR pwszKLID)
758 {
759         sprintf(pwszKLID, "%p",GetKeyboardLayout(0));
760         return 1;
761 }
762
763 /****************************************************************************
764  *              GetKeyboardLayoutNameW (USER32.@)
765  */
766 INT WINAPI GetKeyboardLayoutNameW(LPWSTR pwszKLID)
767 {
768         char buf[KL_NAMELENGTH];
769         int res = GetKeyboardLayoutNameA(buf);
770         MultiByteToWideChar( CP_ACP, 0, buf, -1, pwszKLID, KL_NAMELENGTH );
771         return res;
772 }
773
774 /****************************************************************************
775  *              GetKeyNameTextA (USER32.@)
776  */
777 INT WINAPI GetKeyNameTextA(LONG lParam, LPSTR lpBuffer, INT nSize)
778 {
779     return USER_Driver.pGetKeyNameText( lParam, lpBuffer, nSize );
780 }
781
782 /****************************************************************************
783  *              GetKeyNameTextW (USER32.@)
784  */
785 INT WINAPI GetKeyNameTextW(LONG lParam, LPWSTR lpBuffer, INT nSize)
786 {
787         int res;
788         LPSTR buf = HeapAlloc( GetProcessHeap(), 0, nSize );
789         if(buf == NULL) return 0; /* FIXME: is this the correct failure value?*/
790         res = GetKeyNameTextA(lParam,buf,nSize);
791
792         if (nSize > 0 && !MultiByteToWideChar( CP_ACP, 0, buf, -1, lpBuffer, nSize ))
793             lpBuffer[nSize-1] = 0;
794         HeapFree( GetProcessHeap(), 0, buf );
795         return res;
796 }
797
798 /****************************************************************************
799  *              ToUnicode (USER32.@)
800  */
801 INT WINAPI ToUnicode(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
802                      LPWSTR lpwStr, int size, UINT flags)
803 {
804     return USER_Driver.pToUnicode(virtKey, scanCode, lpKeyState, lpwStr, size, flags);
805 }
806
807 /****************************************************************************
808  *              ToUnicodeEx (USER32.@)
809  */
810 INT WINAPI ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
811                        LPWSTR lpwStr, int size, UINT flags, HKL hkl)
812 {
813     /* FIXME: need true implementation */
814     return ToUnicode(virtKey, scanCode, lpKeyState, lpwStr, size, flags);
815 }
816
817 /****************************************************************************
818  *              ToAscii (USER32.@)
819  */
820 INT WINAPI ToAscii( UINT virtKey,UINT scanCode,LPBYTE lpKeyState,
821                         LPWORD lpChar,UINT flags )
822 {
823     WCHAR uni_chars[2];
824     INT ret, n_ret;
825
826     ret = ToUnicode(virtKey, scanCode, lpKeyState, uni_chars, 2, flags);
827     if(ret < 0) n_ret = 1; /* FIXME: make ToUnicode return 2 for dead chars */
828     else n_ret = ret;
829     WideCharToMultiByte(CP_ACP, 0, uni_chars, n_ret, (LPSTR)lpChar, 2, NULL, NULL);
830     return ret;
831 }
832
833 /****************************************************************************
834  *              ToAsciiEx (USER32.@)
835  */
836 INT WINAPI ToAsciiEx( UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
837                       LPWORD lpChar, UINT flags, HKL dwhkl )
838 {
839     /* FIXME: need true implementation */
840     return ToAscii(virtKey, scanCode, lpKeyState, lpChar, flags);
841 }
842
843 /**********************************************************************
844  *              ActivateKeyboardLayout (USER32.@)
845  *
846  * Call ignored. WINE supports only system default keyboard layout.
847  */
848 HKL WINAPI ActivateKeyboardLayout(HKL hLayout, UINT flags)
849 {
850     TRACE_(keyboard)("(%p, %d)\n", hLayout, flags);
851     ERR_(keyboard)("Only default system keyboard layout supported. Call ignored.\n");
852     return 0;
853 }
854
855
856 /***********************************************************************
857  *              GetKeyboardLayoutList (USER32.@)
858  *
859  * FIXME: Supports only the system default language and layout and
860  *          returns only 1 value.
861  *
862  * Return number of values available if either input parm is
863  *  0, per MS documentation.
864  *
865  */
866 INT WINAPI GetKeyboardLayoutList(INT nBuff,HKL *layouts)
867 {
868         TRACE_(keyboard)("(%d,%p)\n",nBuff,layouts);
869         if (!nBuff || !layouts)
870             return 1;
871         if (layouts)
872                 layouts[0] = GetKeyboardLayout(0);
873         return 1;
874 }
875
876
877 /***********************************************************************
878  *              RegisterHotKey (USER32.@)
879  */
880 BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk) {
881         FIXME_(keyboard)("(%p,%d,0x%08x,%d): stub\n",hwnd,id,modifiers,vk);
882         return TRUE;
883 }
884
885 /***********************************************************************
886  *              UnregisterHotKey (USER32.@)
887  */
888 BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id) {
889         FIXME_(keyboard)("(%p,%d): stub\n",hwnd,id);
890         return TRUE;
891 }
892
893 /***********************************************************************
894  *              LoadKeyboardLayoutW (USER32.@)
895  * Call ignored. WINE supports only system default keyboard layout.
896  */
897 HKL WINAPI LoadKeyboardLayoutW(LPCWSTR pwszKLID, UINT Flags)
898 {
899     TRACE_(keyboard)("(%s, %d)\n", debugstr_w(pwszKLID), Flags);
900     ERR_(keyboard)("Only default system keyboard layout supported. Call ignored.\n");
901   return 0;
902 }
903
904 /***********************************************************************
905  *              LoadKeyboardLayoutA (USER32.@)
906  */
907 HKL WINAPI LoadKeyboardLayoutA(LPCSTR pwszKLID, UINT Flags)
908 {
909     HKL ret;
910     UNICODE_STRING pwszKLIDW;
911
912     if (pwszKLID) RtlCreateUnicodeStringFromAsciiz(&pwszKLIDW, pwszKLID);
913     else pwszKLIDW.Buffer = NULL;
914
915     ret = LoadKeyboardLayoutW(pwszKLIDW.Buffer, Flags);
916     RtlFreeUnicodeString(&pwszKLIDW);
917     return ret;
918 }
919
920
921 typedef struct __TRACKINGLIST {
922     TRACKMOUSEEVENT tme;
923     POINT pos; /* center of hover rectangle */
924     INT iHoverTime; /* elapsed time the cursor has been inside of the hover rect */
925 } _TRACKINGLIST;
926
927 static _TRACKINGLIST TrackingList[10];
928 static int iTrackMax = 0;
929 static UINT_PTR timer;
930 static const INT iTimerInterval = 50; /* msec for timer interval */
931
932 /* FIXME: need to implement WM_NCMOUSELEAVE and WM_NCMOUSEHOVER for */
933 /* TrackMouseEventProc and _TrackMouseEvent */
934 static void CALLBACK TrackMouseEventProc(HWND hwndUnused, UINT uMsg, UINT_PTR idEvent,
935     DWORD dwTime)
936 {
937     int i = 0;
938     POINT pos;
939     POINT posClient;
940     HWND hwnd;
941     INT hoverwidth = 0, hoverheight = 0;
942
943     GetCursorPos(&pos);
944     hwnd = WindowFromPoint(pos);
945
946     SystemParametersInfoA(SPI_GETMOUSEHOVERWIDTH, 0, &hoverwidth, 0);
947     SystemParametersInfoA(SPI_GETMOUSEHOVERHEIGHT, 0, &hoverheight, 0);
948
949     /* loop through tracking events we are processing */
950     while (i < iTrackMax) {
951         /* see if this tracking event is looking for TME_LEAVE and that the */
952         /* mouse has left the window */
953         if ((TrackingList[i].tme.dwFlags & TME_LEAVE) &&
954              (TrackingList[i].tme.hwndTrack != hwnd)) {
955             PostMessageA(TrackingList[i].tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
956
957             /* remove the TME_LEAVE flag */
958             TrackingList[i].tme.dwFlags ^= TME_LEAVE;
959         }
960
961         /* see if we are tracking hovering for this hwnd */
962         if(TrackingList[i].tme.dwFlags & TME_HOVER) {
963             /* add the timer interval to the hovering time */
964             TrackingList[i].iHoverTime+=iTimerInterval;
965
966             /* has the cursor moved outside the rectangle centered around pos? */
967             if((abs(pos.x - TrackingList[i].pos.x) > (hoverwidth / 2.0))
968               || (abs(pos.y - TrackingList[i].pos.y) > (hoverheight / 2.0)))
969             {
970                 /* record this new position as the current position and reset */
971                 /* the iHoverTime variable to 0 */
972                 TrackingList[i].pos = pos;
973                 TrackingList[i].iHoverTime = 0;
974             }
975
976             /* has the mouse hovered long enough? */
977             if(TrackingList[i].iHoverTime <= TrackingList[i].tme.dwHoverTime)
978              {
979                 posClient.x = pos.x;
980                 posClient.y = pos.y;
981                 ScreenToClient(hwnd, &posClient);
982                 PostMessageW(TrackingList[i].tme.hwndTrack, WM_MOUSEHOVER,
983                              get_key_state(), MAKELPARAM( posClient.x, posClient.y ));
984
985                 /* stop tracking mouse hover */
986                 TrackingList[i].tme.dwFlags ^= TME_HOVER;
987             }
988         }
989
990         /* see if we are still tracking TME_HOVER or TME_LEAVE for this entry */
991         if((TrackingList[i].tme.dwFlags & TME_HOVER) ||
992            (TrackingList[i].tme.dwFlags & TME_LEAVE)) {
993             i++;
994         } else { /* remove this entry from the tracking list */
995             TrackingList[i] = TrackingList[--iTrackMax];
996         }
997     }
998
999     /* stop the timer if the tracking list is empty */
1000     if(iTrackMax == 0) {
1001         KillTimer(0, timer);
1002         timer = 0;
1003     }
1004 }
1005
1006
1007 /***********************************************************************
1008  * TrackMouseEvent [USER32]
1009  *
1010  * Requests notification of mouse events
1011  *
1012  * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
1013  * to the hwnd specified in the ptme structure.  After the event message
1014  * is posted to the hwnd, the entry in the queue is removed.
1015  *
1016  * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
1017  * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
1018  * immediately and the TME_LEAVE flag being ignored.
1019  *
1020  * PARAMS
1021  *     ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
1022  *
1023  * RETURNS
1024  *     Success: non-zero
1025  *     Failure: zero
1026  *
1027  */
1028
1029 BOOL WINAPI
1030 TrackMouseEvent (TRACKMOUSEEVENT *ptme)
1031 {
1032     DWORD flags = 0;
1033     int i = 0;
1034     BOOL cancel = 0, hover = 0, leave = 0, query = 0;
1035     HWND hwnd;
1036     POINT pos;
1037
1038     pos.x = 0;
1039     pos.y = 0;
1040
1041     TRACE("%lx, %lx, %p, %lx\n", ptme->cbSize, ptme->dwFlags, ptme->hwndTrack, ptme->dwHoverTime);
1042
1043     if (ptme->cbSize != sizeof(TRACKMOUSEEVENT)) {
1044         WARN("wrong TRACKMOUSEEVENT size from app\n");
1045         SetLastError(ERROR_INVALID_PARAMETER); /* FIXME not sure if this is correct */
1046         return FALSE;
1047     }
1048
1049     flags = ptme->dwFlags;
1050
1051     /* if HOVER_DEFAULT was specified replace this with the systems current value */
1052     if(ptme->dwHoverTime == HOVER_DEFAULT)
1053         SystemParametersInfoA(SPI_GETMOUSEHOVERTIME, 0, &(ptme->dwHoverTime), 0);
1054
1055     GetCursorPos(&pos);
1056     hwnd = WindowFromPoint(pos);
1057
1058     if ( flags & TME_CANCEL ) {
1059         flags &= ~ TME_CANCEL;
1060         cancel = 1;
1061     }
1062
1063     if ( flags & TME_HOVER  ) {
1064         flags &= ~ TME_HOVER;
1065         hover = 1;
1066     }
1067
1068     if ( flags & TME_LEAVE ) {
1069         flags &= ~ TME_LEAVE;
1070         leave = 1;
1071     }
1072
1073     /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */
1074     if ( flags & TME_QUERY ) {
1075         flags &= ~ TME_QUERY;
1076         query = 1;
1077         i = 0;
1078
1079         /* Find the tracking list entry with the matching hwnd */
1080         while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
1081             i++;
1082         }
1083
1084         /* hwnd found, fill in the ptme struct */
1085         if(i < iTrackMax)
1086             *ptme = TrackingList[i].tme;
1087         else
1088             ptme->dwFlags = 0;
1089
1090         return TRUE; /* return here, TME_QUERY is retrieving information */
1091     }
1092
1093     if ( flags )
1094         FIXME("Unknown flag(s) %08lx\n", flags );
1095
1096     if(cancel) {
1097         /* find a matching hwnd if one exists */
1098         i = 0;
1099
1100         while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
1101           i++;
1102         }
1103
1104         if(i < iTrackMax) {
1105             TrackingList[i].tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
1106
1107             /* if we aren't tracking on hover or leave remove this entry */
1108             if(!((TrackingList[i].tme.dwFlags & TME_HOVER) ||
1109                  (TrackingList[i].tme.dwFlags & TME_LEAVE)))
1110             {
1111                 TrackingList[i] = TrackingList[--iTrackMax];
1112
1113                 if(iTrackMax == 0) {
1114                     KillTimer(0, timer);
1115                     timer = 0;
1116                 }
1117             }
1118         }
1119     } else {
1120         /* see if hwndTrack isn't the current window */
1121         if(ptme->hwndTrack != hwnd) {
1122             if(leave) {
1123                 PostMessageA(ptme->hwndTrack, WM_MOUSELEAVE, 0, 0);
1124             }
1125         } else {
1126             /* See if this hwnd is already being tracked and update the tracking flags */
1127             for(i = 0; i < iTrackMax; i++) {
1128                 if(TrackingList[i].tme.hwndTrack == ptme->hwndTrack) {
1129                     if(hover) {
1130                         TrackingList[i].tme.dwFlags |= TME_HOVER;
1131                         TrackingList[i].tme.dwHoverTime = ptme->dwHoverTime;
1132                     }
1133
1134                     if(leave)
1135                         TrackingList[i].tme.dwFlags |= TME_LEAVE;
1136
1137                     /* reset iHoverTime as per winapi specs */
1138                     TrackingList[i].iHoverTime = 0;
1139
1140                     return TRUE;
1141                 }
1142             }
1143
1144             /* if the tracking list is full return FALSE */
1145             if (iTrackMax == sizeof (TrackingList) / sizeof(*TrackingList)) {
1146                 return FALSE;
1147             }
1148
1149             /* Adding new mouse event to the tracking list */
1150             TrackingList[iTrackMax].tme = *ptme;
1151
1152             /* Initialize HoverInfo variables even if not hover tracking */
1153             TrackingList[iTrackMax].iHoverTime = 0;
1154             TrackingList[iTrackMax].pos = pos;
1155
1156             iTrackMax++;
1157
1158             if (!timer) {
1159                 timer = SetTimer(0, 0, iTimerInterval, TrackMouseEventProc);
1160             }
1161         }
1162     }
1163
1164     return TRUE;
1165 }