source: trunk/src/user32/winmouse.cpp@ 7807

Last change on this file since 7807 was 7807, checked in by sandervl, 24 years ago

Ported TrackMouseEvent from Wine

File size: 20.3 KB
Line 
1/* $Id: winmouse.cpp,v 1.21 2002-02-06 16:31:34 sandervl Exp $ */
2/*
3 * Win32 mouse functions
4 *
5 * Copyright 1999-2002 Sander van Leeuwen
6 *
7 * Parts based on Wine code (windows\input.c) (TrackMouseEvent)
8 *
9 * Copyright 1993 Bob Amstadt
10 * Copyright 1996 Albrecht Kleine
11 * Copyright 1997 David Faure
12 * Copyright 1998 Morten Welinder
13 * Copyright 1998 Ulrich Weigand
14 *
15 * TODO: SwapMouseButton:
16 * We shouldn't let win32 apps change this for the whole system
17 * better to change mouse button message translation instead
18 *
19 * Project Odin Software License can be found in LICENSE.TXT
20 *
21 */
22
23#include <odin.h>
24#include <odinwrap.h>
25#include <os2sel.h>
26
27#include <os2win.h>
28#include <misc.h>
29#include "win32wbase.h"
30#include <winuser32.h>
31#include <winuser.h>
32#include <commctrl.h>
33#include <debugtools.h>
34#include <win\mouse.h>
35#include "winmouse.h"
36#include "oslibmsg.h"
37#include "pmwindow.h"
38#include "oslibwin.h"
39
40#define DBG_LOCALLOG DBG_winmouse
41#include "dbglocal.h"
42
43
44ODINDEBUGCHANNEL(USER32-WINMOUSE)
45
46
47/****************************************************************************
48 * local variables
49 ****************************************************************************/
50
51LPMOUSE_EVENT_PROC mouseHandler = NULL;
52WNDPROC keyboardHandler = NULL;
53
54
55//******************************************************************************
56//******************************************************************************
57ODINPROCEDURE1(MOUSE_Enable,
58 LPMOUSE_EVENT_PROC, lpMouseEventProc)
59{
60 if(lpMouseEventProc == (LPMOUSE_EVENT_PROC)-1)
61 mouseHandler = NULL;
62 else
63 mouseHandler = lpMouseEventProc;
64}
65//******************************************************************************
66//******************************************************************************
67ODINPROCEDURE1(KEYBOARD_Enable,
68 WNDPROC, handler)
69{
70 keyboardHandler = handler;
71}
72//******************************************************************************
73//******************************************************************************
74BOOL DInputKeyBoardHandler(MSG *msg)
75{
76 if(!ISKDB_CAPTURED())
77 return FALSE;
78
79 return keyboardHandler(msg->hwnd, msg->message, msg->wParam, msg->lParam);
80}
81//******************************************************************************
82//******************************************************************************
83BOOL DInputMouseHandler(HWND hwnd, ULONG msg, ULONG x, ULONG y)
84{
85 WINE_MOUSEEVENT mouseEvent;
86 DWORD dwFlags = MOUSEEVENTF_ABSOLUTE;
87
88 if(!ISMOUSE_CAPTURED())
89 return FALSE;
90
91 mouseEvent.magic = WINE_MOUSEEVENT_MAGIC;
92 mouseEvent.hWnd = hwnd;
93 mouseEvent.time = OSLibWinQueryMsgTime();
94 mouseEvent.keyState = 0; //not used in dinput right now
95 switch(msg)
96 {
97 case WM_NCLBUTTONDOWN:
98 case WM_LBUTTONDOWN:
99 dwFlags |= MOUSEEVENTF_LEFTDOWN;
100 break;
101 case WM_NCLBUTTONUP:
102 case WM_LBUTTONUP:
103 dwFlags |= MOUSEEVENTF_LEFTUP;
104 break;
105 case WM_NCRBUTTONUP:
106 case WM_RBUTTONUP:
107 dwFlags |= MOUSEEVENTF_RIGHTUP;
108 break;
109 case WM_NCRBUTTONDOWN:
110 case WM_RBUTTONDOWN:
111 dwFlags |= MOUSEEVENTF_RIGHTDOWN;
112 break;
113 case WM_NCMBUTTONUP:
114 case WM_MBUTTONUP:
115 dwFlags |= MOUSEEVENTF_MIDDLEUP;
116 break;
117 case WM_NCMBUTTONDOWN:
118 case WM_MBUTTONDOWN:
119 dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
120 break;
121 case WM_MOUSEMOVE:
122 case WM_NCMOUSEMOVE:
123 dwFlags |= MOUSEEVENTF_MOVE;
124 break;
125 default:
126 //TODO: handle double clicks???
127 return FALSE;
128 }
129
130 x = (((long)x << 16) + ScreenWidth-1) / ScreenWidth;
131 y = (((long)y << 16) + ScreenHeight-1) / ScreenHeight;
132
133 return mouseHandler(dwFlags, x, y, 0, (DWORD)&mouseEvent);
134}
135//******************************************************************************
136//******************************************************************************
137
138// capture handle "cache"
139static HWND hwndWin32Capture = 0;
140
141ODINFUNCTION0(HWND, GetCapture)
142{
143 if (0 == hwndWin32Capture)
144 hwndWin32Capture = OS2ToWin32Handle(OSLibWinQueryCapture());
145
146 return hwndWin32Capture;
147}
148//******************************************************************************
149//******************************************************************************
150ODINFUNCTION1(HWND, SetCapture,
151 HWND, hwnd)
152{
153 HWND hwndPrev = GetCapture();
154 BOOL rc;
155
156 // invalidate capture "cache"
157 hwndWin32Capture = 0;
158
159 if(hwnd == 0)
160 {
161 ReleaseCapture();
162 return hwndPrev;
163 }
164
165 if(hwnd == hwndPrev)
166 {
167 dprintf(("USER32: SetCapture %x; already set to that window; ignore", hwnd));
168 return hwndPrev;
169 }
170
171 if(hwndPrev != NULL)
172 {
173 //SvL: WinSetCapture returns an error if mouse is already captured
174 OSLibWinSetCapture(0);
175 }
176
177 rc = OSLibWinSetCapture(Win32ToOS2Handle(hwnd));
178 dprintf(("USER32: SetCapture %x (prev %x) returned %d", hwnd, hwndPrev, rc));
179 if(hwndPrev)
180 {
181 SendMessageA(hwndPrev, WM_CAPTURECHANGED, 0L, hwnd);
182 }
183 return hwndPrev;
184}
185//******************************************************************************
186//******************************************************************************
187ODINFUNCTION0(BOOL, ReleaseCapture)
188{
189 HWND hwndPrev;
190 BOOL ret;
191
192 hwndPrev = GetCapture();
193
194 // invalidate capture "cache"
195 hwndWin32Capture = 0;
196
197 ret = OSLibWinSetCapture(0);
198 if(hwndPrev)
199 {
200 SendMessageA(hwndPrev, WM_CAPTURECHANGED, 0L, 0L);
201 }
202 return ret;
203}
204//******************************************************************************
205//******************************************************************************
206ODINFUNCTION0(UINT, GetDoubleClickTime)
207{
208 UINT result = OSLibWinQuerySysValue(SVOS_DBLCLKTIME);
209 if(result == 0)
210 SetLastError(ERROR_INVALID_PARAMETER); //TODO: ????
211
212 return result;
213}
214//******************************************************************************
215//******************************************************************************
216ODINFUNCTION1(BOOL, SetDoubleClickTime,
217 UINT, uInterval)
218{
219 BOOL ret = TRUE;
220
221 ret = OSLibWinSetSysValue(SVOS_DBLCLKTIME, uInterval);
222 if(ret == FALSE )
223 {
224 SetLastError(ERROR_INVALID_PARAMETER); //TODO: ????
225 }
226 return (ret);
227}
228//******************************************************************************
229//TODO: we shouldn't let win32 apps change this for the whole system
230// better to change mouse button message translation instead
231BOOL OPEN32API __SwapMouseButton(BOOL swapFlag);
232
233inline BOOL _SwapMouseButton(BOOL swapFlag)
234{
235 BOOL yyrc;
236 USHORT sel = RestoreOS2FS();
237
238 yyrc = __SwapMouseButton(swapFlag);
239 SetFS(sel);
240
241 return yyrc;
242}
243
244//******************************************************************************
245ODINFUNCTION1(BOOL, SwapMouseButton,
246 BOOL, fSwap)
247{
248 return _SwapMouseButton(fSwap);
249}
250
251/*****************************************************************************
252 * Name : VOID WIN32API mouse_event
253 * Purpose : The mouse_event function synthesizes mouse motion and button clicks.
254 * Parameters: DWORD dwFlags flags specifying various motion/click variants
255 * DWORD dx horizontal mouse position or position change
256 * DWORD dy vertical mouse position or position change
257 * DWORD cButtons unused, reserved for future use, set to zero
258 * DWORD dwExtraInfo 32 bits of application-defined information
259 * Variables :
260 * Result :
261 * Remark :
262 * Status : UNTESTED STUB
263 *
264 * Author : Patrick Haller [Thu, 1998/02/26 11:55]
265 *****************************************************************************/
266
267ODINPROCEDURE5(mouse_event,
268 DWORD, dwFlags,
269 DWORD, dx,
270 DWORD, dy,
271 DWORD, cButtons,
272 DWORD, dwExtraInfo)
273{
274 INPUT i;
275
276 // format input packet
277 i.type = INPUT_MOUSE;
278 i.mi.dx = dx;
279 i.mi.dy = dy;
280 i.mi.mouseData = cButtons; // PH: is this really correct?
281 i.mi.dwFlags = dwFlags;
282 i.mi.dwExtraInfo = dwExtraInfo;
283
284 // forward to more modern API
285 SendInput(1, &i, sizeof(i) );
286}
287
288
289/*****************************************************************************
290 * Name : UINT SendInput
291 * Purpose : The SendInput function synthesizes keystrokes, mouse motions,
292 * and button clicks
293 * Parameters: UINT nInputs // count if input events
294 * LPINPUT pInputs // array of input structures
295 * int chSize // size of structure
296 * Variables :
297 * Result : number of events successfully inserted,
298 * 0 if the input was already blocked by another thread
299 * Remark :
300 * Status : UNTESTED STUB
301 *
302 * Author : Patrick Haller [Thu, 1998/02/26 11:55]
303 *****************************************************************************/
304
305ODINFUNCTION3(UINT, SendInput,
306 UINT, nInputs,
307 LPINPUT, pInputs,
308 int, chSize)
309{
310 dprintf(("not correctly implemented"));
311
312 // The simulated input is sent to the
313 // foreground thread's message queue.
314 // (WM_KEYUP, WM_KEYDOWN)
315 // After GetMessage or PeekMessage,
316 // TranslateMessage posts an appropriate
317 // WM_CHAR message.
318 HWND hwnd = GetForegroundWindow();
319
320 LPINPUT piBase = pInputs;
321 for (int i = 0;
322 i < nInputs;
323 i++,
324 piBase++)
325 {
326 switch(piBase->type)
327 {
328 case INPUT_MOUSE:
329 {
330 PMOUSEINPUT p = (PMOUSEINPUT)&piBase->mi;
331 MSG msg;
332
333 // simulate mouse input message
334 // @@@PH
335 }
336 break;
337
338 // compose a keyboard input message
339 case INPUT_KEYBOARD:
340 {
341 PKEYBDINPUT p = (PKEYBDINPUT)&piBase->ki;
342 MSG msg;
343 BOOL fUnicode = (p->dwFlags & KEYEVENTF_UNICODE) == KEYEVENTF_UNICODE;
344 DWORD extrainfo = GetMessageExtraInfo();
345
346 // build keyboard message
347 msg.message = (p->dwFlags & KEYEVENTF_KEYUP) ? WM_KEYUP : WM_KEYDOWN;
348
349 if (p->dwFlags & KEYEVENTF_SCANCODE)
350 {
351 // keystroke is identified by the scancode
352 if (fUnicode)
353 msg.wParam = MapVirtualKeyW(p->wScan, 1);
354 else
355 msg.wParam = MapVirtualKeyA(p->wScan, 1);
356 }
357 else
358 msg.wParam = p->wVk;
359
360 msg.lParam = 0x0000001 | // repeat count
361 ( (p->wScan & 0xff) << 16); // scan code
362
363 if (p->dwFlags & KEYEVENTF_EXTENDEDKEY)
364 msg.lParam |= (1 << 24);
365
366 // set additional message flags
367 if (msg.message == WM_KEYDOWN)
368 {
369 // @@@PH
370 // bit 30 - previous key state cannot be set, how to determine?
371 }
372 else
373 {
374 // WM_KEYUP -> previous key is always 1, transistion state
375 // always 1
376 msg.lParam |= (1 << 30);
377 msg.lParam |= (1 << 31);
378 }
379
380 msg.time = p->time;
381
382 // @@@PH
383 // unknown: do we have to post or to send the message?
384
385 SetMessageExtraInfo( (LPARAM)p->dwExtraInfo );
386
387 if (fUnicode)
388 SendMessageW(hwnd, msg.message, msg.wParam, msg.lParam);
389 else
390 SendMessageA(hwnd, msg.message, msg.wParam, msg.lParam);
391
392 //restore extra info
393 SetMessageExtraInfo(extrainfo);
394 break;
395 }
396
397 case INPUT_HARDWARE:
398 {
399 PHARDWAREINPUT p = (PHARDWAREINPUT)&piBase->hi;
400
401 // @@@PH
402 // not supported for the time being
403 }
404 break;
405
406 default:
407 dprintf(("unsupported input packet type %d",
408 piBase->type));
409 break;
410 }
411 }
412
413 return 0;
414}
415
416/*****************************************************************************
417 * Name : BOOL WIN32API DragDetect
418 * Purpose : The DragDetect function captures the mouse and tracks its movement
419 * Parameters: HWND hwnd
420 * POINT pt
421 * Variables :
422 * Result : If the user moved the mouse outside of the drag rectangle while
423 * holding the left button down, the return value is TRUE.
424 * If the user did not move the mouse outside of the drag rectangle
425 * while holding the left button down, the return value is FALSE.
426 * Remark :
427 * Status : UNTESTED STUB
428 *
429 * Author : Patrick Haller [Thu, 1998/02/26 11:55]
430 *****************************************************************************/
431ODINFUNCTION2(BOOL, DragDetect,
432 HWND, hwnd,
433 POINT, pt)
434{
435 dprintf(("not implemented"));
436
437 return (FALSE);
438}
439//******************************************************************************
440//******************************************************************************
441typedef struct __TRACKINGLIST {
442 TRACKMOUSEEVENT tme;
443 POINT pos; /* center of hover rectangle */
444 INT iHoverTime; /* elapsed time the cursor has been inside of the hover rect */
445} _TRACKINGLIST;
446
447#define UINT_PTR UINT
448static _TRACKINGLIST TrackingList[10];
449static int iTrackMax = 0;
450static UINT_PTR timer;
451static const INT iTimerInterval = 50; /* msec for timer interval */
452
453/* FIXME: need to implement WM_NCMOUSELEAVE and WM_NCMOUSEHOVER for */
454/* TrackMouseEventProc and _TrackMouseEvent */
455static void CALLBACK TrackMouseEventProc(HWND hwndUnused, UINT uMsg, UINT_PTR idEvent,
456 DWORD dwTime)
457{
458 int i = 0;
459 POINT pos;
460 HWND hwnd;
461 INT hoverwidth = 0, hoverheight = 0;
462
463 GetCursorPos(&pos);
464 hwnd = WindowFromPoint(pos);
465
466 SystemParametersInfoA(SPI_GETMOUSEHOVERWIDTH, 0, &hoverwidth, 0);
467 SystemParametersInfoA(SPI_GETMOUSEHOVERHEIGHT, 0, &hoverheight, 0);
468
469 /* loop through tracking events we are processing */
470 while (i < iTrackMax) {
471 /* see if this tracking event is looking for TME_LEAVE and that the */
472 /* mouse has left the window */
473 if ((TrackingList[i].tme.dwFlags & TME_LEAVE) &&
474 (TrackingList[i].tme.hwndTrack != hwnd)) {
475 PostMessageA(TrackingList[i].tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
476
477 /* remove the TME_LEAVE flag */
478 TrackingList[i].tme.dwFlags ^= TME_LEAVE;
479 }
480
481 /* see if we are tracking hovering for this hwnd */
482 if(TrackingList[i].tme.dwFlags & TME_HOVER) {
483 /* add the timer interval to the hovering time */
484 TrackingList[i].iHoverTime+=iTimerInterval;
485
486 /* has the cursor moved outside the rectangle centered around pos? */
487 if((abs(pos.x - TrackingList[i].pos.x) > (hoverwidth / 2.0))
488 || (abs(pos.y - TrackingList[i].pos.y) > (hoverheight / 2.0)))
489 {
490 /* record this new position as the current position and reset */
491 /* the iHoverTime variable to 0 */
492 TrackingList[i].pos = pos;
493 TrackingList[i].iHoverTime = 0;
494 }
495
496 /* has the mouse hovered long enough? */
497 if(TrackingList[i].iHoverTime <= TrackingList[i].tme.dwHoverTime)
498 {
499 PostMessageA(TrackingList[i].tme.hwndTrack, WM_MOUSEHOVER, 0, 0);
500
501 /* stop tracking mouse hover */
502 TrackingList[i].tme.dwFlags ^= TME_HOVER;
503 }
504 }
505
506 /* see if we are still tracking TME_HOVER or TME_LEAVE for this entry */
507 if((TrackingList[i].tme.dwFlags & TME_HOVER) ||
508 (TrackingList[i].tme.dwFlags & TME_LEAVE)) {
509 i++;
510 } else { /* remove this entry from the tracking list */
511 TrackingList[i] = TrackingList[--iTrackMax];
512 }
513 }
514
515 /* stop the timer if the tracking list is empty */
516 if(iTrackMax == 0) {
517 KillTimer(0, timer);
518 timer = 0;
519 }
520}
521
522
523/***********************************************************************
524 * TrackMouseEvent [USER32]
525 *
526 * Requests notification of mouse events
527 *
528 * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
529 * to the hwnd specified in the ptme structure. After the event message
530 * is posted to the hwnd, the entry in the queue is removed.
531 *
532 * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
533 * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
534 * immediately and the TME_LEAVE flag being ignored.
535 *
536 * PARAMS
537 * ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
538 *
539 * RETURNS
540 * Success: non-zero
541 * Failure: zero
542 *
543 */
544
545BOOL WINAPI
546TrackMouseEvent (TRACKMOUSEEVENT *ptme)
547{
548 DWORD flags = 0;
549 int i = 0;
550 BOOL cancel = 0, hover = 0, leave = 0, query = 0;
551 HWND hwnd;
552 POINT pos;
553
554 pos.x = 0;
555 pos.y = 0;
556
557 dprintf(("TrackMouseEvent: %lx, %lx, %x, %lx\n", ptme->cbSize, ptme->dwFlags, ptme->hwndTrack, ptme->dwHoverTime));
558
559 if (ptme->cbSize != sizeof(TRACKMOUSEEVENT)) {
560 WARN("wrong TRACKMOUSEEVENT size from app\n");
561 SetLastError(ERROR_INVALID_PARAMETER); /* FIXME not sure if this is correct */
562 return FALSE;
563 }
564
565 flags = ptme->dwFlags;
566
567 /* if HOVER_DEFAULT was specified replace this with the systems current value */
568 if(ptme->dwHoverTime == HOVER_DEFAULT)
569 SystemParametersInfoA(SPI_GETMOUSEHOVERTIME, 0, &(ptme->dwHoverTime), 0);
570
571 GetCursorPos(&pos);
572 hwnd = WindowFromPoint(pos);
573
574 if ( flags & TME_CANCEL ) {
575 flags &= ~ TME_CANCEL;
576 cancel = 1;
577 }
578
579 if ( flags & TME_HOVER ) {
580 flags &= ~ TME_HOVER;
581 hover = 1;
582 }
583
584 if ( flags & TME_LEAVE ) {
585 flags &= ~ TME_LEAVE;
586 leave = 1;
587 }
588
589 /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */
590 if ( flags & TME_QUERY ) {
591 flags &= ~ TME_QUERY;
592 query = 1;
593 i = 0;
594
595 /* Find the tracking list entry with the matching hwnd */
596 while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
597 i++;
598 }
599
600 /* hwnd found, fill in the ptme struct */
601 if(i < iTrackMax)
602 *ptme = TrackingList[i].tme;
603 else
604 ptme->dwFlags = 0;
605
606 return TRUE; /* return here, TME_QUERY is retrieving information */
607 }
608
609 if ( flags )
610 FIXME("Unknown flag(s) %08lx\n", flags );
611
612 if(cancel) {
613 /* find a matching hwnd if one exists */
614 i = 0;
615
616 while((i < iTrackMax) && (TrackingList[i].tme.hwndTrack != ptme->hwndTrack)) {
617 i++;
618 }
619
620 if(i < iTrackMax) {
621 TrackingList[i].tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
622
623 /* if we aren't tracking on hover or leave remove this entry */
624 if(!((TrackingList[i].tme.dwFlags & TME_HOVER) ||
625 (TrackingList[i].tme.dwFlags & TME_LEAVE)))
626 {
627 TrackingList[i] = TrackingList[--iTrackMax];
628
629 if(iTrackMax == 0) {
630 KillTimer(0, timer);
631 timer = 0;
632 }
633 }
634 }
635 } else {
636 /* see if hwndTrack isn't the current window */
637 if(ptme->hwndTrack != hwnd) {
638 if(leave) {
639 PostMessageA(ptme->hwndTrack, WM_MOUSELEAVE, 0, 0);
640 }
641 } else {
642 /* See if this hwnd is already being tracked and update the tracking flags */
643 for(i = 0; i < iTrackMax; i++) {
644 if(TrackingList[i].tme.hwndTrack == ptme->hwndTrack) {
645 if(hover) {
646 TrackingList[i].tme.dwFlags |= TME_HOVER;
647 TrackingList[i].tme.dwHoverTime = ptme->dwHoverTime;
648 }
649
650 if(leave)
651 TrackingList[i].tme.dwFlags |= TME_LEAVE;
652
653 /* reset iHoverTime as per winapi specs */
654 TrackingList[i].iHoverTime = 0;
655
656 return TRUE;
657 }
658 }
659
660 /* if the tracking list is full return FALSE */
661 if (iTrackMax == sizeof (TrackingList) / sizeof(*TrackingList)) {
662 return FALSE;
663 }
664
665 /* Adding new mouse event to the tracking list */
666 TrackingList[iTrackMax].tme = *ptme;
667
668 /* Initialize HoverInfo variables even if not hover tracking */
669 TrackingList[iTrackMax].iHoverTime = 0;
670 TrackingList[iTrackMax].pos = pos;
671
672 iTrackMax++;
673
674 if (!timer) {
675 timer = SetTimer(0, 0, iTimerInterval, (TIMERPROC)TrackMouseEventProc);
676 }
677 }
678 }
679
680 return TRUE;
681}
682//******************************************************************************
683//******************************************************************************
Note: See TracBrowser for help on using the repository browser.