From bdc7f958fda50305744dcefdccac29373da97d01 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 10 Aug 2022 00:41:25 -0400
Subject: [PATCH] cocoa: Added hint to treat MacBook trackpads as touch
devices, not mice.
Fixes #5511.
---
include/SDL_hints.h | 22 ++++++++++++
src/video/cocoa/SDL_cocoavideo.h | 1 +
src/video/cocoa/SDL_cocoavideo.m | 1 +
src/video/cocoa/SDL_cocoawindow.h | 1 +
src/video/cocoa/SDL_cocoawindow.m | 59 +++++++++++++++----------------
5 files changed, 54 insertions(+), 30 deletions(-)
diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index b0e73386ca0..f50b5e06e8c 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -2207,6 +2207,28 @@ extern "C" {
#define SDL_HINT_KMSDRM_DEVICE_INDEX "SDL_KMSDRM_DEVICE_INDEX"
+/**
+ * \brief A variable that treats trackpads as touch devices.
+ *
+ * On macOS (and possibly other platforms in the future), SDL will report
+ * touches on a trackpad as mouse input, which is generally what users
+ * expect from this device; however, these are often actually full
+ * multitouch-capable touch devices, so it might be preferable to some apps
+ * to treat them as such.
+ *
+ * Setting this hint to true will make the trackpad input report as a
+ * multitouch device instead of a mouse. The default is false.
+ *
+ * Note that most platforms don't support this hint. As of 2.24.0, it
+ * only supports MacBooks' trackpads on macOS. Others may follow later.
+ *
+ * This hint is checked during SDL_Init and can not be changed after.
+ *
+ * This hint is available since SDL 2.24.0.
+ */
+#define SDL_HINT_TRACKPAD_IS_TOUCH_ONLY "SDL_TRACKPAD_IS_TOUCH_ONLY"
+
+
/**
* \brief An enumeration of hint priorities
*/
diff --git a/src/video/cocoa/SDL_cocoavideo.h b/src/video/cocoa/SDL_cocoavideo.h
index 4de8511a351..8f4a413e9ef 100644
--- a/src/video/cocoa/SDL_cocoavideo.h
+++ b/src/video/cocoa/SDL_cocoavideo.h
@@ -99,6 +99,7 @@ DECLARE_ALERT_STYLE(Critical);
@interface SDL_VideoData : NSObject
@property (nonatomic) int allow_spaces;
+ @property (nonatomic) int trackpad_is_touch_only;
@property (nonatomic) unsigned int modifierFlags;
@property (nonatomic) void *key_layout;
@property (nonatomic) SDLTranslatorResponder *fieldEdit;
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index 987fa2d4dd0..11a581b3c89 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -197,6 +197,7 @@ @implementation SDL_VideoData
}
data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE);
+ data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, SDL_FALSE);
data.swaplock = SDL_CreateMutex();
if (!data.swaplock) {
diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h
index 9691fa3f17c..ec25e1f5795 100644
--- a/src/video/cocoa/SDL_cocoawindow.h
+++ b/src/video/cocoa/SDL_cocoawindow.h
@@ -56,6 +56,7 @@ typedef enum
BOOL isDragAreaRunning;
}
+-(BOOL) isTouchFromTrackpad:(NSEvent *)theEvent;
-(void) listen:(SDL_WindowData *) data;
-(void) pauseVisibleObservation;
-(void) resumeVisibleObservation;
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 6c7e33dcac1..655bf05a8f2 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1359,26 +1359,39 @@ - (void)scrollWheel:(NSEvent *)theEvent
Cocoa_HandleMouseWheel(_data.window, theEvent);
}
+
+- (BOOL)isTouchFromTrackpad:(NSEvent *)theEvent
+{
+ SDL_Window *window = _data.window;
+ SDL_VideoData *videodata = ((__bridge SDL_WindowData *) window->driverdata).videodata;
+
+ /* if this a MacBook trackpad, we'll make input look like a synthesized
+ event. This is backwards from reality, but better matches user
+ expectations. You can make it look like a generic touch device instead
+ with the SDL_HINT_TRACKPAD_IS_TOUCH_ONLY hint. */
+ BOOL istrackpad = NO;
+ if (!videodata.trackpad_is_touch_only) {
+ @try {
+ istrackpad = ([theEvent subtype] == NSEventSubtypeMouseEvent);
+ }
+ @catch (NSException *e) {
+ /* if NSEvent type doesn't have subtype, such as NSEventTypeBeginGesture on
+ * macOS 10.5 to 10.10, then NSInternalInconsistencyException is thrown.
+ * This still prints a message to terminal so catching it's not an ideal solution.
+ *
+ * *** Assertion failure in -[NSEvent subtype]
+ */
+ }
+ }
+ return istrackpad;
+}
+
- (void)touchesBeganWithEvent:(NSEvent *) theEvent
{
NSSet *touches;
SDL_TouchID touchID;
int existingTouchCount;
-
- /* probably a MacBook trackpad; make this look like a synthesized event.
- This is backwards from reality, but better matches user expectations. */
- BOOL istrackpad = NO;
- @try {
- istrackpad = ([theEvent subtype] == NSEventSubtypeMouseEvent);
- }
- @catch (NSException *e) {
- /* if NSEvent type doesn't have subtype, such as NSEventTypeBeginGesture on
- * macOS 10.5 to 10.10, then NSInternalInconsistencyException is thrown.
- * This still prints a message to terminal so catching it's not an ideal solution.
- *
- * *** Assertion failure in -[NSEvent subtype]
- */
- }
+ const BOOL istrackpad = [self isTouchFromTrackpad:theEvent];
touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil];
touchID = istrackpad ? SDL_MOUSE_TOUCHID : (SDL_TouchID)(intptr_t)[[touches anyObject] device];
@@ -1426,24 +1439,10 @@ - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
- (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
{
NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
+ const BOOL istrackpad = [self isTouchFromTrackpad:theEvent];
SDL_FingerID fingerId;
float x, y;
- /* probably a MacBook trackpad; make this look like a synthesized event.
- This is backwards from reality, but better matches user expectations. */
- BOOL istrackpad = NO;
- @try {
- istrackpad = ([theEvent subtype] == NSEventSubtypeMouseEvent);
- }
- @catch (NSException *e) {
- /* if NSEvent type doesn't have subtype, such as NSEventTypeBeginGesture on
- * macOS 10.5 to 10.10, then NSInternalInconsistencyException is thrown.
- * This still prints a message to terminal so catching it's not an ideal solution.
- *
- * *** Assertion failure in -[NSEvent subtype]
- */
- }
-
for (NSTouch *touch in touches) {
const SDL_TouchID touchId = istrackpad ? SDL_MOUSE_TOUCHID : (SDL_TouchID)(intptr_t)[touch device];
SDL_TouchDeviceType devtype = SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE;