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.

1174 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 *wrong*. (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 messes 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 defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  514. case WM_XBUTTONDOWN:
  515. case WM_XBUTTONUP:
  516. case WM_XBUTTONDBLCLK:
  517. #endif
  518. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_SubclassProc: (%d,%d)"),
  519. MAKEPOINTS(lp).x, MAKEPOINTS(lp).y);
  520. ms.lZ = 0;
  521. lparam:;
  522. /*
  523. * Don't move the cursor if it hasn't moved.
  524. * Otherwise, we recurse ourselves to death.
  525. *
  526. * In fact, if the cursor hasn't moved, ignore this
  527. * motion and do only buttons. Otherwise, you get
  528. * into the situation where we end up reacting to
  529. * our own recentering. (D'oh!)
  530. */
  531. ms.lX = 0;
  532. ms.lY = 0;
  533. if (lp != this->lpCenter && !fWheelScrolling ) {
  534. SetCursorPos(this->ptCenter.x, this->ptCenter.y);
  535. ms.lX = MAKEPOINTS(lp).x - this->ptCenterCli.x;
  536. ms.lY = MAKEPOINTS(lp).y - this->ptCenterCli.y;
  537. }
  538. fWheelScrolling = FALSE;
  539. /*
  540. * Note that these return unswapped mouse button data.
  541. * Arguably a bug, but it's documented, so it's now a
  542. * feature.
  543. */
  544. #define GetButton(n) ((GetAsyncKeyState(n) & 0x8000) >> 8)
  545. ms.rgbButtons[0] = GetButton(VK_LBUTTON);
  546. ms.rgbButtons[1] = GetButton(VK_RBUTTON);
  547. ms.rgbButtons[2] = GetButton(VK_MBUTTON);
  548. #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  549. ms.rgbButtons[3] = GetButton(VK_XBUTTON1);
  550. ms.rgbButtons[4] = GetButton(VK_XBUTTON2);
  551. #else
  552. ms.rgbButtons[3] = 0;
  553. ms.rgbButtons[4] = 0;
  554. #endif
  555. ms.rgbButtons[5] = 0;
  556. ms.rgbButtons[6] = 0;
  557. ms.rgbButtons[7] = 0;
  558. #undef GetButton
  559. /*
  560. * Note that we cannot unaccelerate the mouse when using
  561. * mouse capture, because we don't know what sort of
  562. * coalescing USER has done for us.
  563. */
  564. CEm_Mouse_AddState(&ms, GetMessageTime());
  565. return 0;
  566. }
  567. return DefSubclassProc(hwnd, wm, wp, lp);
  568. }
  569. /*****************************************************************************
  570. *
  571. * @doc INTERNAL
  572. *
  573. * @func HRESULT | CEm_Mouse_Subclass_Acquire |
  574. *
  575. * Acquire/unacquire a mouse via subclassing.
  576. *
  577. * @parm PEM | pem |
  578. *
  579. * Device being acquired.
  580. *
  581. * @parm BOOL | fAcquire |
  582. *
  583. * Whether the device is being acquired or unacquired.
  584. *
  585. *****************************************************************************/
  586. STDMETHODIMP
  587. CEm_Mouse_Subclass_Acquire(PEM this, BOOL fAcquire)
  588. {
  589. HRESULT hres;
  590. EnterProc(CEm_Mouse_Subclass_Acquire, (_ "pu", this, fAcquire));
  591. AssertF(this->dwSignature == CEM_SIGNATURE);
  592. if (fAcquire) { /* Install the hook */
  593. if (this->vi.hwnd && (this->vi.fl & VIFL_CAPTURED)) {
  594. PMOUSEEMULATIONINFO pmei;
  595. /*
  596. * Prefix considers this memory leaked (mb:34652) because we
  597. * don't save the pointer here. The memory is freed when the
  598. * hook is removed so this is not really a problem.
  599. */
  600. hres = AllocCbPpv(cbX(MOUSEEMULATIONINFO), &pmei);
  601. if (SUCCEEDED(hres)) {
  602. if (SetWindowSubclass(this->vi.hwnd,
  603. CEm_Mouse_SubclassProc, 0,
  604. (ULONG_PTR)pmei)) {
  605. /* Nudge it */
  606. SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L);
  607. hres = S_OK;
  608. } else {
  609. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  610. SquirtSqflPtszV(sqfl,
  611. TEXT("Mouse::Acquire: ")
  612. TEXT("Window %p is not valid"),
  613. this->vi.hwnd);
  614. FreePv(pmei);
  615. hres = E_INVALIDARG;
  616. }
  617. }
  618. } else {
  619. RPF("Mouse::Acquire: Non-exclusive mode not supported");
  620. hres = E_FAIL;
  621. }
  622. } else { /* Remove the hook */
  623. PMOUSEEMULATIONINFO pmei;
  624. if (GetWindowSubclass(this->vi.hwnd, CEm_Mouse_SubclassProc,
  625. 0, (PULONG_PTR)&pmei)) {
  626. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  627. SquirtSqflPtszV(sqfl, TEXT("CEm_Mouse_Acquire: ")
  628. TEXT("Telling %p to exit"), this->vi.hwnd);
  629. pmei->fNeedExit = 1;
  630. SendNotifyMessage(this->vi.hwnd, WM_NULL, 0, 0L);
  631. } else { /* Window was already unhooked */
  632. }
  633. hres = S_OK;
  634. }
  635. ExitOleProc();
  636. return hres;
  637. }
  638. /*****************************************************************************
  639. *
  640. * @doc INTERNAL
  641. *
  642. * @func HRESULT | CEm_Mouse_Acquire |
  643. *
  644. * Acquire/unacquire a mouse.
  645. *
  646. * @parm PEM | pem |
  647. *
  648. * Device being acquired.
  649. *
  650. * Whether the device is being acquired or unacquired.
  651. *
  652. *****************************************************************************/
  653. STDMETHODIMP
  654. CEm_Mouse_Acquire(PEM this, BOOL fAcquire)
  655. {
  656. HRESULT hres;
  657. EnterProc(CEm_Mouse_Acquire, (_ "pu", this, fAcquire));
  658. AssertF(this->dwSignature == CEM_SIGNATURE);
  659. #ifdef USE_SLOW_LL_HOOKS
  660. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE ||
  661. DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2);
  662. if (this->vi.fl & DIMAKEEMFL(DIEMFL_MOUSE)) {
  663. /*
  664. * This used to use the subclass technique for exclusive mode
  665. * even if low-level hooks were available because low-level
  666. * hooks turn out to be even slower that subclassing. However,
  667. * subclassing is not transparent as it uses SetCapture which
  668. * causes Accellerator translation to be disabled for the app
  669. * which would be a more serious regression from Win9x than
  670. * performance being even worse than we thought.
  671. */
  672. AssertF(g_fUseLLHooks);
  673. hres = CEm_LL_Acquire(this, fAcquire, this->vi.fl, LLTS_MSE);
  674. if( SUCCEEDED(hres) ) {
  675. if( fAcquire && this->vi.fl & VIFL_CAPTURED ) {
  676. if( !this->fHidden ) {
  677. ShowCursor(0);
  678. this->fHidden = TRUE;
  679. }
  680. } else {
  681. if( this->fHidden ) {
  682. ShowCursor(1);
  683. this->fHidden = FALSE;
  684. }
  685. }
  686. }
  687. } else {
  688. hres = CEm_Mouse_Subclass_Acquire(this, fAcquire);
  689. }
  690. #else
  691. AssertF(DIGETEMFL(this->vi.fl) == DIEMFL_MOUSE2);
  692. hres = CEm_Mouse_Subclass_Acquire(this, fAcquire);
  693. #endif
  694. ExitOleProc();
  695. return hres;
  696. }
  697. /*****************************************************************************
  698. *
  699. * @doc INTERNAL
  700. *
  701. * @func HRESULT | CEm_Mouse_CreateInstance |
  702. *
  703. * Create a mouse thing. Also record what emulation
  704. * level we ended up with so the caller knows.
  705. *
  706. * @parm PVXDDEVICEFORMAT | pdevf |
  707. *
  708. * What the object should look like.
  709. *
  710. * @parm PVXDINSTANCE * | ppviOut |
  711. *
  712. * The answer goes here.
  713. *
  714. *****************************************************************************/
  715. HRESULT EXTERNAL
  716. CEm_Mouse_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
  717. {
  718. HRESULT hres;
  719. #ifdef USE_SLOW_LL_HOOKS
  720. /*
  721. * Note carefully the test. It handles the cases where
  722. *
  723. * 0. The app did not ask for emulation, so we give it the
  724. * best we can. (dwEmulation == 0)
  725. * 1. The app explicitly asked for emulation 1.
  726. * (dwEmulation == DIEMFL_MOUSE)
  727. * 2. The app explicitly asked for emulation 2.
  728. * (dwEmulation == DIEMFL_MOUSE2)
  729. * 3. The registry explicitly asked for both emulation modes.
  730. * (dwEmulation == DIEMFL_MOUSE | DIEMFL_MOUSE2)
  731. * Give it the best we can. (I.e., same as case 0.)
  732. *
  733. * All platforms support emulation 2. Not all platforms support
  734. * emulation 1. If we want emulation 1 but can't get it, then
  735. * we fall back on emulation 2.
  736. */
  737. /*
  738. * First, if we don't have support for emulation 1, then clearly
  739. * we have to use emulation 2.
  740. */
  741. if (!g_fUseLLHooks
  742. #ifdef DEBUG
  743. || (g_flEmulation & DIEMFL_MOUSE2)
  744. #endif
  745. ) {
  746. pdevf->dwEmulation = DIEMFL_MOUSE2;
  747. } else
  748. /*
  749. * Otherwise, we have to choose between 1 and 2. The only case
  750. * where we choose 2 is if 2 is explicitly requested.
  751. */
  752. if (pdevf->dwEmulation == DIEMFL_MOUSE2) {
  753. /* Do nothing */
  754. } else
  755. /*
  756. * All other cases get 1.
  757. */
  758. {
  759. pdevf->dwEmulation = DIEMFL_MOUSE;
  760. }
  761. /*
  762. * Assert that we never give emulation 1 when it doesn't exist.
  763. */
  764. AssertF(fLimpFF(pdevf->dwEmulation & DIEMFL_MOUSE, g_fUseLLHooks));
  765. #else
  766. /*
  767. * We are being compiled for "emulation 2 only", so that simplifies
  768. * matters immensely.
  769. */
  770. pdevf->dwEmulation = DIEMFL_MOUSE2;
  771. #endif
  772. hres = CEm_CreateInstance(pdevf, ppviOut, &s_edMouse);
  773. return hres;
  774. }
  775. /*****************************************************************************
  776. *
  777. * @doc INTERNAL
  778. *
  779. * @func HRESULT | CEm_Mouse_InitButtons |
  780. *
  781. * Initialize the mouse button state in preparation for
  782. * acquisition.
  783. *
  784. * @parm PVXDDWORDDATA | pvdd |
  785. *
  786. * The button states.
  787. *
  788. *****************************************************************************/
  789. HRESULT EXTERNAL
  790. CEm_Mouse_InitButtons(PVXDDWORDDATA pvdd)
  791. {
  792. /* Do this only when nothing is yet acquired */
  793. if (s_edMouse.cAcquire < 0) {
  794. *(LPDWORD)&s_msEd.rgbButtons = pvdd->dw;
  795. /* randomly initializing axes as well as mouse buttons
  796. X and Y are not buttons
  797. Randomize initial values of X and Y */
  798. while( !s_msEd.lX )
  799. {
  800. s_msEd.lX = GetTickCount();
  801. s_msEd.lY = (s_msEd.lX << 16) | (s_msEd.lX >> 16);
  802. s_msEd.lX = s_msEd.lY * (DWORD)((UINT_PTR)&pvdd);
  803. }
  804. }
  805. return S_OK;
  806. }
  807. #ifdef USE_SLOW_LL_HOOKS
  808. /*****************************************************************************
  809. *
  810. * @doc INTERNAL
  811. *
  812. * @func LRESULT | CEm_LL_MseHook |
  813. *
  814. * Low-level mouse hook filter.
  815. *
  816. * @parm int | nCode |
  817. *
  818. * Notification code.
  819. *
  820. * @parm WPARAM | wp |
  821. *
  822. * WM_* mouse message.
  823. *
  824. * @parm LPARAM | lp |
  825. *
  826. * Mouse message information.
  827. *
  828. * @returns
  829. *
  830. * Always chains to the next hook.
  831. *
  832. *****************************************************************************/
  833. LRESULT CALLBACK
  834. CEm_LL_MseHook(int nCode, WPARAM wp, LPARAM lp)
  835. {
  836. PLLTHREADSTATE plts;
  837. if (nCode == HC_ACTION) {
  838. DIMOUSESTATE_INT ms;
  839. POINT pt;
  840. PMSLLHOOKSTRUCT pllhs = (PV)lp;
  841. /*
  842. * We are called only on mouse messages, so we may as
  843. * well prepare ourselves up front.
  844. *
  845. * Note! that we *cannot* use GetAsyncKeyState on the
  846. * buttons, because the buttons haven't been pressed yet!
  847. * Instead, we must update the button state based on the
  848. * received message.
  849. */
  850. ms.lX = 0;
  851. ms.lY = 0;
  852. ms.lZ = 0;
  853. memcpy(ms.rgbButtons, s_msEd.rgbButtons, cbX(ms.rgbButtons));
  854. /*
  855. *
  856. * Annoying! We receive swapped buttons, so we need to
  857. * unswap them. I mark this as `annoying' because
  858. * GetAsyncKeyState returns unswapped buttons, so sometimes
  859. * I do and sometimes I don't. But it isn't `*wrong*'
  860. * because it is the right thing. Arguably, GetAsyncKeyState
  861. * is the one that is broken.
  862. */
  863. if (GetSystemMetrics(SM_SWAPBUTTON)) {
  864. /*
  865. * Assert that the left and right button messages
  866. * run in parallel.
  867. */
  868. CAssertF(WM_RBUTTONDOWN - WM_LBUTTONDOWN ==
  869. WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK &&
  870. WM_RBUTTONDBLCLK - WM_LBUTTONDBLCLK ==
  871. WM_RBUTTONUP - WM_LBUTTONUP);
  872. switch (wp) {
  873. case WM_LBUTTONDOWN:
  874. case WM_LBUTTONDBLCLK:
  875. case WM_LBUTTONUP:
  876. wp = (wp - WM_LBUTTONUP) + WM_RBUTTONUP;
  877. break;
  878. case WM_RBUTTONDOWN:
  879. case WM_RBUTTONDBLCLK:
  880. case WM_RBUTTONUP:
  881. wp = (wp - WM_RBUTTONUP) + WM_LBUTTONUP;
  882. break;
  883. }
  884. }
  885. switch (wp) { /* wp = message number */
  886. case WM_MOUSEWHEEL:
  887. SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d,%d)"),
  888. pllhs->pt.x,
  889. pllhs->pt.y,
  890. pllhs->mouseData);
  891. ms.lZ = (short int)HIWORD(pllhs->mouseData);
  892. goto lparam;
  893. case WM_LBUTTONDOWN:
  894. case WM_LBUTTONDBLCLK:
  895. ms.rgbButtons[0] = 0x80;
  896. goto move;
  897. case WM_LBUTTONUP:
  898. ms.rgbButtons[0] = 0x00;
  899. goto move;
  900. case WM_RBUTTONDOWN:
  901. case WM_RBUTTONDBLCLK:
  902. ms.rgbButtons[1] = 0x80;
  903. goto move;
  904. case WM_RBUTTONUP:
  905. ms.rgbButtons[1] = 0x00;
  906. goto move;
  907. case WM_MBUTTONDOWN:
  908. case WM_MBUTTONDBLCLK:
  909. ms.rgbButtons[2] = 0x80;
  910. goto move;
  911. case WM_MBUTTONUP:
  912. ms.rgbButtons[2] = 0x00;
  913. goto move;
  914. #if defined(WINNT) && (_WIN32_WINNT >= 0x0500)
  915. case WM_XBUTTONDOWN:
  916. case WM_XBUTTONDBLCLK:
  917. /*
  918. * Using switch can be easily extended to support more buttons.
  919. */
  920. switch ( HIWORD(pllhs->mouseData) ) {
  921. case XBUTTON1:
  922. ms.rgbButtons[3] = 0x80;
  923. break;
  924. case XBUTTON2:
  925. ms.rgbButtons[4] = 0x80;
  926. break;
  927. /*
  928. * When we support more than 5 buttons, take care of them.
  929. case XBUTTON3:
  930. ms.rgbButtons[5] = 0x80;
  931. break;
  932. case XBUTTON4:
  933. ms.rgbButtons[6] = 0x80;
  934. break;
  935. case XBUTTON5:
  936. ms.rgbButtons[7] = 0x80;
  937. break;
  938. */
  939. }
  940. goto move;
  941. case WM_XBUTTONUP:
  942. /*
  943. * Using switch can be easily extended to support more buttons.
  944. */
  945. switch ( HIWORD(pllhs->mouseData) ) {
  946. case XBUTTON1:
  947. ms.rgbButtons[3] = 0x00;
  948. break;
  949. case XBUTTON2:
  950. ms.rgbButtons[4] = 0x00;
  951. break;
  952. /*
  953. * When we support more than 5 buttons, take care of them.
  954. case XBUTTON3:
  955. ms.rgbButtons[5] = 0x00;
  956. break;
  957. case XBUTTON4:
  958. ms.rgbButtons[6] = 0x00;
  959. break;
  960. case XBUTTON5:
  961. ms.rgbButtons[7] = 0x00;
  962. break;
  963. */
  964. }
  965. goto move;
  966. #endif
  967. case WM_MOUSEMOVE:
  968. SquirtSqflPtszV(sqfl, TEXT("CEm_LL_MseHook: (%d,%d)"),
  969. pllhs->pt.x, pllhs->pt.y);
  970. move:;
  971. lparam:;
  972. GetCursorPos(&pt);
  973. ms.lX = CEm_Mouse_RemoveAccel(pllhs->pt.x - pt.x);
  974. ms.lY = CEm_Mouse_RemoveAccel(pllhs->pt.y - pt.y);
  975. CEm_Mouse_AddState(&ms, GetTickCount());
  976. }
  977. }
  978. /*
  979. * Eat the message by returning non-zero if at least one client
  980. * is exclusive.
  981. */
  982. plts = g_plts;
  983. if (plts) {
  984. LRESULT rc;
  985. rc = CallNextHookEx(plts->rglhs[LLTS_MSE].hhk, nCode, wp, lp);
  986. if (!plts->rglhs[LLTS_MSE].cExcl) {
  987. return rc;
  988. }
  989. } else {
  990. /*
  991. * This can happen if a message gets posted to the hook after
  992. * releasing the last acquire but before we completely unhook.
  993. */
  994. RPF( "DINPUT: Mouse hook not passed on to next hook" );
  995. }
  996. return 1;
  997. }
  998. #endif /* USE_SLOW_LL_HOOKS */