Add touch gestures

This adds gestures that allow touch device users to emulate the
following mouse/keyboard actions:

- Right-click: touch outside the game's viewport
- Ctrl key: one-finger touch and hold (for one second)
- Mouse wheel: Swipe up / down with two fingers
This commit is contained in:
kichikuou
2024-03-16 13:35:12 +09:00
parent 5e80c96587
commit 18a71960e8
+70 -2
View File
@@ -151,6 +151,13 @@ static SDL_GameController *controllers[MAX_CONTROLLERS];
static SDL_MouseButtonEvent deferred_synthetic_mouse_event;
#define SYNTHETIC_MOUSE_EVENT_DELAY 50
static uint32_t long_touch_start_timestamp;
static SDL_FRect long_touch_finger_rect;
#define LONG_TOUCH_DURATION 1000
static float scroll_gesture_y;
#define SCROLL_GESTURE_SENSITIVITY 0.02f // 2% of screen height
static enum sact_keycode sdl_to_sact_button(int button)
{
switch (button) {
@@ -230,6 +237,21 @@ static void mouse_event(SDL_MouseButtonEvent *e)
#endif
}
static void synthetic_mouse_event(SDL_MouseButtonEvent *e)
{
if (e->state == SDL_PRESSED) {
// Touch outside the viewport is treated as right-click.
SDL_Point p = { .x = e->x, .y = e->y };
if (SDL_PointInRect(&p, &sdl.viewport))
key_state[VK_LBUTTON] = true;
else
key_state[VK_RBUTTON] = true;
} else {
key_state[VK_LBUTTON] = false;
key_state[VK_RBUTTON] = false;
}
}
#define JOYAXIS_DEADZONE 13500
enum joyaxis_axis {
@@ -485,9 +507,14 @@ void handle_events(void)
{
// Flush the deferred mouse button event if it's older than 50ms.
if (deferred_synthetic_mouse_event.timestamp && deferred_synthetic_mouse_event.timestamp + SYNTHETIC_MOUSE_EVENT_DELAY < SDL_GetTicks()) {
mouse_event(&deferred_synthetic_mouse_event);
synthetic_mouse_event(&deferred_synthetic_mouse_event);
deferred_synthetic_mouse_event.timestamp = 0;
}
// Long touch emulates pressing the Ctrl key.
if (long_touch_start_timestamp && long_touch_start_timestamp + LONG_TOUCH_DURATION < SDL_GetTicks()) {
key_state[VK_LBUTTON] = false;
key_state[VK_CONTROL] = true;
}
SDL_Event e;
while (SDL_PollEvent(&e)) {
@@ -530,7 +557,7 @@ void handle_events(void)
case SDL_MOUSEBUTTONDOWN:
if (e.button.which == SDL_TOUCH_MOUSEID) {
if (deferred_synthetic_mouse_event.timestamp)
mouse_event(&deferred_synthetic_mouse_event);
synthetic_mouse_event(&deferred_synthetic_mouse_event);
deferred_synthetic_mouse_event = e.button;
} else {
mouse_event(&e.button);
@@ -539,6 +566,47 @@ void handle_events(void)
case SDL_MOUSEWHEEL:
wheel_dir = e.wheel.y;
break;
case SDL_FINGERDOWN:
if (SDL_GetNumTouchFingers(e.tfinger.touchId) >= 2) {
// The user is about to start a multi-touch gesture.
key_state[VK_LBUTTON] = false;
key_state[VK_RBUTTON] = false;
deferred_synthetic_mouse_event.timestamp = 0;
long_touch_start_timestamp = 0;
scroll_gesture_y = -1.0f;
break;
}
long_touch_start_timestamp = e.tfinger.timestamp;
// Movement within this rect (1% of the screen size from the touch
// start position) will be ignored.
long_touch_finger_rect = (SDL_FRect) {
.x = e.tfinger.x - 0.01,
.y = e.tfinger.y - 0.01,
.w = 0.02,
.h = 0.02
};
break;
case SDL_FINGERMOTION:
if (SDL_PointInFRect(&(SDL_FPoint){ e.tfinger.x, e.tfinger.y }, &long_touch_finger_rect))
break;
// Cancel the timer only if Ctrl emulation has not already started.
if (!key_state[VK_CONTROL])
long_touch_start_timestamp = 0;
break;
case SDL_FINGERUP:
long_touch_start_timestamp = 0;
key_state[VK_CONTROL] = false;
break;
case SDL_MULTIGESTURE:
if (e.mgesture.numFingers == 2) {
if (scroll_gesture_y < 0.0f)
scroll_gesture_y = e.mgesture.y;
float dy = scroll_gesture_y - e.mgesture.y;
if (dy * dy > SCROLL_GESTURE_SENSITIVITY * SCROLL_GESTURE_SENSITIVITY) {
wheel_dir = dy < 0 ? 1 : -1; // Swipe up to scroll down.
scroll_gesture_y = e.mgesture.y;
}
}
case SDL_CONTROLLERDEVICEADDED:
if (e.cdevice.which < MAX_CONTROLLERS)
controllers[e.cdevice.which] = SDL_GameControllerOpen(e.cdevice.which);