From bdc7f958fda50305744dcefdccac29373da97d01 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" 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 b0e73386c..f50b5e06e 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 4de8511a3..8f4a413e9 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 987fa2d4d..11a581b3c 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -197,6 +197,7 @@ Cocoa_VideoInit(_THIS) } 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 9691fa3f1..ec25e1f57 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 6c7e33dca..655bf05a8 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -1359,26 +1359,39 @@ Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL_Window * w 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 @@ Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL_Window * w - (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;