Add SDL Pinch events (#9445)

This commit is contained in:
Sylvain Becker
2025-10-12 23:44:23 +02:00
committed by GitHub
parent e2195621d7
commit 71bf56c9e4
21 changed files with 605 additions and 13 deletions

View File

@@ -1084,6 +1084,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
public static native boolean nativeAllowRecreateActivity();
public static native int nativeCheckSDLThreadCounter();
public static native void onNativeFileDialog(int requestCode, String[] filelist, int filter);
public static native void onNativePinchStart();
public static native void onNativePinchUpdate(float scale);
public static native void onNativePinchEnd();
/**
* This method is called by SDL using JNI.

View File

@@ -23,6 +23,7 @@ import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.ScaleGestureDetector;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
@@ -31,7 +32,8 @@ import android.view.WindowManager;
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener, SensorEventListener {
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
// Sensors
protected SensorManager mSensorManager;
@@ -43,11 +45,16 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// Is SurfaceView ready for rendering
protected boolean mIsSurfaceReady;
// Pinch events
private final ScaleGestureDetector scaleGestureDetector;
// Startup
protected SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
scaleGestureDetector = new ScaleGestureDetector(context, this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
@@ -294,6 +301,8 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
break;
} while (++i < pointerCount);
scaleGestureDetector.onTouchEvent(event);
return true;
}
@@ -415,4 +424,23 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
return false;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
SDLActivity.onNativePinchUpdate(scale);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
SDLActivity.onNativePinchStart();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
SDLActivity.onNativePinchEnd();
}
}

View File

@@ -441,6 +441,23 @@ macro(CheckX11)
if(HAVE_XINPUT2_MULTITOUCH)
set(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1)
endif()
# Check for gesture
check_c_source_compiles("
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/extensions/XInput2.h>
int event_type = XI_GesturePinchBegin;
XITouchClassInfo *t;
Status XIAllowTouchEvents(Display *a,int b,unsigned int c,Window d,int f) {
return (Status)0;
}
int main(int argc, char **argv) { return 0; }" HAVE_XINPUT2_GESTURE)
if(HAVE_XINPUT2_GESTURE)
set(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE 1)
endif()
endif()
# check along with XInput2.h because we use Xfixes with XIBarrierReleasePointer

View File

@@ -218,6 +218,11 @@ typedef enum SDL_EventType
SDL_EVENT_FINGER_MOTION,
SDL_EVENT_FINGER_CANCELED,
/* Pinch events */
SDL_EVENT_PINCH_BEGIN = 0x710, /**< Pinch gesture started */
SDL_EVENT_PINCH_UPDATE, /**< Pinch gesture updated */
SDL_EVENT_PINCH_END, /**< Pinch gesture ended */
/* 0x800, 0x801, and 0x802 were the Gesture events from SDL2. Do not reuse these values! sdl2-compat needs them! */
/* Clipboard events */
@@ -788,6 +793,18 @@ typedef struct SDL_TouchFingerEvent
SDL_WindowID windowID; /**< The window underneath the finger, if any */
} SDL_TouchFingerEvent;
/**
* Pinch event structure (event.pinch.*)
*/
typedef struct SDL_PinchFingerEvent
{
SDL_EventType type; /**< ::SDL_EVENT_PINCH_BEGIN or ::SDL_EVENT_PINCH_UPDATE or ::SDL_EVENT_PINCH_END */
Uint32 reserved;
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
float scale; /**< The scale change since the last SDL_EVENT_PINCH_UPDATE. Scale < 1 is "zoom out". Scale > 1 is "zoom in". */
SDL_WindowID windowID; /**< The window underneath the finger, if any */
} SDL_PinchFingerEvent;
/**
* Pressure-sensitive pen proximity event structure (event.pproximity.*)
*
@@ -1025,6 +1042,7 @@ typedef union SDL_Event
SDL_QuitEvent quit; /**< Quit request event data */
SDL_UserEvent user; /**< Custom event data */
SDL_TouchFingerEvent tfinger; /**< Touch finger event data */
SDL_PinchFingerEvent pinch; /**< Pinch event data */
SDL_PenProximityEvent pproximity; /**< Pen proximity event data */
SDL_PenTouchEvent ptouch; /**< Pen tip touching event data */
SDL_PenMotionEvent pmotion; /**< Pen motion event data */

View File

@@ -436,6 +436,7 @@
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2 1
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO 1
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE @SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE@
#cmakedefine SDL_VIDEO_DRIVER_X11_XRANDR 1
#cmakedefine SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1
#cmakedefine SDL_VIDEO_DRIVER_X11_XSHAPE 1

View File

@@ -121,6 +121,16 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchStart)(
JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchUpdate)(
JNIEnv *env, jclass jcls,
jfloat scale);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchEnd)(
JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
JNIEnv *env, jclass jcls,
jint button, jint action, jfloat x, jfloat y, jboolean relative);
@@ -221,6 +231,9 @@ static JNINativeMethod SDLActivity_tab[] = {
{ "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) },
{ "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) },
{ "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) },
{ "onNativePinchStart", "()V", SDL_JAVA_INTERFACE(onNativePinchStart) },
{ "onNativePinchUpdate", "(F)V", SDL_JAVA_INTERFACE(onNativePinchUpdate) },
{ "onNativePinchEnd", "()V", SDL_JAVA_INTERFACE(onNativePinchEnd) },
{ "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) },
{ "onNativePen", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativePen) },
{ "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) },
@@ -1366,6 +1379,43 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
SDL_UnlockMutex(Android_ActivityMutex);
}
// Pinch
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchStart)(
JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, Android_Window, 0);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchUpdate)(
JNIEnv *env, jclass jcls, jfloat scale)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, Android_Window, scale);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePinchEnd)(
JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
SDL_SendPinch(SDL_EVENT_PINCH_END, 0, Android_Window, 0);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
// Mouse
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
JNIEnv *env, jclass jcls,

View File

@@ -770,6 +770,20 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen)
break;
#undef PRINT_FINGER_EVENT
#define PRINT_PINCH_EVENT(event) \
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u scale=%f)", \
(uint)event->pinch.timestamp, event->pinch.scale)
SDL_EVENT_CASE(SDL_EVENT_PINCH_BEGIN)
PRINT_PINCH_EVENT(event);
break;
SDL_EVENT_CASE(SDL_EVENT_PINCH_UPDATE)
PRINT_PINCH_EVENT(event);
break;
SDL_EVENT_CASE(SDL_EVENT_PINCH_END)
PRINT_PINCH_EVENT(event);
break;
#undef PRINT_PINCH_EVENT
#define PRINT_PTOUCH_EVENT(event) \
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g eraser=%s state=%s)", \
(uint)event->ptouch.timestamp, (uint)event->ptouch.windowID, (uint)event->ptouch.which, (uint)event->ptouch.pen_state, event->ptouch.x, event->ptouch.y, \
@@ -902,12 +916,13 @@ static void SDL_LogEvent(const SDL_Event *event)
return;
}
// sensor/mouse/pen/finger motion are spammy, ignore these if they aren't demanded.
// sensor/mouse/pen/finger/pinch motion are spammy, ignore these if they aren't demanded.
if ((SDL_EventLoggingVerbosity < 2) &&
((event->type == SDL_EVENT_MOUSE_MOTION) ||
(event->type == SDL_EVENT_FINGER_MOTION) ||
(event->type == SDL_EVENT_PEN_AXIS) ||
(event->type == SDL_EVENT_PEN_MOTION) ||
(event->type == SDL_EVENT_PINCH_UPDATE) ||
(event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) ||
(event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) ||
(event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) ||

View File

@@ -500,3 +500,19 @@ void SDL_QuitTouch(void)
SDL_free(SDL_touchDevices);
SDL_touchDevices = NULL;
}
int SDL_SendPinch(SDL_EventType type, Uint64 timestamp, SDL_Window *window, float scale)
{
/* Post the event, if desired */
int posted = 0;
if (SDL_EventEnabled(type)) {
SDL_Event event;
event.type = type;
event.common.timestamp = timestamp;
event.pinch.scale = scale;
event.pinch.windowID = window ? SDL_GetWindowID(window) : 0;
posted = (SDL_PushEvent(&event) > 0);
}
return posted;
}

View File

@@ -57,4 +57,7 @@ extern void SDL_DelTouch(SDL_TouchID id);
// Shutdown the touch subsystem
extern void SDL_QuitTouch(void);
// Send Gesture events
extern int SDL_SendPinch(SDL_EventType type, Uint64 timestamp, SDL_Window *window, float scale);
#endif // SDL_touch_c_h_

View File

@@ -1928,6 +1928,16 @@ void SDLTest_PrintEvent(const SDL_Event *event)
event->tfinger.dx, event->tfinger.dy, event->tfinger.pressure);
break;
case SDL_EVENT_PINCH_BEGIN:
SDL_Log("SDL EVENT: Pinch Begin");
break;
case SDL_EVENT_PINCH_UPDATE:
SDL_Log("SDL EVENT: Pinch Update, scale=%f", event->pinch.scale);
break;
case SDL_EVENT_PINCH_END:
SDL_Log("SDL EVENT: Pinch End");
break;
case SDL_EVENT_RENDER_TARGETS_RESET:
SDL_Log("SDL EVENT: render targets reset in window %" SDL_PRIu32, event->render.windowID);
break;
@@ -2238,6 +2248,7 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const
event->type != SDL_EVENT_FINGER_MOTION &&
event->type != SDL_EVENT_PEN_MOTION &&
event->type != SDL_EVENT_PEN_AXIS &&
event->type != SDL_EVENT_PINCH_UPDATE &&
event->type != SDL_EVENT_JOYSTICK_AXIS_MOTION) ||
(state->verbose & VERBOSE_MOTION)) {
SDLTest_PrintEvent(event);

View File

@@ -122,6 +122,7 @@ typedef enum
- (void)touchesMovedWithEvent:(NSEvent *)theEvent;
- (void)touchesEndedWithEvent:(NSEvent *)theEvent;
- (void)touchesCancelledWithEvent:(NSEvent *)theEvent;
- (void)magnifyWithEvent:(NSEvent *) theEvent;
// Touch event handling
- (void)handleTouches:(NSTouchPhase)phase withEvent:(NSEvent *)theEvent;

View File

@@ -1990,6 +1990,27 @@ static void Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL
[self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
}
- (void)magnifyWithEvent:(NSEvent *)theEvent
{
switch ([theEvent phase]) {
case NSEventPhaseBegan:
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, Cocoa_GetEventTimestamp([theEvent timestamp]), NULL, 0);
break;
case NSEventPhaseChanged:
{
CGFloat scale = 1.0f + [theEvent magnification];
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, Cocoa_GetEventTimestamp([theEvent timestamp]), NULL, scale);
}
break;
case NSEventPhaseEnded:
case NSEventPhaseCancelled:
SDL_SendPinch(SDL_EVENT_PINCH_END, Cocoa_GetEventTimestamp([theEvent timestamp]), NULL, 0);
break;
default:
break;
}
}
- (void)handleTouches:(NSTouchPhase)phase withEvent:(NSEvent *)theEvent
{
NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];

