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

Last change on this file since 9019 was 9019, checked in by sandervl, 23 years ago

SetCursorPos fix for DirectInput

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