|
|
/*****************************************************************************
* * DIEmM.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Emulation module for mouse. * * Contents: * * CEm_Mouse_CreateInstance * CEm_Mouse_InitButtons * CEm_LL_MseHook * *****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
* * The sqiffle for this file. * *****************************************************************************/
#define sqfl sqflEm
/*****************************************************************************
* * Mouse globals * *****************************************************************************/
STDMETHODIMP CEm_Mouse_Acquire(PEM this, BOOL fAcquire);
DIMOUSESTATE_INT s_msEd;
ED s_edMouse = { &s_msEd, 0, CEm_Mouse_Acquire, -1, cbX(DIMOUSESTATE_INT), 0x0, };
/*****************************************************************************
* * The algorithm for applying acceleration is: * * dxC = dxR * if A >= 1 and abs(dxR) > T1 then * dxC = dxR * 2 * if A >= 2 and abs(dxR) > Thres2 then * dxC = dxR * 4 * end if * end if * * where * dxR is the raw mouse motion * dxC is the cooked mouse motion * A is the acceleration * T1 is the first threshold * T2 is the second threshold * * Repeat for dy instead of dx. * * We can optimize this by simply setting the thresholds to MAXLONG * if they are disabled; that way, abs(dx) will never exceed it. * * The result is the following piecewise linear function: * * if 0 < abs(dxR) <= T1: dxC = dxR * if T1 < abs(dxR) <= T2: dxC = dxR * 2 * if T2 < abs(dxR): dxC = dxR * 4 * * If you graph this function, you'll see that it's discontinuous! * * The inverse mapping of this function is what concerns us. * It looks like this: * * if 0 < abs(dxC) <= T1: dxR = dxC * if T1 * 2 < abs(dxC) <= T2 * 2: dxR = dxC / 2 * if T2 * 4 < abs(dxC): dxR = dxC / 4 * * Notice that there are gaps in the graph, so we can fill them in * any way we want, as long as it isn't blatantly *wrong*. (In the * case where we are using emulation, it is possible to get relative * mouse motions that live in the "impossible" limbo zone due to * clipping.) * * if 0 < abs(dxC) <= T1: dxR = dxC * if T1 < abs(dxC) <= T2 * 2: dxR = dxC / 2 * if T2 * 2 < abs(dxC): dxR = dxC / 4 * * Therefore: (you knew the punch line was coming) * * s_rgiMouseThresh[0] = T1 (or MAXLONG) * s_rgiMouseThresh[1] = T2 * 2 (or MAXLONG) * * *****************************************************************************/
static int s_rgiMouseThresh[2];
/*****************************************************************************
* * @doc INTERNAL * * @func void | CEm_Mouse_OnMouseChange | * * The mouse acceleration changed. Go recompute the * unacceleration variables. * *****************************************************************************/
void EXTERNAL CEm_Mouse_OnMouseChange(void) { int rgi[3]; /* Mouse acceleration information */
/*
* See the huge comment block at the definition of * s_rgiMouseThresh for an explanation of the math * that is happening here. * * If acceleration is enabled at all... */
if (SystemParametersInfo(SPI_GETMOUSE, 0, &rgi, 0) && rgi[2]) { s_rgiMouseThresh[0] = rgi[0];
if (rgi[2] >= 2) { s_rgiMouseThresh[1] = rgi[1] * 2;
} else { /* Disable level 2 acceleration */ s_rgiMouseThresh[1] = MAXLONG; }
} else { /* Disable all acceleration */ s_rgiMouseThresh[0] = MAXLONG; }
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_OnMouseChange: ") TEXT("New accelerations %d / %d"), s_rgiMouseThresh[0], s_rgiMouseThresh[1]);
}
/*****************************************************************************
* * Mouse emulation * * Mouse emulation is done by subclassing the window that * captured the mouse. We then do the following things: * * (1) Hide the cursor for the entire vwi. * * (2) Capture the mouse. * * (3) Clip the cursor to the window. (If we let the cursor * leave our window, then it messes up capture.) * * (4) Keep re-centering the mouse whenever it moves. * * (5) Release the capture on WM_SYSCOMMAND so we don't * mess up menus, Alt+F4, etc. * * If we are using NT low-level hooks then mouse emulation * is done by spinning a thread to service ll hook * notifications. The victim window is not subclassed. * *****************************************************************************/
#define dxMinMouse 10
#define dyMinMouse 10
typedef struct MOUSEEMULATIONINFO { POINT ptCenter; /* Center of client rectangle (screen coords) */ POINT ptCenterCli; /* Center of client rectangle (client coords) */ LPARAM lpCenter; /* ptCenter in the form of an LPARAM */
BOOL fInitialized:1; /* Have we gotten started? */ BOOL fNeedExit:1; /* Should we leave now? */ BOOL fExiting:1; /* Are we trying to leave already? */ BOOL fCaptured:1; /* Have we captured the mouse? */ BOOL fHidden:1; /* Have we hidden the mouse? */ BOOL fClipped:1; /* Have we clipped the mouse? */
RECT rcClip; /* ClipCursor rectangle */
} MOUSEEMULATIONINFO, *PMOUSEEMULATIONINFO;
LRESULT CALLBACK CEm_Mouse_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uid, ULONG_PTR dwRef);
/*****************************************************************************
* * CEm_Mouse_InitCoords * * *****************************************************************************/
BOOL INTERNAL CEm_Mouse_InitCoords(HWND hwnd, PMOUSEEMULATIONINFO this) { RECT rcClient; RECT rcDesk;
GetClientRect(hwnd, &rcClient); MapWindowPoints(hwnd, 0, (LPPOINT)&rcClient, 2);
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Client (%d,%d)-(%d,%d)"), rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
/*
* Clip this with the screen, in case the window extends * off-screen. * * Someday: This will need to change when we get multiple monitors. */ GetWindowRect(GetDesktopWindow(), &rcDesk);
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Desk (%d,%d)-(%d,%d)"), rcDesk.left, rcDesk.top, rcDesk.right, rcDesk.bottom);
IntersectRect(&this->rcClip, &rcDesk, &rcClient);
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Clip (%d,%d)-(%d,%d)"), this->rcClip.left, this->rcClip.top, this->rcClip.right, this->rcClip.bottom);
this->ptCenter.x = (this->rcClip.left + this->rcClip.right) >> 1; this->ptCenter.y = (this->rcClip.top + this->rcClip.bottom) >> 1;
this->ptCenterCli.x = this->ptCenter.x - rcClient.left; this->ptCenterCli.y = this->ptCenter.y - rcClient.top;
this->lpCenter = MAKELPARAM(this->ptCenterCli.x, this->ptCenterCli.y);
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: lpCenter (%d, %d)"), MAKEPOINTS(this->lpCenter).x, MAKEPOINTS(this->lpCenter).y);
return this->rcClip.bottom - this->rcClip.top > dyMinMouse && this->rcClip.right - this->rcClip.left > dxMinMouse; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | CEm_Mouse_OnSettingChange | * * If the mouse acceleration changed, then update our globals * so we can unaccelerate the mouse properly. * * @parm WPARAM | wp | * * SystemParametersInfo value. * * @parm LPARAM | lp | * * Name of section that changed. * *****************************************************************************/
void INTERNAL CEm_Mouse_OnSettingChange(WPARAM wp, LPARAM lp) { /*
* If wp is nonzero, then it is an SPI value. * * If wp is zero, then be paranoid if lp == 0 or lp = "windows". */ switch (wp) {
case 0: /* wp == 0; must test lp */ if (lp == 0) { CEm_Mouse_OnMouseChange(); } else if (lstrcmpi((LPTSTR)lp, TEXT("windows")) == 0) { CEm_Mouse_OnMouseChange(); } break;
case SPI_SETMOUSE: CEm_Mouse_OnMouseChange(); break;
default: /* Some other SPI */ break; }
}
/*****************************************************************************
* * CEm_Mouse_Subclass_OnNull * * WM_NULL is a nudge message that makes us reconsider our * place in the world. * * We need this special signal because you cannot call * SetCapture() or ReleaseCapture() from the wrong thread. * *****************************************************************************/
void INTERNAL CEm_Mouse_Subclass_OnNull(HWND hwnd, PMOUSEEMULATIONINFO this) { /*
* Initialize me if I haven't been already. */ if (!this->fInitialized) {
this->fInitialized = 1;
if (!this->fCaptured) { this->fCaptured = 1; SetCapture(hwnd); }
if (!this->fHidden) { this->fHidden = 1; SquirtSqflPtszV(sqflCursor, TEXT("CEm_Mouse_Subclass: Hiding mouse")); ShowCursor(0); }
/*
* Remove any clipping we performed so our math * comes out right again. */ if (this->fClipped) { this->fClipped = 0; ClipCursor(0); }
/*
* (Re)compute mouse acceleration information. */ CEm_Mouse_OnMouseChange();
if (CEm_Mouse_InitCoords(hwnd, this)) {
/*
* Force the LBUTTON up during the recentering move. * * Otherwise, if the user activates the app by clicking * the title bar, USER sees the cursor move while the * left button is down on the title bar and moves the * window. Oops. * * We don't bother forcing the mouse back down after we * have recentered. I can't figure out how, and it's * not worth it. * */ if (GetAsyncKeyState(VK_LBUTTON) < 0) { mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); }
SetCursorPos(this->ptCenter.x, this->ptCenter.y);
this->fClipped = 1; ClipCursor(&this->rcClip);
} else { /* Can't emulate; window too small */ this->fNeedExit = 1; }
}
if (this->fNeedExit && !this->fExiting) {
/*
* Must do this first! ReleaseCapture() will re-enter us, * and if we continued onward, we'd end up partying on freed * memory. */ this->fExiting = 1;
if (this->fCaptured) { ReleaseCapture(); } if (this->fHidden) { SquirtSqflPtszV(sqflCursor, TEXT("CEm_Mouse_Subclass: Showing mouse")); ShowCursor(1); }
if (this->fClipped) { ClipCursor(0); }
CEm_ForceDeviceUnacquire(&s_edMouse, FDUFL_NORMAL);
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Subclass %p unhook"), hwnd); ConfirmF(RemoveWindowSubclass(hwnd, CEm_Mouse_SubclassProc, 0)); FreePv(this);
}
}
/*****************************************************************************
* * @doc INTERNAL * * @func void | CEm_Mouse_RemoveAccel | * * Remove any acceleration from the mouse motion. * * See the huge comment block at s_rgiMouseThresh * for an explanation of what we are doing. * * @parm int | dx | * * Change in coordinate, either dx or dy. * *****************************************************************************/
int INTERNAL CEm_Mouse_RemoveAccel(int dx) { int x = abs(dx); if (x > s_rgiMouseThresh[0]) { dx /= 2; if (x > s_rgiMouseThresh[1]) { dx /= 2; } } return dx; }
/*****************************************************************************
* * @doc EXTERNAL * * @func void | CEm_Mouse_AddState | * * Add a mouse state change. * * The mouse coordinates are relative, not absolute. * * @parm LPDIMOUSESTATE_INT | pms | * * New mouse state, except that coordinates are relative. * * @parm DWORD | tm | * * Time the state change was generated. * *****************************************************************************/
void EXTERNAL CEm_Mouse_AddState(LPDIMOUSESTATE_INT pms, DWORD tm) {
/* Sanity check: Make sure the device has been initialized */ if( s_edMouse.pDevType ) { pms->lX = s_msEd.lX + pms->lX; pms->lY = s_msEd.lY + pms->lY;
/*
* HACK! * * Memphis and NT5 USER both mess up the case where the presence * of a wheel mouse changes dynamically. So if we do not have * a wheel in our data format, then don't record it. * * The consequence of this is that we will not see any more * buttons or wheels than were present when we queried the number * of buttons in the first place. */
/* If we use Subclassing, the movement of wheel can't be accumulated.
* Otherwise, you will see the number keep increasing. Fix bug: 182774. * However, if we use low level hook, we need the code. Fix bug: 238987 */
#ifdef USE_SLOW_LL_HOOKS
if (s_edMouse.pDevType[DIMOFS_Z]) { pms->lZ = s_msEd.lZ + pms->lZ; } #endif
CEm_AddState(&s_edMouse, pms, tm); } }
/*****************************************************************************
* * Mouse window subclass procedure * *****************************************************************************/
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST + 1)
#endif
#define WM_SETACQUIRE WM_USER
#define WM_QUITSELF (WM_USER+1)
LRESULT CALLBACK CEm_Mouse_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp, UINT_PTR uid, ULONG_PTR dwRef) { PMOUSEEMULATIONINFO this = (PMOUSEEMULATIONINFO)dwRef; DIMOUSESTATE_INT ms; static BOOL fWheelScrolling = FALSE;
switch (wm) {
case WM_NCDESTROY: SquirtSqflPtszV(sqfl, TEXT("CEm_Subclass: window destroyed while acquired")); goto unhook;
case WM_CAPTURECHANGED: /*
* "An application should not attempt to set the mouse capture * in response to [WM_CAPTURECHANGED]." * * So we just unhook. */ SquirtSqflPtszV(sqfl, TEXT("CEm_Subclass: %04x lost to %04x"), hwnd, lp); goto unhook;
case WM_SYSCOMMAND: /*
* We've got to unhook because WM_SYSCOMMAND will punt if * the mouse is captured. Otherwise, you couldn't type Alt+F4 * to exit the app, which is kind of a bummer. */
unhook:; // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Acquire: %p ") TEXT("exiting because of %04x"), hwnd, wm); this->fNeedExit = 1; CEm_Mouse_Subclass_OnNull(hwnd, this); break;
case WM_NULL: CEm_Mouse_Subclass_OnNull(hwnd, this); break;
/*
* Note that we use WM_WINDOWPOSCHANGED and not WM_SIZE, because * an application which doesn't send WM_WINDOWPOSCHANGED to * DefWindowProc will will never receive a WM_SIZE message. * * We need to start over to handle the new screen dimensions, * recenter the mouse, and possibly abandon the operation if * things don't look right. */ case WM_WINDOWPOSCHANGED: case WM_DISPLAYCHANGE: this->fInitialized = 0; CEm_Mouse_Subclass_OnNull(hwnd, this); break;
/*
* The mouse acceleration may have changed. */ case WM_SETTINGCHANGE: CEm_Mouse_OnSettingChange(wp, lp); break;
case WM_MOUSEWHEEL: SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_SubclassProc: (%d,%d,%d)"), MAKEPOINTS(lp).x, MAKEPOINTS(lp).y, (short)HIWORD(wp));
ms.lZ = (short)HIWORD(wp); fWheelScrolling = TRUE;
goto lparam;
case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
case WM_XBUTTONDOWN: case WM_XBUTTONUP: case WM_XBUTTONDBLCLK: #endif
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_SubclassProc: (%d,%d)"), MAKEPOINTS(lp).x, MAKEPOINTS(lp).y);
ms.lZ = 0; lparam:;
/*
* Don't move the cursor if it hasn't moved. * Otherwise, we recurse ourselves to death. * * In fact, if the cursor hasn't moved, ignore this * motion and do only buttons. Otherwise, you get * into the situation where we end up reacting to * our own recentering. (D'oh!) */ ms.lX = 0; ms.lY = 0;
if (lp != this->lpCenter && !fWheelScrolling ) { SetCursorPos(this->ptCenter.x, this->ptCenter.y); ms.lX = MAKEPOINTS(lp).x - this->ptCenterCli.x; ms.lY = MAKEPOINTS(lp).y - this->ptCenterCli.y; }
fWheelScrolling = FALSE;
/*
* Note that these return unswapped mouse button data. * Arguably a bug, but it's documented, so it's now a * feature. */ #define GetButton(n) ((GetAsyncKeyState(n) & 0x8000) >> 8)
ms.rgbButtons[0] = GetButton(VK_LBUTTON); ms.rgbButtons[1] = GetButton(VK_RBUTTON); ms.rgbButtons[2] = GetButton(VK_MBUTTON); #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
ms.rgbButtons[3] = GetButton(VK_XBUTTON1); ms.rgbButtons[4] = GetButton(VK_XBUTTON2); #else
ms.rgbButtons[3] = 0; ms.rgbButtons[4] = 0; #endif
ms.rgbButtons[5] = 0; ms.rgbButtons[6] = 0; ms.rgbButtons[7] = 0;
#undef GetButton
/*
* Note that we cannot unaccelerate the mouse when using * mouse capture, because we don't know what sort of * coalescing USER has done for us. */
CEm_Mouse_AddState(&ms, GetMessageTime());
return 0;
}
return DefSubclassProc(hwnd, wm, wp, lp); }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | CEm_Mouse_Subclass_Acquire | * * Acquire/unacquire a mouse via subclassing. * * @parm PEM | pem | * * Device being acquired. * * @parm BOOL | fAcquire | * * Whether the device is being acquired or unacquired. * *****************************************************************************/
STDMETHODIMP CEm_Mouse_Subclass_Acquire(PEM this, BOOL fAcquire) { HRESULT hres; EnterProc(CEm_Mouse_Subclass_Acquire, (_ "pu", this, fAcquire));
AssertF(this->dwSignature == CEM_SIGNATURE);
if (fAcquire) { /* Install the hook */ if (this->vi.hwnd && (this->vi.fl & VIFL_CAPTURED)) { PMOUSEEMULATIONINFO pmei; /*
* Prefix considers this memory leaked (mb:34652) because we * don't save the pointer here. The memory is freed when the * hook is removed so this is not really a problem. */ hres = AllocCbPpv(cbX(MOUSEEMULATIONINFO), &pmei); if (SUCCEEDED(hres)) { if (SetWindowSubclass(this->vi.hwnd, CEm_Mouse_SubclassProc, 0, (ULONG_PTR)pmei)) { /* Nudge it */ SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L); hres = S_OK; } else { // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("Mouse::Acquire: ") TEXT("Window %p is not valid"), this->vi.hwnd); FreePv(pmei); hres = E_INVALIDARG; } }
} else { RPF("Mouse::Acquire: Non-exclusive mode not supported"); hres = E_FAIL; } } else { /* Remove the hook */ PMOUSEEMULATIONINFO pmei; if (GetWindowSubclass(this->vi.hwnd, CEm_Mouse_SubclassProc, 0, (PULONG_PTR)&pmei)) { // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Acquire: ") TEXT("Telling %p to exit"), this->vi.hwnd); pmei->fNeedExit = 1; SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L); } else { /* Window was already unhooked */ } hres = S_OK; }
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | CEm_Mouse_Acquire | * * Acquire/unacquire a mouse. * * @parm PEM | pem | * * Device being acquired. * * Whether the device is being acquired or unacquired. * *****************************************************************************/
STDMETHODIMP CEm_Mouse_Acquire(PEM this, BOOL fAcquire) { HRESULT hres; EnterProc(CEm_Mouse_Acquire, (_ "pu", this, fAcquire));
AssertF(this->dwSignature == CEM_SIGNATURE);
#ifdef USE_SLOW_LL_HOOKS
AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE || DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2);
if (this->vi.fl & DIMAKEEMFL(DIEMFL_MOUSE)) { /*
* This used to use the subclass technique for exclusive mode * even if low-level hooks were available because low-level * hooks turn out to be even slower that subclassing. However, * subclassing is not transparent as it uses SetCapture which * causes Accellerator translation to be disabled for the app * which would be a more serious regression from Win9x than * performance being even worse than we thought. */ AssertF(g_fUseLLHooks); hres = CEm_LL_Acquire(this, fAcquire, this->vi.fl, LLTS_MSE); if( SUCCEEDED(hres) ) { if( fAcquire && this->vi.fl & VIFL_CAPTURED ) { if( !this->fHidden ) { ShowCursor(0); this->fHidden = TRUE; } } else { if( this->fHidden ) { ShowCursor(1); this->fHidden = FALSE; } } } } else { hres = CEm_Mouse_Subclass_Acquire(this, fAcquire); } #else
AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2); hres = CEm_Mouse_Subclass_Acquire(this, fAcquire); #endif
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | CEm_Mouse_CreateInstance | * * Create a mouse thing. Also record what emulation * level we ended up with so the caller knows. * * @parm PVXDDEVICEFORMAT | pdevf | * * What the object should look like. * * @parm PVXDINSTANCE * | ppviOut | * * The answer goes here. * *****************************************************************************/
HRESULT EXTERNAL CEm_Mouse_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut) { HRESULT hres;
#ifdef USE_SLOW_LL_HOOKS
/*
* Note carefully the test. It handles the cases where * * 0. The app did not ask for emulation, so we give it the * best we can. (dwEmulation == 0) * 1. The app explicitly asked for emulation 1. * (dwEmulation == DIEMFL_MOUSE) * 2. The app explicitly asked for emulation 2. * (dwEmulation == DIEMFL_MOUSE2) * 3. The registry explicitly asked for both emulation modes. * (dwEmulation == DIEMFL_MOUSE | DIEMFL_MOUSE2) * Give it the best we can. (I.e., same as case 0.) * * All platforms support emulation 2. Not all platforms support * emulation 1. If we want emulation 1 but can't get it, then * we fall back on emulation 2. */
/*
* First, if we don't have support for emulation 1, then clearly * we have to use emulation 2. */ if (!g_fUseLLHooks #ifdef DEBUG
|| (g_flEmulation & DIEMFL_MOUSE2) #endif
) { pdevf->dwEmulation = DIEMFL_MOUSE2; } else
/*
* Otherwise, we have to choose between 1 and 2. The only case * where we choose 2 is if 2 is explicitly requested. */ if (pdevf->dwEmulation == DIEMFL_MOUSE2) { /* Do nothing */ } else
/*
* All other cases get 1. */ { pdevf->dwEmulation = DIEMFL_MOUSE; }
/*
* Assert that we never give emulation 1 when it doesn't exist. */ AssertF(fLimpFF(pdevf->dwEmulation & DIEMFL_MOUSE, g_fUseLLHooks)); #else
/*
* We are being compiled for "emulation 2 only", so that simplifies * matters immensely. */ pdevf->dwEmulation = DIEMFL_MOUSE2; #endif
hres = CEm_CreateInstance(pdevf, ppviOut, &s_edMouse);
return hres;
}
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | CEm_Mouse_InitButtons | * * Initialize the mouse button state in preparation for * acquisition. * * @parm PVXDDWORDDATA | pvdd | * * The button states. * *****************************************************************************/
HRESULT EXTERNAL CEm_Mouse_InitButtons(PVXDDWORDDATA pvdd) { /* Do this only when nothing is yet acquired */ if (s_edMouse.cAcquire < 0) { *(LPDWORD)&s_msEd.rgbButtons = pvdd->dw;
/* randomly initializing axes as well as mouse buttons
X and Y are not buttons Randomize initial values of X and Y */ while( !s_msEd.lX ) { s_msEd.lX = GetTickCount(); s_msEd.lY = (s_msEd.lX << 16) | (s_msEd.lX >> 16); s_msEd.lX = s_msEd.lY * (DWORD)((UINT_PTR)&pvdd); } } return S_OK; }
#ifdef USE_SLOW_LL_HOOKS
/*****************************************************************************
* * @doc INTERNAL * * @func LRESULT | CEm_LL_MseHook | * * Low-level mouse hook filter. * * @parm int | nCode | * * Notification code. * * @parm WPARAM | wp | * * WM_* mouse message. * * @parm LPARAM | lp | * * Mouse message information. * * @returns * * Always chains to the next hook. * *****************************************************************************/
LRESULT CALLBACK CEm_LL_MseHook(int nCode, WPARAM wp, LPARAM lp) { PLLTHREADSTATE plts;
if (nCode == HC_ACTION) { DIMOUSESTATE_INT ms; POINT pt; PMSLLHOOKSTRUCT pllhs = (PV)lp;
/*
* We are called only on mouse messages, so we may as * well prepare ourselves up front. * * Note! that we *cannot* use GetAsyncKeyState on the * buttons, because the buttons haven't been pressed yet! * Instead, we must update the button state based on the * received message. */
ms.lX = 0; ms.lY = 0; ms.lZ = 0;
memcpy(ms.rgbButtons, s_msEd.rgbButtons, cbX(ms.rgbButtons));
/*
* * Annoying! We receive swapped buttons, so we need to * unswap them. I mark this as `annoying' because * GetAsyncKeyState returns unswapped buttons, so sometimes * I do and sometimes I don't. But it isn't `*wrong*' * because it is the right thing. Arguably, GetAsyncKeyState * is the one that is broken. */
if (GetSystemMetrics(SM_SWAPBUTTON)) {
/*
* Assert that the left and right button messages * run in parallel. */
CAssertF(WM_RBUTTONDOWN - WM_LBUTTONDOWN == WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK && WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK == WM_RBUTTONUP - WM_LBUTTONUP);
switch (wp) {
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: wp = (wp - WM_LBUTTONUP) + WM_RBUTTONUP; break;
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: wp = (wp - WM_RBUTTONUP) + WM_LBUTTONUP; break;
} }
switch (wp) { /* wp = message number */
case WM_MOUSEWHEEL: SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d,%d)"), pllhs->pt.x, pllhs->pt.y, pllhs->mouseData);
ms.lZ = (short int)HIWORD(pllhs->mouseData); goto lparam;
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: ms.rgbButtons[0] = 0x80; goto move;
case WM_LBUTTONUP: ms.rgbButtons[0] = 0x00; goto move;
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: ms.rgbButtons[1] = 0x80; goto move;
case WM_RBUTTONUP: ms.rgbButtons[1] = 0x00; goto move;
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: ms.rgbButtons[2] = 0x80; goto move;
case WM_MBUTTONUP: ms.rgbButtons[2] = 0x00; goto move;
#if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: /*
* Using switch can be easily extended to support more buttons. */ switch ( HIWORD(pllhs->mouseData) ) { case XBUTTON1: ms.rgbButtons[3] = 0x80; break;
case XBUTTON2: ms.rgbButtons[4] = 0x80; break;
/*
* When we support more than 5 buttons, take care of them. case XBUTTON3: ms.rgbButtons[5] = 0x80; break;
case XBUTTON4: ms.rgbButtons[6] = 0x80; break;
case XBUTTON5: ms.rgbButtons[7] = 0x80; break; */ } goto move;
case WM_XBUTTONUP: /*
* Using switch can be easily extended to support more buttons. */ switch ( HIWORD(pllhs->mouseData) ) { case XBUTTON1: ms.rgbButtons[3] = 0x00; break;
case XBUTTON2: ms.rgbButtons[4] = 0x00; break; /*
* When we support more than 5 buttons, take care of them. case XBUTTON3: ms.rgbButtons[5] = 0x00; break;
case XBUTTON4: ms.rgbButtons[6] = 0x00; break;
case XBUTTON5: ms.rgbButtons[7] = 0x00; break; */ } goto move; #endif
case WM_MOUSEMOVE: SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d)"), pllhs->pt.x, pllhs->pt.y);
move:;
lparam:;
GetCursorPos(&pt);
ms.lX = CEm_Mouse_RemoveAccel(pllhs->pt.x - pt.x); ms.lY = CEm_Mouse_RemoveAccel(pllhs->pt.y - pt.y);
CEm_Mouse_AddState(&ms, GetTickCount());
}
}
/*
* Eat the message by returning non-zero if at least one client * is exclusive. */
plts = g_plts; if (plts) { LRESULT rc;
rc = CallNextHookEx(plts->rglhs[LLTS_MSE].hhk, nCode, wp, lp); if (!plts->rglhs[LLTS_MSE].cExcl) { return rc; } } else { /*
* This can happen if a message gets posted to the hook after * releasing the last acquire but before we completely unhook. */ RPF( "DINPUT: Mouse hook not passed on to next hook" ); }
return 1; }
#endif /* USE_SLOW_LL_HOOKS */
|