View File

@@ -46,6 +46,9 @@
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
#if !defined(SDL_PLATFORM_TVOS)
- (IBAction)sdlPinchGesture:(UIPinchGestureRecognizer *)sender;
#endif
- (void)safeAreaInsetsDidChange;

View File

@@ -48,6 +48,7 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
SDL_TouchID directTouchId;
SDL_TouchID indirectTouchId;
float pinch_scale;
#if !defined(SDL_PLATFORM_TVOS)
UIPointerInteraction *indirectPointerInteraction API_AVAILABLE(ios(13.4));
@@ -76,6 +77,15 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
[self addGestureRecognizer:swipeRight];
#endif
#if !defined(SDL_PLATFORM_TVOS)
/* Pinch gestures */
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(sdlPinchGesture:)];
pinchGesture.cancelsTouchesInView = NO;
pinchGesture.delaysTouchesBegan = NO;
pinchGesture.delaysTouchesEnded = NO;
[self addGestureRecognizer:pinchGesture];
#endif
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.autoresizesSubviews = YES;
@@ -470,6 +480,39 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
(int)SDL_ceilf(self.safeAreaInsets.bottom));
}
#if !defined(SDL_PLATFORM_TVOS)
- (IBAction)sdlPinchGesture:(UIPinchGestureRecognizer *)sender
{
CGFloat scale = sender.scale;
UIGestureRecognizerState state = sender.state;
switch (state) {
case UIGestureRecognizerStateBegan:
pinch_scale = 1.0f;
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, sdlwindow, 0);
break;
case UIGestureRecognizerStateChanged:
if (pinch_scale > 0.0f) {
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, sdlwindow, scale / pinch_scale);
}
pinch_scale = scale;
break;
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
SDL_SendPinch(SDL_EVENT_PINCH_END, 0, sdlwindow, 0);
break;
default:
break;
}
}
#endif
- (SDL_Scancode)scancodeFromPress:(UIPress *)press
{
if (press.key != nil) {

View File

@@ -45,6 +45,7 @@
#include "tablet-v2-client-protocol.h"
#include "primary-selection-unstable-v1-client-protocol.h"
#include "input-timestamps-unstable-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -1416,6 +1417,43 @@ static const struct wl_touch_listener touch_listener = {
touch_handler_orientation // Version 6
};
void pinch_begin(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t serial,
uint32_t time,
struct wl_surface *surface,
uint32_t fingers)
{
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, NULL, 0);
}
void pinch_update(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t time,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t scale,
wl_fixed_t rotation)
{
float s = (float)(wl_fixed_to_double(scale));
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, NULL, s);
}
void pinch_end(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t serial,
uint32_t time,
int32_t cancelled)
{
SDL_SendPinch(SDL_EVENT_PINCH_END, 0, NULL, 0);
}
static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_listener = {
pinch_begin,
pinch_update,
pinch_end
};
// Fallback for xkb_keymap_key_get_mods_for_level(), which is only available from 1.0.0, while the SDL minimum is 0.5.0.
#if !SDL_XKBCOMMON_CHECK_VERSION(1, 0, 0)
static size_t xkb_legacy_get_mods_for_level(SDL_WaylandSeat *seat, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, xkb_mod_mask_t *masks_out, size_t masks_size)
@@ -2387,6 +2425,10 @@ static void Wayland_SeatDestroyTouch(SDL_WaylandSeat *seat)
}
}
if (seat->touch.gesture_pinch) {
zwp_pointer_gesture_pinch_v1_destroy(seat->touch.gesture_pinch);
}
SDL_zero(seat->touch);
WAYLAND_wl_list_init(&seat->touch.points);
}
@@ -2430,6 +2472,12 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w
}
SDL_AddTouch((SDL_TouchID)(uintptr_t)seat->touch.wl_touch, SDL_TOUCH_DEVICE_DIRECT, name_fmt);
/* Pinch gesture */
seat->touch.gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(seat->display->zwp_pointer_gestures, seat->pointer.wl_pointer);
zwp_pointer_gesture_pinch_v1_set_user_data(seat->touch.gesture_pinch, seat);
zwp_pointer_gesture_pinch_v1_add_listener(seat->touch.gesture_pinch, &gesture_pinch_listener, seat);
} else if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH) && seat->touch.wl_touch) {
Wayland_SeatDestroyTouch(seat);
}

