Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1176 lines
34 KiB

  1. /*****************************************************************************
  2. *
  3. * DIEmM.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * Emulation module for mouse.
  10. *
  11. * Contents:
  12. *
  13. * CEm_Mouse_CreateInstance
  14. * CEm_Mouse_InitButtons
  15. * CEm_LL_MseHook
  16. *
  17. *****************************************************************************/
  18. #include "dinputpr.h"
  19. /*****************************************************************************
  20. *
  21. * The sqiffle for this file.
  22. *
  23. *****************************************************************************/
  24. #define sqfl sqflEm
  25. /*****************************************************************************
  26. *
  27. * Mouse globals
  28. *
  29. *****************************************************************************/
  30. STDMETHODIMP CEm_Mouse_Acquire(PEM this, BOOL fAcquire);
  31. DIMOUSESTATE_INT s_msEd;
  32. ED s_edMouse = {
  33. &s_msEd,
  34. 0,
  35. CEm_Mouse_Acquire,
  36. -1,
  37. cbX(DIMOUSESTATE_INT),
  38. 0x0,
  39. };
  40. /*****************************************************************************
  41. *
  42. * The algorithm for applying acceleration is:
  43. *
  44. * dxC = dxR
  45. * if A >= 1 and abs(dxR) > T1 then
  46. * dxC = dxR * 2
  47. * if A >= 2 and abs(dxR) > Thres2 then
  48. * dxC = dxR * 4
  49. * end if
  50. * end if
  51. *
  52. * where
  53. * dxR is the raw mouse motion
  54. * dxC is the cooked mouse motion
  55. * A is the acceleration
  56. * T1 is the first threshold
  57. * T2 is the second threshold
  58. *
  59. * Repeat for dy instead of dx.
  60. *
  61. * We can optimize this by simply setting the thresholds to MAXLONG
  62. * if they are disabled; that way, abs(dx) will never exceed it.
  63. *
  64. * The result is the following piecewise linear function:
  65. *
  66. * if 0 < abs(dxR) <= T1: dxC = dxR
  67. * if T1 < abs(dxR) <= T2: dxC = dxR * 2
  68. * if T2 < abs(dxR): dxC = dxR * 4
  69. *
  70. * If you graph this function, you'll see that it's discontinuous!
  71. *
  72. * The inverse mapping of this function is what concerns us.
  73. * It looks like this:
  74. *
  75. * if 0 < abs(dxC) <= T1: dxR = dxC
  76. * if T1 * 2 < abs(dxC) <= T2 * 2: dxR = dxC / 2
  77. * if T2 * 4 < abs(dxC): dxR = dxC / 4
  78. *
  79. * Notice that there are gaps in the graph, so we can fill them in
  80. * any way we want, as long as it isn't blatantly unintelegent. (In the
  81. * case where we are using emulation, it is possible to get relative
  82. * mouse motions that live in the "impossible" limbo zone due to
  83. * clipping.)
  84. *
  85. * if 0 < abs(dxC) <= T1: dxR = dxC
  86. * if T1 < abs(dxC) <= T2 * 2: dxR = dxC / 2
  87. * if T2 * 2 < abs(dxC): dxR = dxC / 4
  88. *
  89. * Therefore: (you knew the punch line was coming)
  90. *
  91. * s_rgiMouseThresh[0] = T1 (or MAXLONG)
  92. * s_rgiMouseThresh[1] = T2 * 2 (or MAXLONG)
  93. *
  94. *
  95. *****************************************************************************/
  96. static int s_rgiMouseThresh[2];
  97. /*****************************************************************************
  98. *
  99. * @doc INTERNAL
  100. *
  101. * @func void | CEm_Mouse_OnMouseChange |
  102. *
  103. * The mouse acceleration changed. Go recompute the
  104. * unacceleration variables.
  105. *
  106. *****************************************************************************/
  107. void EXTERNAL
  108. CEm_Mouse_OnMouseChange(void)
  109. {
  110. int rgi[3]; /* Mouse acceleration information */
  111. /*
  112. * See the huge comment block at the definition of
  113. * s_rgiMouseThresh for an explanation of the math
  114. * that is happening here.
  115. *
  116. * If acceleration is enabled at all...
  117. */
  118. if (SystemParametersInfo(SPI_GETMOUSE, 0, &rgi, 0) && rgi[2]) {
  119. s_rgiMouseThresh[0] = rgi[0];
  120. if (rgi[2] >= 2) {
  121. s_rgiMouseThresh[1] = rgi[1] * 2;
  122. } else { /* Disable level 2 acceleration */
  123. s_rgiMouseThresh[1] = MAXLONG;
  124. }
  125. } else { /* Disable all acceleration */
  126. s_rgiMouseThresh[0] = MAXLONG;
  127. }
  128. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_OnMouseChange: ")
  129. TEXT("New accelerations %d / %d"),
  130. s_rgiMouseThresh[0], s_rgiMouseThresh[1]);
  131. }
  132. /*****************************************************************************
  133. *
  134. * Mouse emulation
  135. *
  136. * Mouse emulation is done by subclassing the window that
  137. * captured the mouse. We then do the following things:
  138. *
  139. * (1) Hide the cursor for the entire vwi.
  140. *
  141. * (2) Capture the mouse.
  142. *
  143. * (3) Clip the cursor to the window. (If we let the cursor
  144. * leave our window, then it screws up capture.)
  145. *
  146. * (4) Keep re-centering the mouse whenever it moves.
  147. *
  148. * (5) Release the capture on WM_SYSCOMMAND so we don't
  149. * mess up menus, Alt+F4, etc.
  150. *
  151. * If we are using NT low-level hooks then mouse emulation
  152. * is done by spinning a thread to service ll hook
  153. * notifications. The victim window is not subclassed.
  154. *
  155. *****************************************************************************/
  156. #define dxMinMouse 10
  157. #define dyMinMouse 10
  158. typedef struct MOUSEEMULATIONINFO {
  159. POINT ptCenter; /* Center of client rectangle (screen coords) */
  160. POINT ptCenterCli; /* Center of client rectangle (client coords) */
  161. LPARAM lpCenter; /* ptCenter in the form of an LPARAM */
  162. BOOL fInitialized:1; /* Have we gotten started? */
  163. BOOL fNeedExit:1; /* Should we leave now? */
  164. BOOL fExiting:1; /* Are we trying to leave already? */
  165. BOOL fCaptured:1; /* Have we captured the mouse? */
  166. BOOL fHidden:1; /* Have we hidden the mouse? */
  167. BOOL fClipped:1; /* Have we clipped the mouse? */
  168. RECT rcClip; /* ClipCursor rectangle */
  169. } MOUSEEMULATIONINFO, *PMOUSEEMULATIONINFO;
  170. LRESULT CALLBACK
  171. CEm_Mouse_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp,
  172. UINT_PTR uid, ULONG_PTR dwRef);
  173. /*****************************************************************************
  174. *
  175. * CEm_Mouse_InitCoords
  176. *
  177. *
  178. *****************************************************************************/
  179. BOOL INTERNAL
  180. CEm_Mouse_InitCoords(HWND hwnd, PMOUSEEMULATIONINFO this)
  181. {
  182. RECT rcClient;
  183. RECT rcDesk;
  184. GetClientRect(hwnd, &rcClient);
  185. MapWindowPoints(hwnd, 0, (LPPOINT)&rcClient, 2);
  186. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Client (%d,%d)-(%d,%d)"),
  187. rcClient.left,
  188. rcClient.top,
  189. rcClient.right,
  190. rcClient.bottom);
  191. /*
  192. * Clip this with the screen, in case the window extends
  193. * off-screen.
  194. *
  195. * Someday: This will need to change when we get multiple monitors.
  196. */
  197. GetWindowRect(GetDesktopWindow(), &rcDesk);
  198. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Desk (%d,%d)-(%d,%d)"),
  199. rcDesk.left,
  200. rcDesk.top,
  201. rcDesk.right,
  202. rcDesk.bottom);
  203. IntersectRect(&this->rcClip, &rcDesk, &rcClient);
  204. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: Clip (%d,%d)-(%d,%d)"),
  205. this->rcClip.left,
  206. this->rcClip.top,
  207. this->rcClip.right,
  208. this->rcClip.bottom);
  209. this->ptCenter.x = (this->rcClip.left + this->rcClip.right) >> 1;
  210. this->ptCenter.y = (this->rcClip.top + this->rcClip.bottom) >> 1;
  211. this->ptCenterCli.x = this->ptCenter.x - rcClient.left;
  212. this->ptCenterCli.y = this->ptCenter.y - rcClient.top;
  213. this->lpCenter = MAKELPARAM(this->ptCenterCli.x, this->ptCenterCli.y);
  214. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_InitCoords: lpCenter (%d, %d)"),
  215. MAKEPOINTS(this->lpCenter).x,
  216. MAKEPOINTS(this->lpCenter).y);
  217. return this->rcClip.bottom - this->rcClip.top > dyMinMouse &&
  218. this->rcClip.right - this->rcClip.left > dxMinMouse;
  219. }
  220. /*****************************************************************************
  221. *
  222. * @doc INTERNAL
  223. *
  224. * @func void | CEm_Mouse_OnSettingChange |
  225. *
  226. * If the mouse acceleration changed, then update our globals
  227. * so we can unaccelerate the mouse properly.
  228. *
  229. * @parm WPARAM | wp |
  230. *
  231. * SystemParametersInfo value.
  232. *
  233. * @parm LPARAM | lp |
  234. *
  235. * Name of section that changed.
  236. *
  237. *****************************************************************************/
  238. void INTERNAL
  239. CEm_Mouse_OnSettingChange(WPARAM wp, LPARAM lp)
  240. {
  241. /*
  242. * If wp is nonzero, then it is an SPI value.
  243. *
  244. * If wp is zero, then be paranoid if lp == 0 or lp = "windows".
  245. */
  246. switch (wp) {
  247. case 0: /* wp == 0; must test lp */
  248. if (lp == 0) {
  249. CEm_Mouse_OnMouseChange();
  250. } else if (lstrcmpi((LPTSTR)lp, TEXT("windows")) == 0) {
  251. CEm_Mouse_OnMouseChange();
  252. }
  253. break;
  254. case SPI_SETMOUSE:
  255. CEm_Mouse_OnMouseChange();
  256. break;
  257. default:
  258. /* Some other SPI */
  259. break;
  260. }
  261. }
  262. /*****************************************************************************
  263. *
  264. * CEm_Mouse_Subclass_OnNull
  265. *
  266. * WM_NULL is a nudge message that makes us reconsider our
  267. * place in the world.
  268. *
  269. * We need this special signal because you cannot call
  270. * SetCapture() or ReleaseCapture() from the wrong thread.
  271. *
  272. *****************************************************************************/
  273. void INTERNAL
  274. CEm_Mouse_Subclass_OnNull(HWND hwnd, PMOUSEEMULATIONINFO this)
  275. {
  276. /*
  277. * Initialize me if I haven't been already.
  278. */
  279. if (!this->fInitialized) {
  280. this->fInitialized = 1;
  281. if (!this->fCaptured) {
  282. this->fCaptured = 1;
  283. SetCapture(hwnd);
  284. }
  285. if (!this->fHidden) {
  286. this->fHidden = 1;
  287. SquirtSqflPtszV(sqflCursor,
  288. TEXT("CEm_Mouse_Subclass: Hiding mouse"));
  289. ShowCursor(0);
  290. }
  291. /*
  292. * Remove any clipping we performed so our math
  293. * comes out right again.
  294. */
  295. if (this->fClipped) {
  296. this->fClipped = 0;
  297. ClipCursor(0);
  298. }
  299. /*
  300. * (Re)compute mouse acceleration information.
  301. */
  302. CEm_Mouse_OnMouseChange();
  303. if (CEm_Mouse_InitCoords(hwnd, this)) {
  304. /*
  305. * Force the LBUTTON up during the recentering move.
  306. *
  307. * Otherwise, if the user activates the app by clicking
  308. * the title bar, USER sees the cursor move while the
  309. * left button is down on the title bar and moves the
  310. * window. Oops.
  311. *
  312. * We don't bother forcing the mouse back down after we
  313. * have recentered. I can't figure out how, and it's
  314. * not worth it.
  315. *
  316. */
  317. if (GetAsyncKeyState(VK_LBUTTON) < 0) {
  318. mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
  319. }
  320. SetCursorPos(this->ptCenter.x, this->ptCenter.y);
  321. this->fClipped = 1;
  322. ClipCursor(&this->rcClip);
  323. } else { /* Can't emulate; window too small */
  324. this->fNeedExit = 1;
  325. }
  326. }
  327. if (this->fNeedExit && !this->fExiting) {
  328. /*
  329. * Must do this first! ReleaseCapture() will re-enter us,
  330. * and if we continued onward, we'd end up partying on freed
  331. * memory.
  332. */
  333. this->fExiting = 1;
  334. if (this->fCaptured) {
  335. ReleaseCapture();
  336. }
  337. if (this->fHidden) {
  338. SquirtSqflPtszV(sqflCursor,
  339. TEXT("CEm_Mouse_Subclass: Showing mouse"));
  340. ShowCursor(1);
  341. }
  342. if (this->fClipped) {
  343. ClipCursor(0);
  344. }
  345. CEm_ForceDeviceUnacquire(&s_edMouse, FDUFL_NORMAL);
  346. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  347. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Subclass %p unhook"), hwnd);
  348. ConfirmF(RemoveWindowSubclass(hwnd, CEm_Mouse_SubclassProc, 0));
  349. FreePv(this);
  350. }
  351. }
  352. /*****************************************************************************
  353. *
  354. * @doc INTERNAL
  355. *
  356. * @func void | CEm_Mouse_RemoveAccel |
  357. *
  358. * Remove any acceleration from the mouse motion.
  359. *
  360. * See the huge comment block at s_rgiMouseThresh
  361. * for an explanation of what we are doing.
  362. *
  363. * @parm int | dx |
  364. *
  365. * Change in coordinate, either dx or dy.
  366. *
  367. *****************************************************************************/
  368. int INTERNAL
  369. CEm_Mouse_RemoveAccel(int dx)
  370. {
  371. int x = abs(dx);
  372. if (x > s_rgiMouseThresh[0]) {
  373. dx /= 2;
  374. if (x > s_rgiMouseThresh[1]) {
  375. dx /= 2;
  376. }
  377. }
  378. return dx;
  379. }
  380. /*****************************************************************************
  381. *
  382. * @doc EXTERNAL
  383. *
  384. * @func void | CEm_Mouse_AddState |
  385. *
  386. * Add a mouse state change.
  387. *
  388. * The mouse coordinates are relative, not absolute.
  389. *
  390. * @parm LPDIMOUSESTATE_INT | pms |
  391. *
  392. * New mouse state, except that coordinates are relative.
  393. *
  394. * @parm DWORD | tm |
  395. *
  396. * Time the state change was generated.
  397. *
  398. *****************************************************************************/
  399. void EXTERNAL
  400. CEm_Mouse_AddState(LPDIMOUSESTATE_INT pms, DWORD tm)
  401. {
  402. /* Sanity check: Make sure the device has been initialized */
  403. if( s_edMouse.pDevType )
  404. {
  405. pms->lX = s_msEd.lX + pms->lX;
  406. pms->lY = s_msEd.lY + pms->lY;
  407. /*
  408. * HACK!
  409. *
  410. * Memphis and NT5 USER both mess up the case where the presence
  411. * of a wheel mouse changes dynamically. So if we do not have
  412. * a wheel in our data format, then don't record it.
  413. *
  414. * The consequence of this is that we will not see any more
  415. * buttons or wheels than were present when we queried the number
  416. * of buttons in the first place.
  417. */
  418. /* If we use Subclassing, the movement of wheel can't be accumulated.
  419. * Otherwise, you will see the number keep increasing. Fix bug: 182774.
  420. * However, if we use low level hook, we need the code. Fix bug: 238987
  421. */
  422. #ifdef USE_SLOW_LL_HOOKS
  423. if (s_edMouse.pDevType[DIMOFS_Z]) {
  424. pms->lZ = s_msEd.lZ + pms->lZ;
  425. }
  426. #endif
  427. CEm_AddState(&s_edMouse, pms, tm);
  428. }
  429. }
  430. /*****************************************************************************
  431. *
  432. * Mouse window subclass procedure
  433. *
  434. *****************************************************************************/
  435. #ifndef WM_MOUSEWHEEL
  436. #define WM_MOUSEWHEEL (WM_MOUSELAST + 1)
  437. #endif
  438. #define WM_SETACQUIRE WM_USER
  439. #define WM_QUITSELF (WM_USER+1)
  440. LRESULT CALLBACK
  441. CEm_Mouse_SubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp,
  442. UINT_PTR uid, ULONG_PTR dwRef)
  443. {
  444. PMOUSEEMULATIONINFO this = (PMOUSEEMULATIONINFO)dwRef;
  445. DIMOUSESTATE_INT ms;
  446. static BOOL fWheelScrolling = FALSE;
  447. switch (wm) {
  448. case WM_NCDESTROY:
  449. SquirtSqflPtszV(sqfl, TEXT("CEm_Subclass: window destroyed while acquired"));
  450. goto unhook;
  451. case WM_CAPTURECHANGED:
  452. /*
  453. * "An application should not attempt to set the mouse capture
  454. * in response to [WM_CAPTURECHANGED]."
  455. *
  456. * So we just unhook.
  457. */
  458. SquirtSqflPtszV(sqfl, TEXT("CEm_Subclass: %04x lost to %04x"),
  459. hwnd, lp);
  460. goto unhook;
  461. case WM_SYSCOMMAND:
  462. /*
  463. * We've got to unhook because WM_SYSCOMMAND will punt if
  464. * the mouse is captured. Otherwise, you couldn't type Alt+F4
  465. * to exit the app, which is kind of a bummer.
  466. */
  467. unhook:;
  468. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  469. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Acquire: %p ")
  470. TEXT("exiting because of %04x"), hwnd, wm);
  471. this->fNeedExit = 1;
  472. CEm_Mouse_Subclass_OnNull(hwnd, this);
  473. break;
  474. case WM_NULL:
  475. CEm_Mouse_Subclass_OnNull(hwnd, this);
  476. break;
  477. /*
  478. * Note that we use WM_WINDOWPOSCHANGED and not WM_SIZE, because
  479. * an application which doesn't send WM_WINDOWPOSCHANGED to
  480. * DefWindowProc will will never receive a WM_SIZE message.
  481. *
  482. * We need to start over to handle the new screen dimensions,
  483. * recenter the mouse, and possibly abandon the operation if
  484. * things don't look right.
  485. */
  486. case WM_WINDOWPOSCHANGED:
  487. case WM_DISPLAYCHANGE:
  488. this->fInitialized = 0;
  489. CEm_Mouse_Subclass_OnNull(hwnd, this);
  490. break;
  491. /*
  492. * The mouse acceleration may have changed.
  493. */
  494. case WM_SETTINGCHANGE:
  495. CEm_Mouse_OnSettingChange(wp, lp);
  496. break;
  497. case WM_MOUSEWHEEL:
  498. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_SubclassProc: (%d,%d,%d)"),
  499. MAKEPOINTS(lp).x, MAKEPOINTS(lp).y, (short)HIWORD(wp));
  500. ms.lZ = (short)HIWORD(wp);
  501. fWheelScrolling = TRUE;
  502. goto lparam;
  503. case WM_MOUSEMOVE:
  504. case WM_LBUTTONDOWN:
  505. case WM_LBUTTONUP:
  506. case WM_LBUTTONDBLCLK:
  507. case WM_RBUTTONDOWN:
  508. case WM_RBUTTONUP:
  509. case WM_RBUTTONDBLCLK:
  510. case WM_MBUTTONDOWN:
  511. case WM_MBUTTONUP:
  512. case WM_MBUTTONDBLCLK:
  513. #if DIRECTINPUT_VERSION >= 0x0700
  514. #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  515. case WM_XBUTTONDOWN:
  516. case WM_XBUTTONUP:
  517. case WM_XBUTTONDBLCLK:
  518. #endif
  519. #endif
  520. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_SubclassProc: (%d,%d)"),
  521. MAKEPOINTS(lp).x, MAKEPOINTS(lp).y);
  522. ms.lZ = 0;
  523. lparam:;
  524. /*
  525. * Don't move the cursor if it hasn't moved.
  526. * Otherwise, we recurse ourselves to death.
  527. *
  528. * In fact, if the cursor hasn't moved, ignore this
  529. * motion and do only buttons. Otherwise, you get
  530. * into the situation where we end up reacting to
  531. * our own recentering. (D'oh!)
  532. */
  533. ms.lX = 0;
  534. ms.lY = 0;
  535. if (lp != this->lpCenter && !fWheelScrolling ) {
  536. SetCursorPos(this->ptCenter.x, this->ptCenter.y);
  537. ms.lX = MAKEPOINTS(lp).x - this->ptCenterCli.x;
  538. ms.lY = MAKEPOINTS(lp).y - this->ptCenterCli.y;
  539. }
  540. fWheelScrolling = FALSE;
  541. /*
  542. * Note that these return unswapped mouse button data.
  543. * Arguably a bug, but it's documented, so it's now a
  544. * feature.
  545. */
  546. #define GetButton(n) ((GetAsyncKeyState(n) & 0x8000) >> 8)
  547. ms.rgbButtons[0] = GetButton(VK_LBUTTON);
  548. ms.rgbButtons[1] = GetButton(VK_RBUTTON);
  549. ms.rgbButtons[2] = GetButton(VK_MBUTTON);
  550. #if DIRECTINPUT_VERSION >= 0x0700
  551. #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  552. ms.rgbButtons[3] = GetButton(VK_XBUTTON1);
  553. ms.rgbButtons[4] = GetButton(VK_XBUTTON2);
  554. #else
  555. ms.rgbButtons[3] = 0;
  556. ms.rgbButtons[4] = 0;
  557. #endif
  558. ms.rgbButtons[5] = 0;
  559. ms.rgbButtons[6] = 0;
  560. ms.rgbButtons[7] = 0;
  561. #else
  562. ms.rgbButtons[3] = 0;
  563. #endif
  564. #undef GetButton
  565. /*
  566. * Note that we cannot unaccelerate the mouse when using
  567. * mouse capture, because we don't know what sort of
  568. * coalescing USER has done for us.
  569. */
  570. CEm_Mouse_AddState(&ms, GetMessageTime());
  571. return 0;
  572. }
  573. return DefSubclassProc(hwnd, wm, wp, lp);
  574. }
  575. /*****************************************************************************
  576. *
  577. * @doc INTERNAL
  578. *
  579. * @func HRESULT | CEm_Mouse_Subclass_Acquire |
  580. *
  581. * Acquire/unacquire a mouse via subclassing.
  582. *
  583. * @parm PEM | pem |
  584. *
  585. * Device being acquired.
  586. *
  587. * @parm BOOL | fAcquire |
  588. *
  589. * Whether the device is being acquired or unacquired.
  590. *
  591. *****************************************************************************/
  592. STDMETHODIMP
  593. CEm_Mouse_Subclass_Acquire(PEM this, BOOL fAcquire)
  594. {
  595. HRESULT hres;
  596. EnterProc(CEm_Mouse_Subclass_Acquire, (_ "pu", this, fAcquire));
  597. AssertF(this->dwSignature == CEM_SIGNATURE);
  598. if (fAcquire) { /* Install the hook */
  599. if (this->vi.hwnd && (this->vi.fl & VIFL_CAPTURED)) {
  600. PMOUSEEMULATIONINFO pmei;
  601. hres = AllocCbPpv(cbX(MOUSEEMULATIONINFO), &pmei);
  602. if (SUCCEEDED(hres)) {
  603. if (SetWindowSubclass(this->vi.hwnd,
  604. CEm_Mouse_SubclassProc, 0,
  605. (ULONG_PTR)pmei)) {
  606. /* Nudge it */
  607. SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L);
  608. hres = S_OK;
  609. } else {
  610. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  611. SquirtSqflPtszV(sqfl,
  612. TEXT("Mouse::Acquire: ")
  613. TEXT("Window %p is not valid"),
  614. this->vi.hwnd);
  615. FreePv(pmei);
  616. hres = E_INVALIDARG;
  617. }
  618. }
  619. } else {
  620. RPF("Mouse::Acquire: Non-exclusive mode not supported");
  621. hres = E_FAIL;
  622. }
  623. } else { /* Remove the hook */
  624. PMOUSEEMULATIONINFO pmei;
  625. if (GetWindowSubclass(this->vi.hwnd, CEm_Mouse_SubclassProc,
  626. 0, (PULONG_PTR)&pmei)) {
  627. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  628. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Acquire: ")
  629. TEXT("Telling %p to exit"), this->vi.hwnd);
  630. pmei->fNeedExit = 1;
  631. SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L);
  632. } else { /* Window was already unhooked */
  633. }
  634. hres = S_OK;
  635. }
  636. ExitOleProc();
  637. return hres;
  638. }
  639. /*****************************************************************************
  640. *
  641. * @doc INTERNAL
  642. *
  643. * @func HRESULT | CEm_Mouse_Acquire |
  644. *
  645. * Acquire/unacquire a mouse.
  646. *
  647. * @parm PEM | pem |
  648. *
  649. * Device being acquired.
  650. *
  651. * Whether the device is being acquired or unacquired.
  652. *
  653. *****************************************************************************/
  654. STDMETHODIMP
  655. CEm_Mouse_Acquire(PEM this, BOOL fAcquire)
  656. {
  657. HRESULT hres;
  658. EnterProc(CEm_Mouse_Acquire, (_ "pu", this, fAcquire));
  659. AssertF(this->dwSignature == CEM_SIGNATURE);
  660. #ifdef USE_SLOW_LL_HOOKS
  661. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE ||
  662. DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2);
  663. if (this->vi.fl & DIMAKEEMFL(DIEMFL_MOUSE)) {
  664. /*
  665. * This used to use the subclass technique for exclusive mode
  666. * even if low-level hooks were available because low-level
  667. * hooks turn out to be even slower that subclassing. However,
  668. * subclassing is not transparent as it uses SetCapture which
  669. * causes Accellerator translation to be disabled for the app
  670. * which would be a more serious regression from Win9x than
  671. * performance being even worse than we thought.
  672. */
  673. AssertF(g_fUseLLHooks);
  674. hres = CEm_LL_Acquire(this, fAcquire, this->vi.fl, LLTS_MSE);
  675. if( SUCCEEDED(hres) ) {
  676. if( fAcquire && this->vi.fl & VIFL_CAPTURED ) {
  677. if( !this->fHidden ) {
  678. ShowCursor(0);
  679. this->fHidden = TRUE;
  680. }
  681. } else {
  682. if( this->fHidden ) {
  683. ShowCursor(1);
  684. this->fHidden = FALSE;
  685. }
  686. }
  687. }
  688. } else {
  689. hres = CEm_Mouse_Subclass_Acquire(this, fAcquire);
  690. }
  691. #else
  692. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2);
  693. hres = CEm_Mouse_Subclass_Acquire(this, fAcquire);
  694. #endif
  695. ExitOleProc();
  696. return hres;
  697. }
  698. /*****************************************************************************
  699. *
  700. * @doc INTERNAL
  701. *
  702. * @func HRESULT | CEm_Mouse_CreateInstance |
  703. *
  704. * Create a mouse thing. Also record what emulation
  705. * level we ended up with so the caller knows.
  706. *
  707. * @parm PVXDDEVICEFORMAT | pdevf |
  708. *
  709. * What the object should look like.
  710. *
  711. * @parm PVXDINSTANCE * | ppviOut |
  712. *
  713. * The answer goes here.
  714. *
  715. *****************************************************************************/
  716. HRESULT EXTERNAL
  717. CEm_Mouse_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
  718. {
  719. HRESULT hres;
  720. #ifdef USE_SLOW_LL_HOOKS
  721. /*
  722. * Note carefully the test. It handles the cases where
  723. *
  724. * 0. The app did not ask for emulation, so we give it the
  725. * best we can. (dwEmulation == 0)
  726. * 1. The app explicitly asked for emulation 1.
  727. * (dwEmulation == DIEMFL_MOUSE)
  728. * 2. The app explicitly asked for emulation 2.
  729. * (dwEmulation == DIEMFL_MOUSE2)
  730. * 3. The registry explicitly asked for both emulation modes.
  731. * (dwEmulation == DIEMFL_MOUSE | DIEMFL_MOUSE2)
  732. * Give it the best we can. (I.e., same as case 0.)
  733. *
  734. * All platforms support emulation 2. Not all platforms support
  735. * emulation 1. If we want emulation 1 but can't get it, then
  736. * we fall back on emulation 2.
  737. */
  738. /*
  739. * First, if we don't have support for emulation 1, then clearly
  740. * we have to use emulation 2.
  741. */
  742. if (!g_fUseLLHooks
  743. #ifdef DEBUG
  744. || (g_flEmulation & DIEMFL_MOUSE2)
  745. #endif
  746. ) {
  747. pdevf->dwEmulation = DIEMFL_MOUSE2;
  748. } else
  749. /*
  750. * Otherwise, we have to choose between 1 and 2. The only case
  751. * where we choose 2 is if 2 is explicitly requested.
  752. */
  753. if (pdevf->dwEmulation == DIEMFL_MOUSE2) {
  754. /* Do nothing */
  755. } else
  756. /*
  757. * All other cases get 1.
  758. */
  759. {
  760. pdevf->dwEmulation = DIEMFL_MOUSE;
  761. }
  762. /*
  763. * Assert that we never give emulation 1 when it doesn't exist.
  764. */
  765. AssertF(fLimpFF(pdevf->dwEmulation & DIEMFL_MOUSE, g_fUseLLHooks));
  766. #else
  767. /*
  768. * We are being compiled for "emulation 2 only", so that simplifies
  769. * matters immensely.
  770. */
  771. pdevf->dwEmulation = DIEMFL_MOUSE2;
  772. #endif
  773. hres = CEm_CreateInstance(pdevf, ppviOut, &s_edMouse);
  774. return hres;
  775. }
  776. /*****************************************************************************
  777. *
  778. * @doc INTERNAL
  779. *
  780. * @func HRESULT | CEm_Mouse_InitButtons |
  781. *
  782. * Initialize the mouse button state in preparation for
  783. * acquisition.
  784. *
  785. * @parm PVXDDWORDDATA | pvdd |
  786. *
  787. * The button states.
  788. *
  789. *****************************************************************************/
  790. HRESULT EXTERNAL
  791. CEm_Mouse_InitButtons(PVXDDWORDDATA pvdd)
  792. {
  793. /* Do this only when nothing is yet acquired */
  794. if (s_edMouse.cAcquire < 0) {
  795. *(LPDWORD)&s_msEd.rgbButtons = pvdd->dw;
  796. /* randomly initializing axes as well as mouse buttons
  797. X and Y are not buttons
  798. Randomize initial values of X and Y */
  799. while( !s_msEd.lX )
  800. {
  801. s_msEd.lX = GetTickCount();
  802. s_msEd.lY = (s_msEd.lX << 16) | (s_msEd.lX >> 16);
  803. s_msEd.lX = s_msEd.lY * (DWORD)((UINT_PTR)&pvdd);
  804. }
  805. }
  806. return S_OK;
  807. }
  808. #ifdef USE_SLOW_LL_HOOKS
  809. /*****************************************************************************
  810. *
  811. * @doc INTERNAL
  812. *
  813. * @func LRESULT | CEm_LL_MseHook |
  814. *
  815. * Low-level mouse hook filter.
  816. *
  817. * @parm int | nCode |
  818. *
  819. * Notification code.
  820. *
  821. * @parm WPARAM | wp |
  822. *
  823. * WM_* mouse message.
  824. *
  825. * @parm LPARAM | lp |
  826. *
  827. * Mouse message information.
  828. *
  829. * @returns
  830. *
  831. * Always chains to the next hook.
  832. *
  833. *****************************************************************************/
  834. LRESULT CALLBACK
  835. CEm_LL_MseHook(int nCode, WPARAM wp, LPARAM lp)
  836. {
  837. PLLTHREADSTATE plts;
  838. if (nCode == HC_ACTION) {
  839. DIMOUSESTATE_INT ms;
  840. POINT pt;
  841. PMSLLHOOKSTRUCT pllhs = (PV)lp;
  842. /*
  843. * We are called only on mouse messages, so we may as
  844. * well prepare ourselves up front.
  845. *
  846. * Note! that we *cannot* use GetAsyncKeyState on the
  847. * buttons, because the buttons haven't been pressed yet!
  848. * Instead, we must update the button state based on the
  849. * received message.
  850. */
  851. ms.lX = 0;
  852. ms.lY = 0;
  853. ms.lZ = 0;
  854. memcpy(ms.rgbButtons, s_msEd.rgbButtons, cbX(ms.rgbButtons));
  855. /*
  856. *
  857. * Annoying! We receive swapped buttons, so we need to
  858. * unswap them. I mark this as `annoying' because
  859. * GetAsyncKeyState returns unswapped buttons, so sometimes
  860. * I do and sometimes I don't. But it isn't unintelegent
  861. * because it is the right thing. Arguably, GetAsyncKeyState
  862. * is the one that is broken.
  863. */
  864. if (GetSystemMetrics(SM_SWAPBUTTON)) {
  865. /*
  866. * Assert that the left and right button messages
  867. * run in parallel.
  868. */
  869. CAssertF(WM_RBUTTONDOWN - WM_LBUTTONDOWN ==
  870. WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK &&
  871. WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK ==
  872. WM_RBUTTONUP - WM_LBUTTONUP);
  873. switch (wp) {
  874. case WM_LBUTTONDOWN:
  875. case WM_LBUTTONDBLCLK:
  876. case WM_LBUTTONUP:
  877. wp = (wp - WM_LBUTTONUP) + WM_RBUTTONUP;
  878. break;
  879. case WM_RBUTTONDOWN:
  880. case WM_RBUTTONDBLCLK:
  881. case WM_RBUTTONUP:
  882. wp = (wp - WM_RBUTTONUP) + WM_LBUTTONUP;
  883. break;
  884. }
  885. }
  886. switch (wp) { /* wp = message number */
  887. case WM_MOUSEWHEEL:
  888. SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d,%d)"),
  889. pllhs->pt.x,
  890. pllhs->pt.y,
  891. pllhs->mouseData);
  892. ms.lZ = (short int)HIWORD(pllhs->mouseData);
  893. goto lparam;
  894. case WM_LBUTTONDOWN:
  895. case WM_LBUTTONDBLCLK:
  896. ms.rgbButtons[0] = 0x80;
  897. goto move;
  898. case WM_LBUTTONUP:
  899. ms.rgbButtons[0] = 0x00;
  900. goto move;
  901. case WM_RBUTTONDOWN:
  902. case WM_RBUTTONDBLCLK:
  903. ms.rgbButtons[1] = 0x80;
  904. goto move;
  905. case WM_RBUTTONUP:
  906. ms.rgbButtons[1] = 0x00;
  907. goto move;
  908. case WM_MBUTTONDOWN:
  909. case WM_MBUTTONDBLCLK:
  910. ms.rgbButtons[2] = 0x80;
  911. goto move;
  912. case WM_MBUTTONUP:
  913. ms.rgbButtons[2] = 0x00;
  914. goto move;
  915. #if DIRECTINPUT_VERSION >= 0x0700
  916. #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  917. case WM_XBUTTONDOWN:
  918. case WM_XBUTTONDBLCLK:
  919. /*
  920. * Using switch can be easily extended to support more buttons.
  921. */
  922. switch ( HIWORD(pllhs->mouseData) ) {
  923. case XBUTTON1:
  924. ms.rgbButtons[3] = 0x80;
  925. break;
  926. case XBUTTON2:
  927. ms.rgbButtons[4] = 0x80;
  928. break;
  929. /*
  930. * When we support more than 5 buttons, take care of them.
  931. case XBUTTON3:
  932. ms.rgbButtons[5] = 0x80;
  933. break;
  934. case XBUTTON4:
  935. ms.rgbButtons[6] = 0x80;
  936. break;
  937. case XBUTTON5:
  938. ms.rgbButtons[7] = 0x80;
  939. break;
  940. */
  941. }
  942. goto move;
  943. case WM_XBUTTONUP:
  944. /*
  945. * Using switch can be easily extended to support more buttons.
  946. */
  947. switch ( HIWORD(pllhs->mouseData) ) {
  948. case XBUTTON1:
  949. ms.rgbButtons[3] = 0x00;
  950. break;
  951. case XBUTTON2:
  952. ms.rgbButtons[4] = 0x00;
  953. break;
  954. /*
  955. * When we support more than 5 buttons, take care of them.
  956. case XBUTTON3:
  957. ms.rgbButtons[5] = 0x00;
  958. break;
  959. case XBUTTON4:
  960. ms.rgbButtons[6] = 0x00;
  961. break;
  962. case XBUTTON5:
  963. ms.rgbButtons[7] = 0x00;
  964. break;
  965. */
  966. }
  967. goto move;
  968. #endif
  969. #endif
  970. case WM_MOUSEMOVE:
  971. SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d)"),
  972. pllhs->pt.x, pllhs->pt.y);
  973. move:;
  974. lparam:;
  975. GetCursorPos(&pt);
  976. ms.lX = CEm_Mouse_RemoveAccel(pllhs->pt.x - pt.x);
  977. ms.lY = CEm_Mouse_RemoveAccel(pllhs->pt.y - pt.y);
  978. CEm_Mouse_AddState(&ms, GetTickCount());
  979. }
  980. }
  981. /*
  982. * Eat the message by returning non-zero if at least one client
  983. * is exclusive.
  984. */
  985. plts = g_plts;
  986. if (plts) {
  987. LRESULT rc;
  988. rc = CallNextHookEx(plts->rglhs[LLTS_MSE].hhk, nCode, wp, lp);
  989. if (!plts->rglhs[LLTS_MSE].cExcl) {
  990. return rc;
  991. }
  992. } else {
  993. /*
  994. * This can happen if a message gets posted to the hook after
  995. * releasing the last acquire but before we completely unhook.
  996. */
  997. RPF( "DINPUT: Mouse hook not passed on to next hook" );
  998. }
  999. return 1;
  1000. }
  1001. #endif /* USE_SLOW_LL_HOOKS */