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.

GestureHandler.cxx 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /* Copyright 2019 Aaron Sowry for Cendio AB
  2. * Copyright 2020 Pierre Ossman for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <assert.h>
  23. #include <math.h>
  24. #include <rfb/util.h>
  25. #include <rfb/LogWriter.h>
  26. #include "GestureHandler.h"
  27. static rfb::LogWriter vlog("GestureHandler");
  28. static const unsigned char GH_NOGESTURE = 0;
  29. static const unsigned char GH_ONETAP = 1;
  30. static const unsigned char GH_TWOTAP = 2;
  31. static const unsigned char GH_THREETAP = 4;
  32. static const unsigned char GH_DRAG = 8;
  33. static const unsigned char GH_LONGPRESS = 16;
  34. static const unsigned char GH_TWODRAG = 32;
  35. static const unsigned char GH_PINCH = 64;
  36. static const unsigned char GH_INITSTATE = 127;
  37. const unsigned GH_MOVE_THRESHOLD = 50;
  38. const unsigned GH_ANGLE_THRESHOLD = 90; // Degrees
  39. // Timeout when waiting for gestures (ms)
  40. const unsigned GH_MULTITOUCH_TIMEOUT = 250;
  41. // Maximum time between press and release for a tap (ms)
  42. const unsigned GH_TAP_TIMEOUT = 1000;
  43. // Timeout when waiting for longpress (ms)
  44. const unsigned GH_LONGPRESS_TIMEOUT = 1000;
  45. GestureHandler::GestureHandler() :
  46. state(GH_INITSTATE), waitingRelease(false),
  47. longpressTimer(this), twoTouchTimer(this)
  48. {
  49. }
  50. GestureHandler::~GestureHandler()
  51. {
  52. }
  53. void GestureHandler::handleTouchBegin(int id, double x, double y)
  54. {
  55. GHTouch ght;
  56. // Ignore any new touches if there is already an active gesture,
  57. // or we're in a cleanup state
  58. if (hasDetectedGesture() || (state == GH_NOGESTURE)) {
  59. ignored.insert(id);
  60. return;
  61. }
  62. // Did it take too long between touches that we should no longer
  63. // consider this a single gesture?
  64. if ((tracked.size() > 0) &&
  65. (rfb::msSince(&tracked.begin()->second.started) > GH_MULTITOUCH_TIMEOUT)) {
  66. state = GH_NOGESTURE;
  67. ignored.insert(id);
  68. return;
  69. }
  70. // If we're waiting for fingers to release then we should no longer
  71. // recognize new touches
  72. if (waitingRelease) {
  73. state = GH_NOGESTURE;
  74. ignored.insert(id);
  75. return;
  76. }
  77. gettimeofday(&ght.started, NULL);
  78. ght.active = true;
  79. ght.lastX = ght.firstX = x;
  80. ght.lastY = ght.firstY = y;
  81. ght.angle = 0;
  82. tracked[id] = ght;
  83. switch (tracked.size()) {
  84. case 1:
  85. longpressTimer.start(GH_LONGPRESS_TIMEOUT);
  86. break;
  87. case 2:
  88. state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);
  89. longpressTimer.stop();
  90. break;
  91. case 3:
  92. state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);
  93. break;
  94. default:
  95. state = GH_NOGESTURE;
  96. }
  97. }
  98. void GestureHandler::handleTouchUpdate(int id, double x, double y)
  99. {
  100. GHTouch *touch, *prevTouch;
  101. double deltaX, deltaY, prevDeltaMove;
  102. unsigned deltaAngle;
  103. // If this is an update for a touch we're not tracking, ignore it
  104. if (tracked.count(id) == 0)
  105. return;
  106. touch = &tracked[id];
  107. // Update the touches last position with the event coordinates
  108. touch->lastX = x;
  109. touch->lastY = y;
  110. deltaX = x - touch->firstX;
  111. deltaY = y - touch->firstY;
  112. // Update angle when the touch has moved
  113. if ((touch->firstX != touch->lastX) ||
  114. (touch->firstY != touch->lastY))
  115. touch->angle = atan2(deltaY, deltaX) * 180 / M_PI;
  116. if (!hasDetectedGesture()) {
  117. // Ignore moves smaller than the minimum threshold
  118. if (hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD)
  119. return;
  120. // Can't be a tap or long press as we've seen movement
  121. state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);
  122. longpressTimer.stop();
  123. if (tracked.size() != 1)
  124. state &= ~(GH_DRAG);
  125. if (tracked.size() != 2)
  126. state &= ~(GH_TWODRAG | GH_PINCH);
  127. // We need to figure out which of our different two touch gestures
  128. // this might be
  129. if (tracked.size() == 2) {
  130. // The other touch can be first or last in tracked
  131. // depending on which event came first
  132. prevTouch = &tracked.rbegin()->second;
  133. if (prevTouch == touch)
  134. prevTouch = &tracked.begin()->second;
  135. // How far the previous touch point has moved since start
  136. prevDeltaMove = hypot(prevTouch->firstX - prevTouch->lastX,
  137. prevTouch->firstY - prevTouch->lastY);
  138. // We know that the current touch moved far enough,
  139. // but unless both touches moved further than their
  140. // threshold we don't want to disqualify any gestures
  141. if (prevDeltaMove > GH_MOVE_THRESHOLD) {
  142. // The angle difference between the direction of the touch points
  143. deltaAngle = fabs(touch->angle - prevTouch->angle);
  144. deltaAngle = fabs(((deltaAngle + 180) % 360) - 180);
  145. // PINCH or TWODRAG can be eliminated depending on the angle
  146. if (deltaAngle > GH_ANGLE_THRESHOLD)
  147. state &= ~GH_TWODRAG;
  148. else
  149. state &= ~GH_PINCH;
  150. if (twoTouchTimer.isStarted())
  151. twoTouchTimer.stop();
  152. } else if(!twoTouchTimer.isStarted()) {
  153. // We can't determine the gesture right now, let's
  154. // wait and see if more events are on their way
  155. twoTouchTimer.start(50);
  156. }
  157. }
  158. if (!hasDetectedGesture())
  159. return;
  160. pushEvent(GestureBegin);
  161. }
  162. pushEvent(GestureUpdate);
  163. }
  164. void GestureHandler::handleTouchEnd(int id)
  165. {
  166. std::map<int, GHTouch>::const_iterator iter;
  167. // Check if this is an ignored touch
  168. if(ignored.count(id)) {
  169. ignored.erase(id);
  170. if (ignored.empty() && tracked.empty()) {
  171. state = GH_INITSTATE;
  172. waitingRelease = false;
  173. }
  174. return;
  175. }
  176. // We got a TouchEnd before the timer triggered,
  177. // this cannot result in a gesture anymore.
  178. if (!hasDetectedGesture() && twoTouchTimer.isStarted()) {
  179. twoTouchTimer.stop();
  180. state = GH_NOGESTURE;
  181. }
  182. // Some gesture don't trigger until a touch is released
  183. if (!hasDetectedGesture()) {
  184. // Can't be a gesture that relies on movement
  185. state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);
  186. // Or something that relies on more time
  187. state &= ~GH_LONGPRESS;
  188. longpressTimer.stop();
  189. if (!waitingRelease) {
  190. gettimeofday(&releaseStart, NULL);
  191. waitingRelease = true;
  192. // Can't be a tap that requires more touches than we current have
  193. switch (tracked.size()) {
  194. case 1:
  195. state &= ~(GH_TWOTAP | GH_THREETAP);
  196. break;
  197. case 2:
  198. state &= ~(GH_ONETAP | GH_THREETAP);
  199. break;
  200. }
  201. }
  202. }
  203. // Waiting for all touches to release? (i.e. some tap)
  204. if (waitingRelease) {
  205. // Were all touches release roughly the same time?
  206. if (rfb::msSince(&releaseStart) > GH_MULTITOUCH_TIMEOUT)
  207. state = GH_NOGESTURE;
  208. // Did too long time pass between press and release?
  209. for (iter = tracked.begin(); iter != tracked.end(); ++iter) {
  210. if (rfb::msSince(&iter->second.started) > GH_TAP_TIMEOUT) {
  211. state = GH_NOGESTURE;
  212. break;
  213. }
  214. }
  215. tracked[id].active = false;
  216. // Are we still waiting for more releases?
  217. if (hasDetectedGesture()) {
  218. pushEvent(GestureBegin);
  219. } else {
  220. // Have we reached a dead end?
  221. if (state != GH_NOGESTURE)
  222. return;
  223. }
  224. }
  225. if (hasDetectedGesture())
  226. pushEvent(GestureEnd);
  227. // Ignore any remaining touches until they are ended
  228. for (iter = tracked.begin(); iter != tracked.end(); ++iter) {
  229. if (iter->second.active)
  230. ignored.insert(iter->first);
  231. }
  232. tracked.clear();
  233. state = GH_NOGESTURE;
  234. ignored.erase(id);
  235. if (ignored.empty()) {
  236. state = GH_INITSTATE;
  237. waitingRelease = false;
  238. }
  239. }
  240. bool GestureHandler::hasDetectedGesture()
  241. {
  242. if (state == GH_NOGESTURE)
  243. return false;
  244. // Check to see if the bitmask value is a power of 2
  245. // (i.e. only one bit set). If it is, we have a state.
  246. if (state & (state - 1))
  247. return false;
  248. // For taps we also need to have all touches released
  249. // before we've fully detected the gesture
  250. if (state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {
  251. std::map<int, GHTouch>::const_iterator iter;
  252. // Any touch still active/pressed?
  253. for (iter = tracked.begin(); iter != tracked.end(); ++iter) {
  254. if (iter->second.active)
  255. return false;
  256. }
  257. }
  258. return true;
  259. }
  260. bool GestureHandler::handleTimeout(rfb::Timer* t)
  261. {
  262. if (t == &longpressTimer)
  263. longpressTimeout();
  264. else if (t == &twoTouchTimer)
  265. twoTouchTimeout();
  266. return false;
  267. }
  268. void GestureHandler::longpressTimeout()
  269. {
  270. assert(!hasDetectedGesture());
  271. state = GH_LONGPRESS;
  272. pushEvent(GestureBegin);
  273. }
  274. void GestureHandler::twoTouchTimeout()
  275. {
  276. double avgMoveH, avgMoveV, fdx, fdy, ldx, ldy, deltaTouchDistance;
  277. assert(!tracked.empty());
  278. // How far each touch point has moved since start
  279. getAverageMovement(&avgMoveH, &avgMoveV);
  280. avgMoveH = fabs(avgMoveH);
  281. avgMoveV = fabs(avgMoveV);
  282. // The difference in the distance between where
  283. // the touch points started and where they are now
  284. getAverageDistance(&fdx, &fdy, &ldx, &ldy);
  285. deltaTouchDistance = fabs(hypot(fdx, fdy) - hypot(ldx, ldy));
  286. if ((avgMoveV < deltaTouchDistance) &&
  287. (avgMoveH < deltaTouchDistance))
  288. state = GH_PINCH;
  289. else
  290. state = GH_TWODRAG;
  291. pushEvent(GestureBegin);
  292. pushEvent(GestureUpdate);
  293. }
  294. void GestureHandler::pushEvent(GestureEventType t)
  295. {
  296. GestureEvent gev;
  297. double avgX, avgY;
  298. gev.type = t;
  299. gev.gesture = stateToGesture(state);
  300. // For most gesture events the current (average) position is the
  301. // most useful
  302. getPosition(NULL, NULL, &avgX, &avgY);
  303. // However we have a slight distance to detect gestures, so for the
  304. // first gesture event we want to use the first positions we saw
  305. if (t == GestureBegin)
  306. getPosition(&avgX, &avgY, NULL, NULL);
  307. // For these gestures, we always want the event coordinates
  308. // to be where the gesture began, not the current touch location.
  309. switch (state) {
  310. case GH_TWODRAG:
  311. case GH_PINCH:
  312. getPosition(&avgX, &avgY, NULL, NULL);
  313. break;
  314. }
  315. gev.eventX = avgX;
  316. gev.eventY = avgY;
  317. // Some gestures also have a magnitude
  318. if (state == GH_PINCH) {
  319. if (t == GestureBegin)
  320. getAverageDistance(&gev.magnitudeX, &gev.magnitudeY,
  321. NULL, NULL);
  322. else
  323. getAverageDistance(NULL, NULL,
  324. &gev.magnitudeX, &gev.magnitudeY);
  325. } else if (state == GH_TWODRAG) {
  326. if (t == GestureBegin)
  327. gev.magnitudeX = gev.magnitudeY = 0;
  328. else
  329. getAverageMovement(&gev.magnitudeX, &gev.magnitudeY);
  330. }
  331. handleGestureEvent(gev);
  332. }
  333. GestureEventGesture GestureHandler::stateToGesture(unsigned char state)
  334. {
  335. switch (state) {
  336. case GH_ONETAP:
  337. return GestureOneTap;
  338. case GH_TWOTAP:
  339. return GestureTwoTap;
  340. case GH_THREETAP:
  341. return GestureThreeTap;
  342. case GH_DRAG:
  343. return GestureDrag;
  344. case GH_LONGPRESS:
  345. return GestureLongPress;
  346. case GH_TWODRAG:
  347. return GestureTwoDrag;
  348. case GH_PINCH:
  349. return GesturePinch;
  350. }
  351. assert(false);
  352. return (GestureEventGesture)0;
  353. }
  354. void GestureHandler::getPosition(double *firstX, double *firstY,
  355. double *lastX, double *lastY)
  356. {
  357. size_t size;
  358. double fx = 0, fy = 0, lx = 0, ly = 0;
  359. assert(!tracked.empty());
  360. size = tracked.size();
  361. std::map<int, GHTouch>::const_iterator iter;
  362. for (iter = tracked.begin(); iter != tracked.end(); ++iter) {
  363. fx += iter->second.firstX;
  364. fy += iter->second.firstY;
  365. lx += iter->second.lastX;
  366. ly += iter->second.lastY;
  367. }
  368. if (firstX)
  369. *firstX = fx / size;
  370. if (firstY)
  371. *firstY = fy / size;
  372. if (lastX)
  373. *lastX = lx / size;
  374. if (lastY)
  375. *lastY = ly / size;
  376. }
  377. void GestureHandler::getAverageMovement(double *h, double *v)
  378. {
  379. double totalH, totalV;
  380. size_t size;
  381. assert(!tracked.empty());
  382. totalH = totalV = 0;
  383. size = tracked.size();
  384. std::map<int, GHTouch>::const_iterator iter;
  385. for (iter = tracked.begin(); iter != tracked.end(); ++iter) {
  386. totalH += iter->second.lastX - iter->second.firstX;
  387. totalV += iter->second.lastY - iter->second.firstY;
  388. }
  389. if (h)
  390. *h = totalH / size;
  391. if (v)
  392. *v = totalV / size;
  393. }
  394. void GestureHandler::getAverageDistance(double *firstX, double *firstY,
  395. double *lastX, double *lastY)
  396. {
  397. double dx, dy;
  398. assert(!tracked.empty());
  399. // Distance between the first and last tracked touches
  400. dx = fabs(tracked.rbegin()->second.firstX - tracked.begin()->second.firstX);
  401. dy = fabs(tracked.rbegin()->second.firstY - tracked.begin()->second.firstY);
  402. if (firstX)
  403. *firstX = dx;
  404. if (firstY)
  405. *firstY = dy;
  406. dx = fabs(tracked.rbegin()->second.lastX - tracked.begin()->second.lastX);
  407. dy = fabs(tracked.rbegin()->second.lastY - tracked.begin()->second.lastY);
  408. if (lastX)
  409. *lastX = dx;
  410. if (lastY)
  411. *lastY = dy;
  412. }