View File

@@ -192,6 +192,7 @@ typedef struct SDL_WaylandSeat
struct zwp_input_timestamps_v1 *timestamps;
Uint64 highres_timestamp_ns;
struct wl_list points;
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
} touch;
struct

View File

@@ -67,6 +67,7 @@
#include "xdg-toplevel-icon-v1-client-protocol.h"
#include "color-management-v1-client-protocol.h"
#include "pointer-warp-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -1322,6 +1323,8 @@ static void handle_registry_global(void *data, struct wl_registry *registry, uin
Wayland_InitColorManager(d);
} else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) {
d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1);
} else if (SDL_strcmp(interface, "zwp_pointer_gestures_v1") == 0) {
d->zwp_pointer_gestures = wl_registry_bind(d->registry, id, &zwp_pointer_gestures_v1_interface, 1);
}
#ifdef SDL_WL_FIXES_VERSION
else if (SDL_strcmp(interface, "wl_fixes") == 0) {
@@ -1645,6 +1648,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
data->wp_pointer_warp_v1 = NULL;
}
if (data->zwp_pointer_gestures) {
zwp_pointer_gestures_v1_destroy(data->zwp_pointer_gestures);
data->zwp_pointer_gestures = NULL;
}
if (data->compositor) {
wl_compositor_destroy(data->compositor);
data->compositor = NULL;

View File

@@ -86,6 +86,7 @@ struct SDL_VideoData
struct wp_color_manager_v1 *wp_color_manager_v1;
struct zwp_tablet_manager_v2 *tablet_manager;
struct wl_fixes *wl_fixes;
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures;
struct xkb_context *xkb_context;

View File

@@ -38,6 +38,11 @@ static bool xinput2_initialized;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
static bool xinput2_multitouch_supported;
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
static int xinput2_gesture_supported = 0;
#endif
static int X11_Xinput2IsGestureSupported(void);
/* Opcode returned X11_XQueryExtension
* It will be used in event processing
@@ -272,8 +277,8 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
return false; // X server does not have XInput at all
}
// We need at least 2.2 for Multitouch, 2.0 otherwise.
version = query_xinput2_version(data->display, 2, 2);
// We need at least 2.4 for Gesture, 2.2 for Multitouch, 2.0 otherwise.
version = query_xinput2_version(data->display, 2, 4);
if (!xinput2_version_atleast(version, 2, 0)) {
return false; // X server does not support the version we want at all.
}
@@ -287,6 +292,9 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2
xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE // Gesture needs XInput 2.4
xinput2_gesture_supported = xinput2_version_atleast(version, 2, 4);
#endif
// Populate the atoms for finding relative axes
xinput2_rel_x_atom = X11_XInternAtom(data->display, "Rel X", False);
@@ -735,6 +743,28 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
} break;
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
case XI_GesturePinchBegin:
case XI_GesturePinchUpdate:
case XI_GesturePinchEnd:
{
const XIGesturePinchEvent *xev = (const XIGesturePinchEvent *)cookie->data;
float x, y;
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
if (cookie->evtype == XI_GesturePinchBegin) {
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, window, 0);
} else if (cookie->evtype == XI_GesturePinchUpdate) {
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, window, (float)xev->scale);
} else {
SDL_SendPinch(SDL_EVENT_PINCH_END, 0, window, 0);
}
} break;
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
}
@@ -774,6 +804,14 @@ void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window)
XISetMask(mask, XI_Motion);
}
if (X11_Xinput2IsGestureSupported()) {
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
XISetMask(mask, XI_GesturePinchBegin);
XISetMask(mask, XI_GesturePinchUpdate);
XISetMask(mask, XI_GesturePinchEnd);
#endif
}
X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1);
#endif
}
@@ -836,6 +874,15 @@ bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *windo
return false;
}
int X11_Xinput2IsGestureSupported(void)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
return xinput2_initialized && xinput2_gesture_supported;
#else
return 0;
#endif
}
void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH

View File

@@ -30,6 +30,7 @@ static SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
static float angle = 0.0f;
static int translate_cx = 0;
static int translate_cy = 0;
static float pinch_scale = 1.0f;
static int done;
@@ -103,11 +104,14 @@ static void loop(void)
} else if (event.key.key == SDLK_DOWN) {
translate_cy += 1;
} else {
SDLTest_CommonEvent(state, &event, &done);
}
} else {
SDLTest_CommonEvent(state, &event, &done);
} else if (event.type == SDL_EVENT_PINCH_BEGIN) {
} else if (event.type == SDL_EVENT_PINCH_UPDATE) {
pinch_scale *= event.pinch.scale;
} else if (event.type == SDL_EVENT_PINCH_END) {
}
SDLTest_CommonEvent(state, &event, &done);
}
for (i = 0; i < state->num_windows; ++i) {
@@ -136,24 +140,24 @@ static void loop(void)
cy += translate_cy;
a = (angle * SDL_PI_F) / 180.0f;
verts[0].position.x = cx + d * SDL_cosf(a);
verts[0].position.y = cy + d * SDL_sinf(a);
verts[0].position.x = cx + (d * SDL_cosf(a)) * pinch_scale;
verts[0].position.y = cy + (d * SDL_sinf(a)) * pinch_scale;
verts[0].color.r = 1.0f;
verts[0].color.g = 0;
verts[0].color.b = 0;
verts[0].color.a = 1.0f;
a = ((angle + 120) * SDL_PI_F) / 180.0f;
verts[1].position.x = cx + d * SDL_cosf(a);
verts[1].position.y = cy + d * SDL_sinf(a);
verts[1].position.x = cx + (d * SDL_cosf(a)) * pinch_scale;
verts[1].position.y = cy + (d * SDL_sinf(a)) * pinch_scale;
verts[1].color.r = 0;
verts[1].color.g = 1.0f;
verts[1].color.b = 0;
verts[1].color.a = 1.0f;
a = ((angle + 240) * SDL_PI_F) / 180.0f;
verts[2].position.x = cx + d * SDL_cosf(a);
verts[2].position.y = cy + d * SDL_sinf(a);
verts[2].position.x = cx + (d * SDL_cosf(a)) * pinch_scale;
verts[2].position.y = cy + (d * SDL_sinf(a)) * pinch_scale;
verts[2].color.r = 0;
verts[2].color.g = 0;
verts[2].color.b = 1.0f;

View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="pointer_gestures_unstable_v1">
<interface name="zwp_pointer_gestures_v1" version="3">
<description summary="touchpad gestures">
A global interface to provide semantic touchpad gestures for a given
pointer.
Three gestures are currently supported: swipe, pinch, and hold.
Pinch and swipe gestures follow a three-stage cycle: begin, update,
end, hold gestures follow a two-stage cycle: begin and end. All
gestures are identified by a unique id.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<request name="get_swipe_gesture">
<description summary="get swipe gesture">
Create a swipe gesture object. See the
wl_pointer_gesture_swipe interface for details.
</description>
<arg name="id" type="new_id" interface="zwp_pointer_gesture_swipe_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
<request name="get_pinch_gesture">
<description summary="get pinch gesture">
Create a pinch gesture object. See the
wl_pointer_gesture_pinch interface for details.
</description>
<arg name="id" type="new_id" interface="zwp_pointer_gesture_pinch_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
<!-- Version 2 additions -->
<request name="release" type="destructor" since="2">
<description summary="destroy the pointer gesture object">
Destroy the pointer gesture object. Swipe, pinch and hold objects
created via this gesture object remain valid.
</description>
</request>
<!-- Version 3 additions -->
<request name="get_hold_gesture" since="3">
<description summary="get hold gesture">
Create a hold gesture object. See the
wl_pointer_gesture_hold interface for details.
</description>
<arg name="id" type="new_id" interface="zwp_pointer_gesture_hold_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
</interface>
<interface name="zwp_pointer_gesture_swipe_v1" version="2">
<description summary="a swipe gesture object">
A swipe gesture object notifies a client about a multi-finger swipe
gesture detected on an indirect input device such as a touchpad.
The gesture is usually initiated by multiple fingers moving in the
same direction but once initiated the direction may change.
The precise conditions of when such a gesture is detected are
implementation-dependent.
A gesture consists of three stages: begin, update (optional) and end.
There cannot be multiple simultaneous hold, pinch or swipe gestures on a
same pointer/seat, how compositors prevent these situations is
implementation-dependent.
A gesture may be cancelled by the compositor or the hardware.
Clients should not consider performing permanent or irreversible
actions until the end of a gesture has been received.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the pointer swipe gesture object"/>
</request>
<event name="begin">
<description summary="multi-finger swipe begin">
This event is sent when a multi-finger swipe gesture is detected
on the device.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="fingers" type="uint" summary="number of fingers"/>
</event>
<event name="update">
<description summary="multi-finger swipe motion">
This event is sent when a multi-finger swipe gesture changes the
position of the logical center.
The dx and dy coordinates are relative coordinates of the logical
center of the gesture compared to the previous event.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="delta x coordinate in surface coordinate space"/>
<arg name="dy" type="fixed" summary="delta y coordinate in surface coordinate space"/>
</event>
<event name="end">
<description summary="multi-finger swipe end">
This event is sent when a multi-finger swipe gesture ceases to
be valid. This may happen when one or more fingers are lifted or
the gesture is cancelled.
When a gesture is cancelled, the client should undo state changes
caused by this gesture. What causes a gesture to be cancelled is
implementation-dependent.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="cancelled" type="int" summary="1 if the gesture was cancelled, 0 otherwise"/>
</event>
</interface>
<interface name="zwp_pointer_gesture_pinch_v1" version="2">
<description summary="a pinch gesture object">
A pinch gesture object notifies a client about a multi-finger pinch
gesture detected on an indirect input device such as a touchpad.
The gesture is usually initiated by multiple fingers moving towards
each other or away from each other, or by two or more fingers rotating
around a logical center of gravity. The precise conditions of when
such a gesture is detected are implementation-dependent.
A gesture consists of three stages: begin, update (optional) and end.
There cannot be multiple simultaneous hold, pinch or swipe gestures on a
same pointer/seat, how compositors prevent these situations is
implementation-dependent.
A gesture may be cancelled by the compositor or the hardware.
Clients should not consider performing permanent or irreversible
actions until the end of a gesture has been received.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the pinch gesture object"/>
</request>
<event name="begin">
<description summary="multi-finger pinch begin">
This event is sent when a multi-finger pinch gesture is detected
on the device.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="fingers" type="uint" summary="number of fingers"/>
</event>
<event name="update">
<description summary="multi-finger pinch motion">
This event is sent when a multi-finger pinch gesture changes the
position of the logical center, the rotation or the relative scale.
The dx and dy coordinates are relative coordinates in the
surface coordinate space of the logical center of the gesture.
The scale factor is an absolute scale compared to the
pointer_gesture_pinch.begin event, e.g. a scale of 2 means the fingers
are now twice as far apart as on pointer_gesture_pinch.begin.
The rotation is the relative angle in degrees clockwise compared to the previous
pointer_gesture_pinch.begin or pointer_gesture_pinch.update event.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="delta x coordinate in surface coordinate space"/>
<arg name="dy" type="fixed" summary="delta y coordinate in surface coordinate space"/>
<arg name="scale" type="fixed" summary="scale relative to the initial finger position"/>
<arg name="rotation" type="fixed" summary="angle in degrees cw relative to the previous event"/>
</event>
<event name="end">
<description summary="multi-finger pinch end">
This event is sent when a multi-finger pinch gesture ceases to
be valid. This may happen when one or more fingers are lifted or
the gesture is cancelled.
When a gesture is cancelled, the client should undo state changes
caused by this gesture. What causes a gesture to be cancelled is
implementation-dependent.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="cancelled" type="int" summary="1 if the gesture was cancelled, 0 otherwise"/>
</event>
</interface>
<interface name="zwp_pointer_gesture_hold_v1" version="3">
<description summary="a hold gesture object">
A hold gesture object notifies a client about a single- or
multi-finger hold gesture detected on an indirect input device such as
a touchpad. The gesture is usually initiated by one or more fingers
being held down without significant movement. The precise conditions
of when such a gesture is detected are implementation-dependent.
In particular, this gesture may be used to cancel kinetic scrolling.
A hold gesture consists of two stages: begin and end. Unlike pinch and
swipe there is no update stage.
There cannot be multiple simultaneous hold, pinch or swipe gestures on a
same pointer/seat, how compositors prevent these situations is
implementation-dependent.
A gesture may be cancelled by the compositor or the hardware.
Clients should not consider performing permanent or irreversible
actions until the end of a gesture has been received.
</description>
<request name="destroy" type="destructor" since="3">
<description summary="destroy the hold gesture object"/>
</request>
<event name="begin" since="3">
<description summary="multi-finger hold begin">
This event is sent when a hold gesture is detected on the device.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="fingers" type="uint" summary="number of fingers"/>
</event>
<event name="end" since="3">
<description summary="multi-finger hold end">
This event is sent when a hold gesture ceases to
be valid. This may happen when the holding fingers are lifted or
the gesture is cancelled, for example if the fingers move past an
implementation-defined threshold, the finger count changes or the hold
gesture changes into a different type of gesture.
When a gesture is cancelled, the client may need to undo state changes
caused by this gesture. What causes a gesture to be cancelled is
implementation-dependent.
</description>
<arg name="serial" type="uint"/>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="cancelled" type="int" summary="1 if the gesture was cancelled, 0 otherwise"/>
</event>
</interface>
</protocol>