/* Copyright 2019 Aaron Sowry for Cendio AB * Copyright 2019-2020 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #define XK_MISCELLANY #include #include #include "GestureHandler.h" #include "BaseTouchHandler.h" // Sensitivity threshold for gestures static const int ZOOMSENS = 30; static const int SCRLSENS = 50; static const unsigned DOUBLE_TAP_TIMEOUT = 1000; static const unsigned DOUBLE_TAP_THRESHOLD = 50; BaseTouchHandler::BaseTouchHandler() { gettimeofday(&lastTapTime, nullptr); } BaseTouchHandler::~BaseTouchHandler() { } void BaseTouchHandler::handleGestureEvent(const GestureEvent& ev) { double magnitude; switch (ev.type) { case GestureBegin: switch (ev.gesture) { case GestureOneTap: handleTapEvent(ev, 1); break; case GestureTwoTap: handleTapEvent(ev, 3); break; case GestureThreeTap: handleTapEvent(ev, 2); break; case GestureDrag: fakeMotionEvent(ev); fakeButtonEvent(true, 1, ev); break; case GestureLongPress: fakeMotionEvent(ev); fakeButtonEvent(true, 3, ev); break; case GestureTwoDrag: lastMagnitudeX = ev.magnitudeX; lastMagnitudeY = ev.magnitudeY; fakeMotionEvent(ev); break; case GesturePinch: lastMagnitudeX = hypot(ev.magnitudeX, ev.magnitudeY); fakeMotionEvent(ev); break; } break; case GestureUpdate: switch (ev.gesture) { case GestureOneTap: case GestureTwoTap: case GestureThreeTap: break; case GestureDrag: case GestureLongPress: fakeMotionEvent(ev); break; case GestureTwoDrag: // Always scroll in the same position. // We don't know if the mouse was moved so we need to move it // every update. fakeMotionEvent(ev); while ((ev.magnitudeY - lastMagnitudeY) > SCRLSENS) { fakeButtonEvent(true, 4, ev); fakeButtonEvent(false, 4, ev); lastMagnitudeY += SCRLSENS; } while ((ev.magnitudeY - lastMagnitudeY) < -SCRLSENS) { fakeButtonEvent(true, 5, ev); fakeButtonEvent(false, 5, ev); lastMagnitudeY -= SCRLSENS; } while ((ev.magnitudeX - lastMagnitudeX) > SCRLSENS) { fakeButtonEvent(true, 6, ev); fakeButtonEvent(false, 6, ev); lastMagnitudeX += SCRLSENS; } while ((ev.magnitudeX - lastMagnitudeX) < -SCRLSENS) { fakeButtonEvent(true, 7, ev); fakeButtonEvent(false, 7, ev); lastMagnitudeX -= SCRLSENS; } break; case GesturePinch: // Always scroll in the same position. // We don't know if the mouse was moved so we need to move it // every update. fakeMotionEvent(ev); magnitude = hypot(ev.magnitudeX, ev.magnitudeY); if (abs(magnitude - lastMagnitudeX) > ZOOMSENS) { fakeKeyEvent(true, XK_Control_L, ev); while ((magnitude - lastMagnitudeX) > ZOOMSENS) { fakeButtonEvent(true, 4, ev); fakeButtonEvent(false, 4, ev); lastMagnitudeX += ZOOMSENS; } while ((magnitude - lastMagnitudeX) < -ZOOMSENS) { fakeButtonEvent(true, 5, ev); fakeButtonEvent(false, 5, ev); lastMagnitudeX -= ZOOMSENS; } fakeKeyEvent(false, XK_Control_L, ev); } } break; case GestureEnd: switch (ev.gesture) { case GestureOneTap: case GestureTwoTap: case GestureThreeTap: case GesturePinch: case GestureTwoDrag: break; case GestureDrag: fakeMotionEvent(ev); fakeButtonEvent(false, 1, ev); break; case GestureLongPress: fakeMotionEvent(ev); fakeButtonEvent(false, 3, ev); break; } break; } } void BaseTouchHandler::handleTapEvent(const GestureEvent& ev, int buttonEvent) { GestureEvent newEv = ev; // If the user quickly taps multiple times we assume they meant to // hit the same spot, so slightly adjust coordinates if ((rfb::msSince(&lastTapTime) < DOUBLE_TAP_TIMEOUT) && (firstDoubleTapEvent.type == ev.type)) { double dx = firstDoubleTapEvent.eventX - ev.eventX; double dy = firstDoubleTapEvent.eventY - ev.eventY; double distance = hypot(dx, dy); if (distance < DOUBLE_TAP_THRESHOLD) { newEv.eventX = firstDoubleTapEvent.eventX; newEv.eventY = firstDoubleTapEvent.eventY; } else { firstDoubleTapEvent = ev; } } else { firstDoubleTapEvent = ev; } gettimeofday(&lastTapTime, nullptr); fakeMotionEvent(newEv); fakeButtonEvent(true, buttonEvent, newEv); fakeButtonEvent(false, buttonEvent, newEv); }