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.

478 lines
12 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. gesture.cpp
  5. Abstract:
  6. This module contains the code to invoke SuperTIP.
  7. Author:
  8. Michael Tsang (MikeTs) 11-Jul-2000
  9. Environment:
  10. User mode
  11. Revision History:
  12. --*/
  13. #include "pch.h"
  14. static const int BUFFERSIZE = 200; // The maximum number of points to collect while pen is up
  15. typedef struct RecogRec
  16. {
  17. POINT pt;
  18. DWORD dwTime;
  19. } RECOGREC, *PRECOGREC;
  20. RECOGREC coords[BUFFERSIZE] = {0}; //circular buffer
  21. int index = 0; //current buffer insertion point
  22. int startIndex = 0; //starting index. Only the points
  23. // between startIndex and index are
  24. // valid.
  25. inline void resetBuffer() {index = startIndex = 0;}
  26. /*++
  27. @doc INTERNAL
  28. @func int | RecognizeGesture | Do gesture recognition.
  29. @parm IN LONG | x | X position.
  30. @parm IN LONG | y | Y position.
  31. @parm IN WORD | wButtons | Buttons state.
  32. @parm IN DWORD | dwTime | Time stamp.
  33. @parm IN BOOL | fLowLevelMouse | TRUE if called by LowLevelMouseProc.
  34. @rvalue SUCCESS | Returns gesture recognized.
  35. @rvalue FAILURE | Returns NO_GESTURE.
  36. --*/
  37. int
  38. RecognizeGesture(
  39. IN LONG x,
  40. IN LONG y,
  41. IN WORD wButtons,
  42. IN DWORD dwTime,
  43. IN BOOL fLowLevelMouse
  44. )
  45. {
  46. TRACEPROC("RecognizeGesture", 5)
  47. int gesture = NO_GESTURE;
  48. static LONG xLast = 0;
  49. static LONG yLast = 0;
  50. static WORD wLastButtons = 0;
  51. static DWORD dwTimeLast = 0;
  52. int cxScreen = fLowLevelMouse? gcxScreen: gcxPrimary,
  53. cyScreen = fLowLevelMouse? gcyScreen: gcyPrimary;
  54. TRACEENTER(("(x=%d,y=%d,Buttons=%x,Time=%d,fLowLevelMouse=%x)\n",
  55. x, y, wButtons,dwTime, fLowLevelMouse));
  56. //
  57. // Scale the coordinate system from 64K back to screen coordinates.
  58. //
  59. x = (x*cxScreen) >> 16;
  60. y = (y*cyScreen) >> 16;
  61. //
  62. // After converting back to screen coordinates, we need to coalesce
  63. // the previous point if they are identical.
  64. //
  65. if ((x != xLast) || (y != yLast) || (wButtons != wLastButtons) ||
  66. (dwTime - dwTimeLast > (DWORD)gConfig.GestureSettings.iStopTime))
  67. {
  68. xLast = x;
  69. yLast = y;
  70. wLastButtons = wButtons;
  71. dwTimeLast = dwTime;
  72. if (wButtons == 0)
  73. {
  74. POINT pt;
  75. pt.x = x;
  76. pt.y = y;
  77. gesture = Recognize(pt, dwTime);
  78. if (gesture == NO_GESTURE)
  79. {
  80. AddItem(pt, dwTime);
  81. }
  82. else
  83. {
  84. resetBuffer();
  85. NotifyClient(GestureEvent, gesture, 0);
  86. DoGestureAction(gConfig.GestureSettings.GestureMap[gesture],
  87. x + glVirtualDesktopLeft,
  88. y + glVirtualDesktopTop);
  89. }
  90. }
  91. else
  92. {
  93. resetBuffer();
  94. }
  95. }
  96. TRACEEXIT(("=%d\n", gesture));
  97. return gesture;
  98. } //RecognizeGesture
  99. /*++
  100. @doc INTERNAL
  101. @func int | Recognize | Recognize the gesture.
  102. @parm IN POINT& | pt | Current point.
  103. @parm IN DWORD | dwTime | Time.
  104. @rvalue None.
  105. --*/
  106. int
  107. Recognize(
  108. IN POINT& pt,
  109. IN DWORD dwTime
  110. )
  111. {
  112. TRACEPROC("Recognize", 5)
  113. int gesture = NO_GESTURE;
  114. TRACEENTER(("(x=%d,y=%d,Time=%d)\n", pt.x, pt.y, dwTime));
  115. if (penStopped(pt, dwTime, index, startIndex))
  116. {
  117. bool beenOut = false;
  118. int numPoints = 0;
  119. int numOutPoints = 0;
  120. int xmin = 1000; //bounding box for points outside the test circle.
  121. int xmax = -1000;
  122. int ymin = 1000;
  123. int ymax = -1000;
  124. int curIndex = (index > 0)? index - 1: BUFFERSIZE - 1;
  125. DWORD delt;
  126. while ((curIndex != startIndex) &&
  127. ((delt = dwTime - coords[curIndex].dwTime) <
  128. (DWORD)gConfig.GestureSettings.iMaxTimeToInspect))
  129. {
  130. //go backwards through the buffer
  131. numPoints++;
  132. if (dist(pt, curIndex) >= gConfig.GestureSettings.iRadius)
  133. {
  134. //
  135. // Outside the test circle. Accumulate the bounding box
  136. // of points.
  137. //
  138. numOutPoints++;
  139. beenOut = true;
  140. // Positive if coord is east of point
  141. int relx = coords[curIndex].pt.x - pt.x;
  142. // Positive if coord is south of point
  143. int rely = coords[curIndex].pt.y - pt.y;
  144. // Possibly expand the bounding box
  145. if (relx < xmin) xmin = relx;
  146. if (relx > xmax) xmax = relx;
  147. if (rely < ymin) ymin = rely;
  148. if (rely > ymax) ymax = rely;
  149. }
  150. else
  151. {
  152. //
  153. // Inside the test circle
  154. //
  155. gesture = NO_GESTURE;
  156. if (beenOut)
  157. {
  158. //
  159. // Check points and return something
  160. //
  161. if ((numOutPoints >= gConfig.GestureSettings.iMinOutPts) &&
  162. checkEarlierPoints(pt,
  163. coords[curIndex].dwTime,
  164. curIndex,
  165. startIndex))
  166. {
  167. int bW = xmax - xmin;
  168. int bH = ymax - ymin;
  169. if (bW >= bH)
  170. {
  171. //
  172. // horizontal spike
  173. //
  174. if (bW > bH*gConfig.GestureSettings.iAspectRatio)
  175. {
  176. //
  177. // bbox must be wide enough
  178. //
  179. gesture = (xmin > 0)? RIGHT_SPIKE: LEFT_SPIKE;
  180. }
  181. }
  182. else
  183. {
  184. //
  185. // vertical spike
  186. //
  187. if (bH > bW*gConfig.GestureSettings.iAspectRatio)
  188. {
  189. //
  190. // bbox must be tall enough
  191. //
  192. gesture = (ymin < 0)? UP_SPIKE: DOWN_SPIKE;
  193. }
  194. }
  195. }
  196. break;
  197. }
  198. }
  199. curIndex = (curIndex > 0)? curIndex - 1: BUFFERSIZE - 1;
  200. }
  201. }
  202. TRACEEXIT(("=%d\n", gesture));
  203. return gesture;
  204. } //Recognize
  205. /*++
  206. @doc INTERNAL
  207. @func void | AddItem | Add an item to the recognize buffer.
  208. @parm IN LONG | x | X position.
  209. @parm IN LONG | y | Y position.
  210. @parm IN DWORD | dwTime | Time.
  211. @rvalue None.
  212. --*/
  213. void
  214. AddItem(
  215. IN POINT& pt,
  216. IN DWORD dwTime
  217. )
  218. {
  219. TRACEPROC("AddItem", 5)
  220. TRACEENTER(("(x=%d,y=%d,Time=%d)\n", pt.x, pt.y, dwTime));
  221. coords[index].pt = pt;
  222. coords[index].dwTime = dwTime;
  223. index++;
  224. if (index >= BUFFERSIZE)
  225. {
  226. //
  227. // End of circular buffer, wrap around.
  228. //
  229. index = 0;
  230. }
  231. if (index == startIndex)
  232. {
  233. //
  234. // The entire buffer is full of valid points.
  235. //
  236. startIndex++;
  237. if (startIndex >= BUFFERSIZE)
  238. {
  239. startIndex = 0;
  240. }
  241. }
  242. TRACEEXIT(("!\n"));
  243. return;
  244. } //AddItem
  245. /*++
  246. @doc INTERNAL
  247. @func bool | penStopped | The pen is considered stopped if all the
  248. points within the previous STOPTIME ms are within the STOPDIST
  249. pixel circle centered on given point. If the buffer is less
  250. than STOPTIME ms long, returns false.
  251. @parm IN POINT & | pt | The given point.
  252. @parm IN DWORD | dwTime | Time.
  253. @parm IN int | ci | current index.
  254. @parm IN int | li | limiting index.
  255. @rvalue SUCCESS | Returns TRUE (pen has stopped).
  256. @rvalue FAILURE | Returns FALSE.
  257. --*/
  258. bool
  259. penStopped(
  260. IN POINT& pt,
  261. IN DWORD dwTime,
  262. IN int ci,
  263. IN int li
  264. )
  265. {
  266. TRACEPROC("penStopped", 5)
  267. bool rc = false;
  268. TRACEENTER(("(x=%d,y=%d,time=%d,ci=%d,li=%d\n", pt.x, pt.y, dwTime, ci, li));
  269. ci = (ci > 0)? ci - 1: BUFFERSIZE - 1;
  270. while (ci != li)
  271. {
  272. if (dist(pt, ci) > gConfig.GestureSettings.iStopDist)
  273. {
  274. break;
  275. }
  276. if ((dwTime - coords[ci].dwTime) >=
  277. (DWORD)gConfig.GestureSettings.iStopTime)
  278. {
  279. rc = true;
  280. break;
  281. }
  282. ci = (ci > 0)? ci - 1: BUFFERSIZE - 1;
  283. }
  284. TRACEEXIT(("=%x\n", rc));
  285. return rc;
  286. } //penStopped
  287. /*++
  288. @doc INTERNAL
  289. @func bool | checkEarlierPoints | Called when we have a candidate stroke,
  290. and the pen has just left the target circle and the point at
  291. coords[index] for the first time. Inspects up to pointsToExamine
  292. earlier points. They must all be there, must all be within the
  293. target circle (bigRadius), and they must all have occurred within
  294. checkTime of the time the pen left the target circle for the first
  295. time. This helps to filter out "overshoot" events where the pen
  296. passes through the target circle quickly, then returns to the final
  297. touchdown point.
  298. @parm IN POINT & | pt | The given point.
  299. @parm IN DWORD | dwTime | Time.
  300. @parm IN int | ci | current index.
  301. @parm IN int | li | limiting index.
  302. @rvalue SUCCESS | Returns TRUE (pen has stopped).
  303. @rvalue FAILURE | Returns FALSE.
  304. --*/
  305. bool
  306. checkEarlierPoints(
  307. IN POINT& pt,
  308. IN DWORD dwTime,
  309. IN int ci,
  310. IN int li
  311. )
  312. {
  313. TRACEPROC("checkEarlierPoints", 5)
  314. bool rc = true;
  315. int numPoints = 0;
  316. TRACEENTER(("(x=%d,y=%d,time=%d,ci=%d,li=%d\n",
  317. pt.x, pt.y, dwTime, ci, li));
  318. ci = (ci > 0)? ci - 1: BUFFERSIZE - 1;
  319. while ((ci != li) && (numPoints < gConfig.GestureSettings.iPointsToExamine))
  320. {
  321. if (dist(pt, ci) >= gConfig.GestureSettings.iRadius)
  322. {
  323. rc = false;
  324. break;
  325. }
  326. if ((dwTime - coords[ci].dwTime) >=
  327. (DWORD)gConfig.GestureSettings.iCheckTime)
  328. {
  329. break;
  330. }
  331. numPoints++;
  332. ci = (ci > 0)? ci - 1: BUFFERSIZE - 1;
  333. }
  334. if ((ci == li) && (numPoints < gConfig.GestureSettings.iPointsToExamine))
  335. {
  336. rc = false;
  337. }
  338. TRACEEXIT(("=%x\n", rc));
  339. return rc;
  340. } //checkEarlierPoints
  341. /*++
  342. @doc INTERNAL
  343. @func int | dist | Calculates the squared distance between the given pt
  344. and the point at coords[index].
  345. @parm IN POINT & | pt | The given point.
  346. @parm IN int index | index into the recog buffer.
  347. @rvalue Returns the squared distance calculated.
  348. --*/
  349. int
  350. dist(
  351. IN POINT& pt,
  352. IN int index
  353. )
  354. {
  355. TRACEPROC("dist", 5)
  356. int dx, dy, d;
  357. TRACEENTER(("(x=%d,y=%d,index=%d)\n", pt.x, pt.y, index));
  358. dx = pt.x - coords[index].pt.x;
  359. dy = pt.y - coords[index].pt.y;
  360. d = dx*dx + dy*dy;
  361. TRACEEXIT(("=%d\n", d));
  362. return d;
  363. } //dist
  364. /*++
  365. @doc INTERNAL
  366. @func VOID | DoGestureAction | Do the gesture action.
  367. @parm IN GESTURE_ACTION | Action | Gesture action to be performed.
  368. @parm IN LONG | x | x coordinate of gesture end-point.
  369. @parm IN LONG | y | y coordinate of gesture end-point.
  370. @rvalue None.
  371. --*/
  372. VOID
  373. DoGestureAction(
  374. IN GESTURE_ACTION Action,
  375. IN LONG x,
  376. IN LONG y
  377. )
  378. {
  379. TRACEPROC("DoGestureAction", 2)
  380. TRACEENTER(("(Action=%d,x=%d,y=%d)\n", Action, x, y));
  381. switch (Action)
  382. {
  383. case PopupSuperTIP:
  384. case PopupMIP:
  385. if (ghwndSuperTIP != NULL)
  386. {
  387. PostMessage(ghwndSuperTIP,
  388. WM_GESTURE,
  389. (WPARAM)Action,
  390. MAKELONG(WORD(x), WORD(y)));
  391. }
  392. break;
  393. case SendHotkey:
  394. SetEvent(ghHotkeyEvent);
  395. break;
  396. }
  397. TRACEEXIT(("!\n"));
  398. return;
  399. } //DoGestureAction