mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-14 05:12:27 +00:00
Rewrite touchscreen support for direct input
This commit is contained in:
parent
782c5f8a34
commit
8cb98b5365
2 changed files with 75 additions and 165 deletions
|
@ -22,14 +22,17 @@
|
|||
|
||||
#define MOUSE_POLLING_INTERVAL 5
|
||||
|
||||
// How long the mouse button will be pressed for a tap to click gesture
|
||||
#define TAP_BUTTON_RELEASE_DELAY 100
|
||||
// How long the fingers must be stationary to start a right click
|
||||
#define LONG_PRESS_ACTIVATION_DELAY 650
|
||||
|
||||
// How long the fingers must be stationary to start a drag
|
||||
#define DRAG_ACTIVATION_DELAY 650
|
||||
// How far the finger can move before it cancels a right click
|
||||
#define LONG_PRESS_ACTIVATION_DELTA 0.01f
|
||||
|
||||
// How far the finger can move before it cancels a drag or tap
|
||||
#define DEAD_ZONE_DELTA 0.1f
|
||||
// How long the double tap deadzone stays in effect between touch up and touch down
|
||||
#define DOUBLE_TAP_DEAD_ZONE_DELAY 250
|
||||
|
||||
// How far the finger can move before it can override the double tap deadzone
|
||||
#define DOUBLE_TAP_DEAD_ZONE_DELTA 0.025f
|
||||
|
||||
// How long the Start button must be pressed to toggle mouse emulation
|
||||
#define MOUSE_EMULATION_LONG_PRESS_TIME 750
|
||||
|
@ -60,11 +63,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
m_GamepadMouse(prefs.gamepadMouse),
|
||||
m_MouseMoveTimer(0),
|
||||
m_FakeCaptureActive(false),
|
||||
m_LeftButtonReleaseTimer(0),
|
||||
m_RightButtonReleaseTimer(0),
|
||||
m_DragTimer(0),
|
||||
m_DragButton(0),
|
||||
m_NumFingersDown(0),
|
||||
m_LongPressTimer(0),
|
||||
m_StreamWidth(streamWidth),
|
||||
m_StreamHeight(streamHeight),
|
||||
m_AbsoluteMouseMode(false)
|
||||
|
@ -142,8 +141,8 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
|
|||
m_GamepadMask = getAttachedGamepadMask();
|
||||
|
||||
SDL_zero(m_GamepadState);
|
||||
SDL_zero(m_TouchDownEvent);
|
||||
SDL_zero(m_CumulativeDelta);
|
||||
SDL_zero(m_LastTouchDownEvent);
|
||||
SDL_zero(m_LastTouchUpEvent);
|
||||
|
||||
SDL_AtomicSet(&m_MouseDeltaX, 0);
|
||||
SDL_AtomicSet(&m_MouseDeltaY, 0);
|
||||
|
@ -179,9 +178,7 @@ SdlInputHandler::~SdlInputHandler()
|
|||
}
|
||||
|
||||
SDL_RemoveTimer(m_MouseMoveTimer);
|
||||
SDL_RemoveTimer(m_LeftButtonReleaseTimer);
|
||||
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
||||
SDL_RemoveTimer(m_DragTimer);
|
||||
SDL_RemoveTimer(m_LongPressTimer);
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
||||
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
|
||||
|
@ -755,32 +752,11 @@ void SdlInputHandler::sendGamepadState(GamepadState* state)
|
|||
state->rsY);
|
||||
}
|
||||
|
||||
Uint32 SdlInputHandler::releaseLeftButtonTimerCallback(Uint32, void*)
|
||||
Uint32 SdlInputHandler::longPressTimerCallback(Uint32, void *param)
|
||||
{
|
||||
// Raise the left click and start a right click
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint32 SdlInputHandler::releaseRightButtonTimerCallback(Uint32, void*)
|
||||
{
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint32 SdlInputHandler::dragTimerCallback(Uint32, void *param)
|
||||
{
|
||||
auto me = reinterpret_cast<SdlInputHandler*>(param);
|
||||
|
||||
// Check how many fingers are down now to decide
|
||||
// which button to hold down
|
||||
if (me->m_NumFingersDown == 2) {
|
||||
me->m_DragButton = BUTTON_RIGHT;
|
||||
}
|
||||
else if (me->m_NumFingersDown == 1) {
|
||||
me->m_DragButton = BUTTON_LEFT;
|
||||
}
|
||||
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, me->m_DragButton);
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1254,10 +1230,8 @@ void SdlInputHandler::rumble(unsigned short controllerNumber, unsigned short low
|
|||
#endif
|
||||
}
|
||||
|
||||
void SdlInputHandler::handleTouchFingerEvent(SDL_Window*, SDL_TouchFingerEvent* event)
|
||||
void SdlInputHandler::handleTouchFingerEvent(SDL_Window* window, SDL_TouchFingerEvent* event)
|
||||
{
|
||||
int fingerIndex = -1;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 10)
|
||||
if (SDL_GetTouchDeviceType(event->touchId) != SDL_TOUCH_DEVICE_DIRECT) {
|
||||
// Ignore anything that isn't a touchscreen. We may get callbacks
|
||||
|
@ -1279,128 +1253,74 @@ void SdlInputHandler::handleTouchFingerEvent(SDL_Window*, SDL_TouchFingerEvent*
|
|||
// leave the client area during a drag motion.
|
||||
// dx and dy are deltas from the last touch event, not the first touch down.
|
||||
|
||||
// Determine the index of this finger using our list
|
||||
// of fingers that are currently active on screen.
|
||||
// This is also required to handle finger up which
|
||||
// where the finger will not be in SDL_GetTouchFinger()
|
||||
// anymore.
|
||||
if (event->type != SDL_FINGERDOWN) {
|
||||
for (int i = 0; i < MAX_FINGERS; i++) {
|
||||
if (event->fingerId == m_TouchDownEvent[i].fingerId) {
|
||||
fingerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Resolve the new finger by determining the ID of each
|
||||
// finger on the display.
|
||||
int numTouchFingers = SDL_GetNumTouchFingers(event->touchId);
|
||||
for (int i = 0; i < numTouchFingers; i++) {
|
||||
SDL_Finger* finger = SDL_GetTouchFinger(event->touchId, i);
|
||||
SDL_assert(finger != nullptr);
|
||||
if (finger != nullptr) {
|
||||
if (finger->id == event->fingerId) {
|
||||
fingerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fingerIndex < 0 || fingerIndex >= MAX_FINGERS) {
|
||||
// Too many fingers
|
||||
// Ignore touch down events with more than one finger
|
||||
if (event->type == SDL_FINGERDOWN && SDL_GetNumTouchFingers(event->touchId) > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle cursor motion based on the position of the
|
||||
// primary finger on screen
|
||||
if (fingerIndex == 0) {
|
||||
// The event x and y values are relative to our window width
|
||||
// and height. However, we want to scale them to be relative
|
||||
// to the host resolution. Fortunately this is easy since we
|
||||
// already have normalized values. We'll just multiply them
|
||||
// by the stream dimensions to get real X and Y values rather
|
||||
// than the client window dimensions.
|
||||
short deltaX = static_cast<short>(event->dx * m_StreamWidth);
|
||||
short deltaY = static_cast<short>(event->dy * m_StreamHeight);
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||
}
|
||||
// Ignore touch move and touch up events from the non-primary finger
|
||||
if (event->type != SDL_FINGERDOWN && event->fingerId != m_LastTouchDownEvent.fingerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start a drag timer when primary or secondary
|
||||
// fingers go down
|
||||
if (event->type == SDL_FINGERDOWN &&
|
||||
(fingerIndex == 0 || fingerIndex == 1)) {
|
||||
SDL_RemoveTimer(m_DragTimer);
|
||||
m_DragTimer = SDL_AddTimer(DRAG_ACTIVATION_DELAY,
|
||||
dragTimerCallback,
|
||||
this);
|
||||
SDL_Rect src, dst;
|
||||
int windowWidth, windowHeight;
|
||||
|
||||
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
||||
|
||||
src.x = src.y = 0;
|
||||
src.w = m_StreamWidth;
|
||||
src.h = m_StreamHeight;
|
||||
|
||||
dst.x = dst.y = 0;
|
||||
dst.w = windowWidth;
|
||||
dst.h = windowHeight;
|
||||
|
||||
// Use the stream and window sizes to determine the video region
|
||||
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
||||
|
||||
if (qSqrt(qPow(event->x - m_LastTouchDownEvent.x, 2) + qPow(event->y - m_LastTouchDownEvent.y, 2)) > LONG_PRESS_ACTIVATION_DELTA) {
|
||||
// Moved too far since touch down. Cancel the long press timer.
|
||||
SDL_RemoveTimer(m_LongPressTimer);
|
||||
m_LongPressTimer = 0;
|
||||
}
|
||||
|
||||
if (event->type == SDL_FINGERMOTION) {
|
||||
// Count the total cumulative dx/dy that the finger
|
||||
// has moved.
|
||||
m_CumulativeDelta[fingerIndex] += qAbs(event->x);
|
||||
m_CumulativeDelta[fingerIndex] += qAbs(event->y);
|
||||
// Don't reposition for finger down events within the deadzone. This makes double-clicking easier.
|
||||
if (event->type != SDL_FINGERDOWN ||
|
||||
event->timestamp - m_LastTouchUpEvent.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY ||
|
||||
qSqrt(qPow(event->x - m_LastTouchUpEvent.x, 2) + qPow(event->y - m_LastTouchUpEvent.y, 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) {
|
||||
// Scale window-relative events to be video-relative and clamp to video region
|
||||
short x = qMin(qMax((int)(event->x * windowWidth), dst.x), dst.x + dst.w);
|
||||
short y = qMin(qMax((int)(event->y * windowHeight), dst.y), dst.y + dst.h);
|
||||
|
||||
// If it's outside the deadzone delta, cancel drags and taps
|
||||
if (m_CumulativeDelta[fingerIndex] > DEAD_ZONE_DELTA) {
|
||||
SDL_RemoveTimer(m_DragTimer);
|
||||
m_DragTimer = 0;
|
||||
|
||||
// This effectively cancels the tap logic below
|
||||
m_TouchDownEvent[fingerIndex].timestamp = 0;
|
||||
}
|
||||
// Update the cursor position relative to the video region
|
||||
LiSendMousePositionEvent(x - dst.x, y - dst.y, dst.w, dst.h);
|
||||
}
|
||||
|
||||
if (event->type == SDL_FINGERUP) {
|
||||
// Cancel the drag timer on finger up
|
||||
SDL_RemoveTimer(m_DragTimer);
|
||||
m_DragTimer = 0;
|
||||
if (event->type == SDL_FINGERDOWN) {
|
||||
m_LastTouchDownEvent = *event;
|
||||
|
||||
// Release any drag
|
||||
if (m_DragButton != 0) {
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, m_DragButton);
|
||||
m_DragButton = 0;
|
||||
}
|
||||
// 2 finger tap
|
||||
else if (event->timestamp - m_TouchDownEvent[1].timestamp < 250) {
|
||||
// Zero timestamp of the primary finger to ensure we won't
|
||||
// generate a left click if the primary finger comes up soon.
|
||||
m_TouchDownEvent[0].timestamp = 0;
|
||||
// Start/restart the long press timer
|
||||
SDL_RemoveTimer(m_LongPressTimer);
|
||||
m_LongPressTimer = SDL_AddTimer(LONG_PRESS_ACTIVATION_DELAY,
|
||||
longPressTimerCallback,
|
||||
nullptr);
|
||||
|
||||
// Press down the right mouse button
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
|
||||
|
||||
// Queue a timer to release it in 100 ms
|
||||
SDL_RemoveTimer(m_RightButtonReleaseTimer);
|
||||
m_RightButtonReleaseTimer = SDL_AddTimer(TAP_BUTTON_RELEASE_DELAY,
|
||||
releaseRightButtonTimerCallback,
|
||||
nullptr);
|
||||
}
|
||||
// 1 finger tap
|
||||
else if (event->timestamp - m_TouchDownEvent[0].timestamp < 250) {
|
||||
// Press down the left mouse button
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
|
||||
|
||||
// Queue a timer to release it in 100 ms
|
||||
SDL_RemoveTimer(m_LeftButtonReleaseTimer);
|
||||
m_LeftButtonReleaseTimer = SDL_AddTimer(TAP_BUTTON_RELEASE_DELAY,
|
||||
releaseLeftButtonTimerCallback,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
m_NumFingersDown = SDL_GetNumTouchFingers(event->touchId);
|
||||
|
||||
if (event->type == SDL_FINGERDOWN) {
|
||||
m_TouchDownEvent[fingerIndex] = *event;
|
||||
m_CumulativeDelta[fingerIndex] = 0;
|
||||
// Left button down on finger down
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
|
||||
}
|
||||
else if (event->type == SDL_FINGERUP) {
|
||||
m_TouchDownEvent[fingerIndex] = {};
|
||||
m_LastTouchUpEvent = *event;
|
||||
|
||||
// Cancel the long press timer
|
||||
SDL_RemoveTimer(m_LongPressTimer);
|
||||
m_LongPressTimer = 0;
|
||||
|
||||
// Left button up on finger up
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
|
||||
|
||||
// Raise right button too in case we triggered a long press gesture
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,13 +81,7 @@ private:
|
|||
void sendGamepadState(GamepadState* state);
|
||||
|
||||
static
|
||||
Uint32 releaseLeftButtonTimerCallback(Uint32 interval, void* param);
|
||||
|
||||
static
|
||||
Uint32 releaseRightButtonTimerCallback(Uint32 interval, void* param);
|
||||
|
||||
static
|
||||
Uint32 dragTimerCallback(Uint32 interval, void* param);
|
||||
Uint32 longPressTimerCallback(Uint32 interval, void* param);
|
||||
|
||||
static
|
||||
Uint32 mouseMoveTimerCallback(Uint32 interval, void* param);
|
||||
|
@ -105,13 +99,9 @@ private:
|
|||
QSet<short> m_KeysDown;
|
||||
bool m_FakeCaptureActive;
|
||||
|
||||
SDL_TouchFingerEvent m_TouchDownEvent[MAX_FINGERS];
|
||||
float m_CumulativeDelta[MAX_FINGERS];
|
||||
SDL_TimerID m_LeftButtonReleaseTimer;
|
||||
SDL_TimerID m_RightButtonReleaseTimer;
|
||||
SDL_TimerID m_DragTimer;
|
||||
char m_DragButton;
|
||||
int m_NumFingersDown;
|
||||
SDL_TouchFingerEvent m_LastTouchDownEvent;
|
||||
SDL_TouchFingerEvent m_LastTouchUpEvent;
|
||||
SDL_TimerID m_LongPressTimer;
|
||||
int m_StreamWidth;
|
||||
int m_StreamHeight;
|
||||
bool m_AbsoluteMouseMode;
|
||||
|
|
Loading…
Reference in a new issue