Removed the MESSAGEQUEUE structure, and store the corresponding
[wine] / dlls / user / 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 "config.h"
26 #include "wine/port.h"
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <assert.h>
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "wine/server.h"
43 #include "message.h"
44 #include "user_private.h"
45 #include "winternl.h"
46 #include "wine/debug.h"
47 #include "winerror.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(key);
50 WINE_DECLARE_DEBUG_CHANNEL(keyboard);
51
52
53 /***********************************************************************
54  *           get_key_state
55  */
56 static WORD get_key_state(void)
57 {
58     WORD ret = 0;
59
60     if (GetSystemMetrics( SM_SWAPBUTTON ))
61     {
62         if (GetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_LBUTTON;
63         if (GetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_RBUTTON;
64     }
65     else
66     {
67         if (GetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_LBUTTON;
68         if (GetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_RBUTTON;
69     }
70     if (GetAsyncKeyState(VK_MBUTTON) & 0x80)  ret |= MK_MBUTTON;
71     if (GetAsyncKeyState(VK_SHIFT) & 0x80)    ret |= MK_SHIFT;
72     if (GetAsyncKeyState(VK_CONTROL) & 0x80)  ret |= MK_CONTROL;
73     if (GetAsyncKeyState(VK_XBUTTON1) & 0x80) ret |= MK_XBUTTON1;
74     if (GetAsyncKeyState(VK_XBUTTON2) & 0x80) ret |= MK_XBUTTON2;
75     return ret;
76 }
77
78
79 /***********************************************************************
80  *              SendInput  (USER32.@)
81  */
82 UINT WINAPI SendInput( UINT count, LPINPUT inputs, int size )
83 {
84     if (USER_Driver.pSendInput) return USER_Driver.pSendInput( count, inputs, size );
85     return 0;
86 }
87
88
89 /***********************************************************************
90  *              keybd_event (USER32.@)
91  */
92 void WINAPI keybd_event( BYTE bVk, BYTE bScan,
93                          DWORD dwFlags, ULONG_PTR dwExtraInfo )
94 {
95     INPUT input;
96
97     input.type = INPUT_KEYBOARD;
98     input.u.ki.wVk = bVk;
99     input.u.ki.wScan = bScan;
100     input.u.ki.dwFlags = dwFlags;
101     input.u.ki.time = GetTickCount();
102     input.u.ki.dwExtraInfo = dwExtraInfo;
103     SendInput( 1, &input, sizeof(input) );
104 }
105
106
107 /***********************************************************************
108  *              mouse_event (USER32.@)
109  */
110 void WINAPI mouse_event( DWORD dwFlags, DWORD dx, DWORD dy,
111                          DWORD dwData, ULONG_PTR dwExtraInfo )
112 {
113     INPUT input;
114
115     input.type = INPUT_MOUSE;
116     input.u.mi.dx = dx;
117     input.u.mi.dy = dy;
118     input.u.mi.mouseData = dwData;
119     input.u.mi.dwFlags = dwFlags;
120     input.u.mi.time = GetCurrentTime();
121     input.u.mi.dwExtraInfo = dwExtraInfo;
122     SendInput( 1, &input, sizeof(input) );
123 }
124
125
126 /***********************************************************************
127  *              GetCursorPos (USER32.@)
128  */
129 BOOL WINAPI GetCursorPos( POINT *pt )
130 {
131     if (pt && USER_Driver.pGetCursorPos) return USER_Driver.pGetCursorPos( pt );
132     return FALSE;
133 }
134
135
136 /***********************************************************************
137  *              GetCursorInfo (USER32.@)
138  */
139 BOOL WINAPI GetCursorInfo( PCURSORINFO pci )
140 {
141     if (!pci) return 0;
142     if (get_user_thread_info()->cursor_count >= 0) pci->flags = CURSOR_SHOWING;
143     else pci->flags = 0;
144     GetCursorPos(&pci->ptScreenPos);
145     return 1;
146 }
147
148
149 /***********************************************************************
150  *              SetCursorPos (USER32.@)
151  */
152 BOOL WINAPI SetCursorPos( INT x, INT y )
153 {
154     if (USER_Driver.pSetCursorPos) return USER_Driver.pSetCursorPos( x, y );
155     return FALSE;
156 }
157
158
159 /**********************************************************************
160  *              SetCapture (USER32.@)
161  */
162 HWND WINAPI SetCapture( HWND hwnd )
163 {
164     HWND previous = 0;
165
166     SERVER_START_REQ( set_capture_window )
167     {
168         req->handle = hwnd;
169         req->flags  = 0;
170         if (!wine_server_call_err( req ))
171         {
172             previous = reply->previous;
173             hwnd = reply->full_handle;
174         }
175     }
176     SERVER_END_REQ;
177
178     if (previous && previous != hwnd)
179         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
180     return previous;
181 }
182
183
184 /**********************************************************************
185  *              ReleaseCapture (USER32.@)
186  */
187 BOOL WINAPI ReleaseCapture(void)
188 {
189     return (SetCapture(0) != 0);
190 }
191
192
193 /**********************************************************************
194  *              GetCapture (USER32.@)
195  */
196 HWND WINAPI GetCapture(void)
197 {
198     HWND ret = 0;
199
200     SERVER_START_REQ( get_thread_input )
201     {
202         req->tid = GetCurrentThreadId();
203         if (!wine_server_call_err( req )) ret = reply->capture;
204     }
205     SERVER_END_REQ;
206     return ret;
207 }
208
209
210 /**********************************************************************
211  *              GetAsyncKeyState (USER32.@)
212  *
213  *      Determine if a key is or was pressed.  retval has high-order
214  * bit set to 1 if currently pressed, low-order bit set to 1 if key has
215  * been pressed.
216  */
217 SHORT WINAPI GetAsyncKeyState(INT nKey)
218 {
219     if (USER_Driver.pGetAsyncKeyState) return USER_Driver.pGetAsyncKeyState( nKey );
220     return 0;
221 }
222
223
224 /***********************************************************************
225  *              GetQueueStatus (USER32.@)
226  */
227 DWORD WINAPI GetQueueStatus( UINT flags )
228 {
229     DWORD ret = 0;
230
231     /* check for pending X events */
232     if (USER_Driver.pMsgWaitForMultipleObjectsEx)
233         USER_Driver.pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_ALLINPUT, 0 );
234
235     SERVER_START_REQ( get_queue_status )
236     {
237         req->clear = 1;
238         wine_server_call( req );
239         ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags );
240     }
241     SERVER_END_REQ;
242     return ret;
243 }
244
245
246 /***********************************************************************
247  *              GetInputState   (USER32.@)
248  */
249 BOOL WINAPI GetInputState(void)
250 {
251     DWORD ret = 0;
252
253     /* check for pending X events */
254     if (USER_Driver.pMsgWaitForMultipleObjectsEx)
255         USER_Driver.pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_INPUT, 0 );
256
257     SERVER_START_REQ( get_queue_status )
258     {
259         req->clear = 0;
260         wine_server_call( req );
261         ret = reply->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
262     }
263     SERVER_END_REQ;
264     return ret;
265 }
266
267
268 /**********************************************************************
269  *              AttachThreadInput (USER32.@)
270  *
271  * Attaches the input processing mechanism of one thread to that of
272  * another thread.
273  */
274 BOOL WINAPI AttachThreadInput( DWORD from, DWORD to, BOOL attach )
275 {
276     BOOL ret;
277
278     SERVER_START_REQ( attach_thread_input )
279     {
280         req->tid_from = from;
281         req->tid_to   = to;
282         req->attach   = attach;
283         ret = !wine_server_call_err( req );
284     }
285     SERVER_END_REQ;
286     return ret;
287 }
288
289
290 /**********************************************************************
291  *              GetKeyState (USER32.@)
292  *
293  * An application calls the GetKeyState function in response to a
294  * keyboard-input message.  This function retrieves the state of the key
295  * at the time the input message was generated.
296  */
297 SHORT WINAPI GetKeyState(INT vkey)
298 {
299     SHORT retval = 0;
300
301     SERVER_START_REQ( get_key_state )
302     {
303         req->tid = GetCurrentThreadId();
304         req->key = vkey;
305         if (!wine_server_call( req )) retval = (signed char)reply->state;
306     }
307     SERVER_END_REQ;
308     TRACE("key (0x%x) -> %x\n", vkey, retval);
309     return retval;
310 }
311
312
313 /**********************************************************************
314  *              GetKeyboardState (USER32.@)
315  */
316 BOOL WINAPI GetKeyboardState( LPBYTE state )
317 {
318     BOOL ret;
319
320     TRACE("(%p)\n", state);
321
322     memset( state, 0, 256 );
323     SERVER_START_REQ( get_key_state )
324     {
325         req->tid = GetCurrentThreadId();
326         req->key = -1;
327         wine_server_set_reply( req, state, 256 );
328         ret = !wine_server_call_err( req );
329     }
330     SERVER_END_REQ;
331     return ret;
332 }
333
334
335 /**********************************************************************
336  *              SetKeyboardState (USER32.@)
337  */
338 BOOL WINAPI SetKeyboardState( LPBYTE state )
339 {
340     BOOL ret;
341
342     TRACE("(%p)\n", state);
343
344     SERVER_START_REQ( set_key_state )
345     {
346         req->tid = GetCurrentThreadId();
347         wine_server_add_data( req, state, 256 );
348         ret = !wine_server_call_err( req );
349     }
350     SERVER_END_REQ;
351     return ret;
352 }
353
354
355 /**********************************************************************
356  *              VkKeyScanA (USER32.@)
357  *
358  * VkKeyScan translates an ANSI character to a virtual-key and shift code
359  * for the current keyboard.
360  * high-order byte yields :
361  *      0       Unshifted
362  *      1       Shift
363  *      2       Ctrl
364  *      3-5     Shift-key combinations that are not used for characters
365  *      6       Ctrl-Alt
366  *      7       Ctrl-Alt-Shift
367  *      I.e. :  Shift = 1, Ctrl = 2, Alt = 4.
368  * FIXME : works ok except for dead chars :
369  * VkKeyScan '^'(0x5e, 94) ... got keycode 00 ... returning 00
370  * VkKeyScan '`'(0x60, 96) ... got keycode 00 ... returning 00
371  */
372 SHORT WINAPI VkKeyScanA(CHAR cChar)
373 {
374     WCHAR wChar;
375
376     if (IsDBCSLeadByte(cChar)) return -1;
377
378     MultiByteToWideChar(CP_ACP, 0, &cChar, 1, &wChar, 1);
379     return VkKeyScanW(wChar);
380 }
381
382 /******************************************************************************
383  *              VkKeyScanW (USER32.@)
384  */
385 SHORT WINAPI VkKeyScanW(WCHAR cChar)
386 {
387     return VkKeyScanExW(cChar, GetKeyboardLayout(0));
388 }
389
390 /**********************************************************************
391  *              VkKeyScanExA (USER32.@)
392  */
393 WORD WINAPI VkKeyScanExA(CHAR cChar, HKL dwhkl)
394 {
395     WCHAR wChar;
396
397     if (IsDBCSLeadByte(cChar)) return -1;
398
399     MultiByteToWideChar(CP_ACP, 0, &cChar, 1, &wChar, 1);
400     return VkKeyScanExW(wChar, dwhkl);
401 }
402
403 /******************************************************************************
404  *              VkKeyScanExW (USER32.@)
405  */
406 WORD WINAPI VkKeyScanExW(WCHAR cChar, HKL dwhkl)
407 {
408     if (USER_Driver.pVkKeyScanEx)
409         return USER_Driver.pVkKeyScanEx(cChar, dwhkl);
410     return -1;
411 }
412
413 /**********************************************************************
414  *              OemKeyScan (USER32.@)
415  */
416 DWORD WINAPI OemKeyScan(WORD wOemChar)
417 {
418     TRACE("(%d)\n", wOemChar);
419     return wOemChar;
420 }
421
422 /******************************************************************************
423  *              GetKeyboardType (USER32.@)
424  */
425 INT WINAPI GetKeyboardType(INT nTypeFlag)
426 {
427     TRACE_(keyboard)("(%d)\n", nTypeFlag);
428     switch(nTypeFlag)
429     {
430     case 0:      /* Keyboard type */
431         return 4;    /* AT-101 */
432     case 1:      /* Keyboard Subtype */
433         return 0;    /* There are no defined subtypes */
434     case 2:      /* Number of F-keys */
435         return 12;   /* We're doing an 101 for now, so return 12 F-keys */
436     default:
437         WARN_(keyboard)("Unknown type\n");
438         return 0;    /* The book says 0 here, so 0 */
439     }
440 }
441
442 /******************************************************************************
443  *              MapVirtualKeyA (USER32.@)
444  */
445 UINT WINAPI MapVirtualKeyA(UINT code, UINT maptype)
446 {
447     return MapVirtualKeyExA( code, maptype, GetKeyboardLayout(0) );
448 }
449
450 /******************************************************************************
451  *              MapVirtualKeyW (USER32.@)
452  */
453 UINT WINAPI MapVirtualKeyW(UINT code, UINT maptype)
454 {
455     return MapVirtualKeyExW(code, maptype, GetKeyboardLayout(0));
456 }
457
458 /******************************************************************************
459  *              MapVirtualKeyExA (USER32.@)
460  */
461 UINT WINAPI MapVirtualKeyExA(UINT code, UINT maptype, HKL hkl)
462 {
463     return MapVirtualKeyExW(code, maptype, hkl);
464 }
465
466 /******************************************************************************
467  *              MapVirtualKeyExW (USER32.@)
468  */
469 UINT WINAPI MapVirtualKeyExW(UINT code, UINT maptype, HKL hkl)
470 {
471     TRACE_(keyboard)("(%d, %d, %p)\n", code, maptype, hkl);
472
473     if (USER_Driver.pMapVirtualKeyEx)
474         return USER_Driver.pMapVirtualKeyEx(code, maptype, hkl);
475     return 0;
476 }
477
478 /****************************************************************************
479  *              GetKBCodePage (USER32.@)
480  */
481 UINT WINAPI GetKBCodePage(void)
482 {
483     return GetOEMCP();
484 }
485
486 /***********************************************************************
487  *              GetKeyboardLayout (USER32.@)
488  *
489  *        - device handle for keyboard layout defaulted to
490  *          the language id. This is the way Windows default works.
491  *        - the thread identifier (dwLayout) is also ignored.
492  */
493 HKL WINAPI GetKeyboardLayout(DWORD dwLayout)
494 {
495     if (USER_Driver.pGetKeyboardLayout)
496         return USER_Driver.pGetKeyboardLayout(dwLayout);
497     return 0;
498 }
499
500 /****************************************************************************
501  *              GetKeyboardLayoutNameA (USER32.@)
502  */
503 BOOL WINAPI GetKeyboardLayoutNameA(LPSTR pszKLID)
504 {
505     WCHAR buf[KL_NAMELENGTH];
506
507     if (GetKeyboardLayoutNameW(buf))
508         return WideCharToMultiByte( CP_ACP, 0, buf, -1, pszKLID, KL_NAMELENGTH, NULL, NULL ) != 0;
509     return FALSE;
510 }
511
512 /****************************************************************************
513  *              GetKeyboardLayoutNameW (USER32.@)
514  */
515 BOOL WINAPI GetKeyboardLayoutNameW(LPWSTR pwszKLID)
516 {
517     if (USER_Driver.pGetKeyboardLayoutName)
518         return USER_Driver.pGetKeyboardLayoutName(pwszKLID);
519     return FALSE;
520 }
521
522 /****************************************************************************
523  *              GetKeyNameTextA (USER32.@)
524  */
525 INT WINAPI GetKeyNameTextA(LONG lParam, LPSTR lpBuffer, INT nSize)
526 {
527     WCHAR buf[256];
528     INT ret;
529
530     if (!GetKeyNameTextW(lParam, buf, 256))
531         return 0;
532     ret = WideCharToMultiByte(CP_ACP, 0, buf, -1, lpBuffer, nSize, NULL, NULL);
533     if (!ret && nSize)
534     {
535         ret = nSize - 1;
536         lpBuffer[ret] = 0;
537     }
538     return ret;
539 }
540
541 /****************************************************************************
542  *              GetKeyNameTextW (USER32.@)
543  */
544 INT WINAPI GetKeyNameTextW(LONG lParam, LPWSTR lpBuffer, INT nSize)
545 {
546     if (USER_Driver.pGetKeyNameText)
547         return USER_Driver.pGetKeyNameText( lParam, lpBuffer, nSize );
548     return 0;
549 }
550
551 /****************************************************************************
552  *              ToUnicode (USER32.@)
553  */
554 INT WINAPI ToUnicode(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
555                      LPWSTR lpwStr, int size, UINT flags)
556 {
557     return ToUnicodeEx(virtKey, scanCode, lpKeyState, lpwStr, size, flags, GetKeyboardLayout(0));
558 }
559
560 /****************************************************************************
561  *              ToUnicodeEx (USER32.@)
562  */
563 INT WINAPI ToUnicodeEx(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
564                        LPWSTR lpwStr, int size, UINT flags, HKL hkl)
565 {
566     if (USER_Driver.pToUnicodeEx)
567         return USER_Driver.pToUnicodeEx(virtKey, scanCode, lpKeyState, lpwStr, size, flags, hkl);
568     return 0;
569 }
570
571 /****************************************************************************
572  *              ToAscii (USER32.@)
573  */
574 INT WINAPI ToAscii( UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
575                     LPWORD lpChar, UINT flags )
576 {
577     return ToAsciiEx(virtKey, scanCode, lpKeyState, lpChar, flags, GetKeyboardLayout(0));
578 }
579
580 /****************************************************************************
581  *              ToAsciiEx (USER32.@)
582  */
583 INT WINAPI ToAsciiEx( UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
584                       LPWORD lpChar, UINT flags, HKL dwhkl )
585 {
586     WCHAR uni_chars[2];
587     INT ret, n_ret;
588
589     ret = ToUnicodeEx(virtKey, scanCode, lpKeyState, uni_chars, 2, flags, dwhkl);
590     if (ret < 0) n_ret = 1; /* FIXME: make ToUnicode return 2 for dead chars */
591     else n_ret = ret;
592     WideCharToMultiByte(CP_ACP, 0, uni_chars, n_ret, (LPSTR)lpChar, 2, NULL, NULL);
593     return ret;
594 }
595
596 /**********************************************************************
597  *              ActivateKeyboardLayout (USER32.@)
598  */
599 HKL WINAPI ActivateKeyboardLayout(HKL hLayout, UINT flags)
600 {
601     TRACE_(keyboard)("(%p, %d)\n", hLayout, flags);
602
603     if (USER_Driver.pActivateKeyboardLayout)
604         return USER_Driver.pActivateKeyboardLayout(hLayout, flags);
605     return 0;
606 }
607
608
609 /***********************************************************************
610  *              GetKeyboardLayoutList (USER32.@)
611  *
612  * Return number of values available if either input parm is
613  *  0, per MS documentation.
614  */
615 UINT WINAPI GetKeyboardLayoutList(INT nBuff, HKL *layouts)
616 {
617     TRACE_(keyboard)("(%d,%p)\n",nBuff,layouts);
618
619     if (USER_Driver.pGetKeyboardLayoutList)
620         return USER_Driver.pGetKeyboardLayoutList(nBuff, layouts);
621     return 0;
622 }
623
624
625 /***********************************************************************
626  *              RegisterHotKey (USER32.@)
627  */
628 BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
629 {
630     FIXME_(keyboard)("(%p,%d,0x%08x,%d): stub\n",hwnd,id,modifiers,vk);
631     return TRUE;
632 }
633
634 /***********************************************************************
635  *              UnregisterHotKey (USER32.@)
636  */
637 BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id)
638 {
639     FIXME_(keyboard)("(%p,%d): stub\n",hwnd,id);
640     return TRUE;
641 }
642
643 /***********************************************************************
644  *              LoadKeyboardLayoutW (USER32.@)
645  */
646 HKL WINAPI LoadKeyboardLayoutW(LPCWSTR pwszKLID, UINT Flags)
647 {
648     TRACE_(keyboard)("(%s, %d)\n", debugstr_w(pwszKLID), Flags);
649
650     if (USER_Driver.pLoadKeyboardLayout)
651         return USER_Driver.pLoadKeyboardLayout(pwszKLID, Flags);
652     return 0;
653 }
654
655 /***********************************************************************
656  *              LoadKeyboardLayoutA (USER32.@)
657  */
658 HKL WINAPI LoadKeyboardLayoutA(LPCSTR pwszKLID, UINT Flags)
659 {
660     HKL ret;
661     UNICODE_STRING pwszKLIDW;
662
663     if (pwszKLID) RtlCreateUnicodeStringFromAsciiz(&pwszKLIDW, pwszKLID);
664     else pwszKLIDW.Buffer = NULL;
665
666     ret = LoadKeyboardLayoutW(pwszKLIDW.Buffer, Flags);
667     RtlFreeUnicodeString(&pwszKLIDW);
668     return ret;
669 }
670
671
672 /***********************************************************************
673  *              UnloadKeyboardLayout (USER32.@)
674  */
675 BOOL WINAPI UnloadKeyboardLayout(HKL hkl)
676 {
677     TRACE_(keyboard)("(%p)\n", hkl);
678
679     if (USER_Driver.pUnloadKeyboardLayout)
680         return USER_Driver.pUnloadKeyboardLayout(hkl);
681     return 0;
682 }
683
684 typedef struct __TRACKINGLIST {
685     TRACKMOUSEEVENT tme;
686     POINT pos; /* center of hover rectangle */
687     INT iHoverTime; /* elapsed time the cursor has been inside of the hover rect */
688 } _TRACKINGLIST;
689
690 static _TRACKINGLIST TrackingList[10];
691 static int iTrackMax = 0;
692 static UINT_PTR timer;
693 static const INT iTimerInterval = 50; /* msec for timer interval */
694
695 static void CALLBACK TrackMouseEventProc(HWND hwndUnused, UINT uMsg, UINT_PTR idEvent,
696     DWORD dwTime)
697 {
698     int i = 0;
699     POINT pos;
700     POINT posClient;
701     HWND hwnd;
702     INT nonclient;
703     INT hoverwidth = 0, hoverheight = 0;
704     RECT client;
705
706     GetCursorPos(&pos);
707     hwnd = WindowFromPoint(pos);
708
709     SystemParametersInfoA(SPI_GETMOUSEHOVERWIDTH, 0, &hoverwidth, 0);
710     SystemParametersInfoA(SPI_GETMOUSEHOVERHEIGHT, 0, &hoverheight, 0);
711
712     /* loop through tracking events we are processing */
713     while (i < iTrackMax) {
714         if (TrackingList[i].tme.dwFlags & TME_NONCLIENT) {
715             nonclient = 1;
716         }
717         else {
718             nonclient = 0;
719         }
720
721         /* see if this tracking event is looking for TME_LEAVE and that the */
722         /* mouse has left the window */
723         if (TrackingList[i].tme.dwFlags & TME_LEAVE) {
724             if (TrackingList[i].tme.hwndTrack != hwnd) {
725                 if (nonclient) {
726                     PostMessageA(TrackingList[i].tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0);
727                 }
728                 else {
729                     PostMessageA(TrackingList[i].tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
730                 }
731
732                 /* remove the TME_LEAVE flag */
733                 TrackingList[i].tme.dwFlags ^= TME_LEAVE;
734             }
735             else {
736                 GetClientRect(hwnd, &client);
737                 MapWindowPoints(hwnd, NULL, (LPPOINT)&client, 2);
738                 if(PtInRect(&client, pos)) {
739                     if (nonclient) {
740                         PostMessageA(TrackingList[i].tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0);
741                         /* remove the TME_LEAVE flag */
742                         TrackingList[i].tme.dwFlags ^= TME_LEAVE;
743                     }
744                 }
745                 else {
746                     if (!nonclient) {
747                         PostMessageA(TrackingList[i].tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
748                         /* remove the TME_LEAVE flag */
749                         TrackingList[i].tme.dwFlags ^= TME_LEAVE;
750                     }
751                 }
752             }
753         }
754
755         /* see if we are tracking hovering for this hwnd */
756         if(TrackingList[i].tme.dwFlags & TME_HOVER) {
757             /* add the timer interval to the hovering time */
758             TrackingList[i].iHoverTime+=iTimerInterval;
759
760             /* has the cursor moved outside the rectangle centered around pos? */
761             if((abs(pos.x - TrackingList[i].pos.x) > (hoverwidth / 2.0))
762               || (abs(pos.y - TrackingList[i].pos.y) > (hoverheight / 2.0)))
763             {
764                 /* record this new position as the current position and reset */
765                 /* the iHoverTime variable to 0 */
766                 TrackingList[i].pos = pos;
767                 TrackingList[i].iHoverTime = 0;
768             }
769
770             /* has the mouse hovered long enough? */
771             if(TrackingList[i].iHoverTime <= TrackingList[i].tme.dwHoverTime)
772             {
773                 posClient.x = pos.x;
774                 posClient.y = pos.y;
775                 ScreenToClient(hwnd, &posClient);
776                 if (nonclient) {
777                     PostMessageW(TrackingList[i].tme.hwndTrack, WM_NCMOUSEHOVER,
778                                 get_key_state(), MAKELPARAM( posClient.x, posClient.y ));
779                 }
780                 else {
781                     PostMessageW(TrackingList[i].tme.hwndTrack, WM_MOUSEHOVER,
782                                 get_key_state(), MAKELPARAM( posClient.x, posClient.y ));
783                 }
784
785                 /* stop tracking mouse hover */
786                 TrackingList[i].tme.dwFlags ^= TME_HOVER;
787             }
788         }
789
790         /* see if we are still tracking TME_HOVER or TME_LEAVE for this entry */
791         if((TrackingList[i].tme.dwFlags & TME_HOVER) ||
792            (TrackingList[i].tme.dwFlags & TME_LEAVE)) {
793             i++;
794         } else { /* remove this entry from the tracking list */
795             TrackingList[i] = TrackingList[--iTrackMax];
796         }
797     }
798
799     /* stop the timer if the tracking list is empty */
800     if(iTrackMax == 0) {
801         KillTimer(0, timer);
802         timer = 0;
803     }
804 }
805
806
807 /***********************************************************************
808  * TrackMouseEvent [USER32]
809  *
810  * Requests notification of mouse events
811  *
812  * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
813  * to the hwnd specified in the ptme structure.  After the event message
814  * is posted to the hwnd, the entry in the queue is removed.
815  *
816  * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
817  * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
818  * immediately and the TME_LEAVE flag being ignored.
819  *
820  * PARAMS
821  *     ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
822  *
823  * RETURNS
824  *     Success: non-zero
825  *     Failure: zero
826  *
827  */
828
829 BOOL WINAPI
830 TrackMouseEvent (TRACKMOUSEEVENT *ptme)
831 {
832     DWORD flags = 0;
833     int i = 0;
834     BOOL cancel = 0, hover = 0, leave = 0, query = 0, nonclient = 0, inclient = 0;
835     HWND hwnd;
836     POINT pos;
837     RECT client;
838
839
840     pos.x = 0;
841     pos.y = 0;
842     SetRectEmpty(&client);
843
844     TRACE("%lx, %lx, %p, %lx\n", ptme->cbSize, ptme->dwFlags, ptme->hwndTrack, ptme->dwHoverTime);
845
846     if (ptme->cbSize != sizeof(TRACKMOUSEEVENT)) {
847         WARN("wrong TRACKMOUSEEVENT size from app\n");
848         SetLastError(ERROR_INVALID_PARAMETER); /* FIXME not sure if this is correct */
849         return FALSE;
850     }
851
852     flags = ptme->dwFlags;
853
854     /* if HOVER_DEFAULT was specified replace this with the systems current value */
855     if(ptme->dwHoverTime == HOVER_DEFAULT)
856         SystemParametersInfoA(SPI_GETMOUSEHOVERTIME, 0, &(ptme->dwHoverTime), 0);
857
858     GetCursorPos(&pos);
859     hwnd = WindowFromPoint(pos);
860
861     if ( flags & TME_CANCEL ) {
862         flags &= ~ TME_CANCEL;
863         cancel = 1;
864     }
865
866     if ( flags & TME_HOVER  ) {
867         flags &= ~ TME_HOVER;
868         hover = 1;
869     }
870
871     if ( flags & TME_LEAVE ) {
872         flags &= ~ TME_LEAVE;
873         leave = 1;
874     }
875
876     if ( flags & TME_NONCLIENT ) {
877         flags &= ~ TME_NONCLIENT;
878         nonclient = 1;
879     }
880
881     /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */
882     if ( flags & TME_QUERY ) {
883         flags &= ~ TME_QUERY;
884         query = 1;
885         i = 0;
886
887         /* Find the tracking list entry with the matching hwnd */
888         while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
889             i++;
890         }
891
892         /* hwnd found, fill in the ptme struct */
893         if(i < iTrackMax)
894             *ptme = TrackingList[i].tme;
895         else
896             ptme->dwFlags = 0;
897
898         return TRUE; /* return here, TME_QUERY is retrieving information */
899     }
900
901     if ( flags )
902         FIXME("Unknown flag(s) %08lx\n", flags );
903
904     if(cancel) {
905         /* find a matching hwnd if one exists */
906         i = 0;
907
908         while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
909           i++;
910         }
911
912         if(i < iTrackMax) {
913             TrackingList[i].tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
914
915             /* if we aren't tracking on hover or leave remove this entry */
916             if(!((TrackingList[i].tme.dwFlags & TME_HOVER) ||
917                  (TrackingList[i].tme.dwFlags & TME_LEAVE)))
918             {
919                 TrackingList[i] = TrackingList[--iTrackMax];
920
921                 if(iTrackMax == 0) {
922                     KillTimer(0, timer);
923                     timer = 0;
924                 }
925             }
926         }
927     } else {
928         /* see if hwndTrack isn't the current window */
929         if(ptme->hwndTrack != hwnd) {
930             if(leave) {
931                 if(nonclient) {
932                     PostMessageA(ptme->hwndTrack, WM_NCMOUSELEAVE, 0, 0);
933                 }
934                 else {
935                     PostMessageA(ptme->hwndTrack, WM_MOUSELEAVE, 0, 0);
936                 }
937             }
938         } else {
939             GetClientRect(ptme->hwndTrack, &client);
940             MapWindowPoints(ptme->hwndTrack, NULL, (LPPOINT)&client, 2);
941             if(PtInRect(&client, pos)) {
942                 inclient = 1;
943             }
944             if(nonclient && inclient) {
945                 PostMessageA(ptme->hwndTrack, WM_NCMOUSELEAVE, 0, 0);
946                 return TRUE;
947             }
948             else if(!nonclient && !inclient) {
949                 PostMessageA(ptme->hwndTrack, WM_MOUSELEAVE, 0, 0);
950                 return TRUE;
951             }
952
953             /* See if this hwnd is already being tracked and update the tracking flags */
954             for(i = 0; i < iTrackMax; i++) {
955                 if(TrackingList[i].tme.hwndTrack == ptme->hwndTrack) {
956                     TrackingList[i].tme.dwFlags = 0;
957
958                     if(hover) {
959                         TrackingList[i].tme.dwFlags |= TME_HOVER;
960                         TrackingList[i].tme.dwHoverTime = ptme->dwHoverTime;
961                     }
962
963                     if(leave)
964                         TrackingList[i].tme.dwFlags |= TME_LEAVE;
965
966                     if(nonclient)
967                         TrackingList[i].tme.dwFlags |= TME_NONCLIENT;
968
969                     /* reset iHoverTime as per winapi specs */
970                     TrackingList[i].iHoverTime = 0;
971
972                     return TRUE;
973                 }
974             }
975
976             /* if the tracking list is full return FALSE */
977             if (iTrackMax == sizeof (TrackingList) / sizeof(*TrackingList)) {
978                 return FALSE;
979             }
980
981             /* Adding new mouse event to the tracking list */
982             TrackingList[iTrackMax].tme = *ptme;
983
984             /* Initialize HoverInfo variables even if not hover tracking */
985             TrackingList[iTrackMax].iHoverTime = 0;
986             TrackingList[iTrackMax].pos = pos;
987
988             iTrackMax++;
989
990             if (!timer) {
991                 timer = SetTimer(0, 0, iTimerInterval, TrackMouseEventProc);
992             }
993         }
994     }
995
996     return TRUE;
997 }