From 587eb48f613583236bd5c14d3e2158521bdee05f Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Fri, 15 Dec 2023 15:38:32 -0800 Subject: [PATCH] Add a basic mouse grab mode when running on macOS Adds a "Grab Mouse" command to the "Window" menu which toggles SDL_SetRelativeMouseMode. That "traps" the mouse to the current window (and hides it) which makes it less annoying to deal with the separate acceleration curves of the host and guest OS. This needs to be done via AppKit code since SDL does not have a cross-platform way to add menu commands. I also looked into the extended ADB mouse protocol but it does not appear to do that we want. If we use it to report a device class of classAbsolute (0) the acceleration curve changes to a flat response (we end up using 'accl' 0 from https://github.com/elliotnunn/supermario/blob/9dd3c4bef84df2ea30f5ec2c5e97b043e8267b3f/base/SuperMarioProj.1994-02-09/Resources/MiscROMRsrcs.r#L64-L76) but the coordinates are still interpreted as being deltas. If I use a device type 4 (Absolute pointing devices) then the mouse position is never queried. Per https://68kmla.org/bb/index.php?threads/anyone-have-an-adb-graphics-tablet-or-joystick.39128/ all tablet devices end up using custom drivers. --- main_sdl.m | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/main_sdl.m b/main_sdl.m index db6daf9..93d6606 100644 --- a/main_sdl.m +++ b/main_sdl.m @@ -20,6 +20,12 @@ along with this program. If not, see . */ #include +#include + +@interface DPPCResponder : NSResponder +@end + +static DPPCResponder *dppcResponder; /** @file SDL-specific main function additions for macOS hosts. */ @@ -37,4 +43,55 @@ void remap_appkit_menu_shortcuts(void) { } } } + + // Add a "Grab Mouse" commamand to the Window menu. + NSMenuItem *windowMenuItem = [[NSApp mainMenu] itemWithTitle:@"Window"]; + if (windowMenuItem) { + NSMenu *windowMenu = [windowMenuItem submenu]; + if (windowMenu) { + NSMenuItem *grabMouseMenuItem = [[NSMenuItem alloc] initWithTitle:@"Grab Mouse" + action:@selector(grabMouse:) + keyEquivalent:@"g"]; + [grabMouseMenuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl]; + [windowMenu addItem:grabMouseMenuItem]; + } + } + + // Insert a responder in the chain to handle the command (SDL does not + // expose its NSAppDelegate implementation so we can't subclass it). + dppcResponder = [DPPCResponder new]; + [dppcResponder setNextResponder:[NSApp nextResponder]]; + [NSApp setNextResponder:dppcResponder]; } + +@implementation DPPCResponder + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + if ([menuItem action] == @selector(grabMouse:)) { + [menuItem setState:SDL_GetRelativeMouseMode() ? NSControlStateValueOn : NSControlStateValueOff]; + return YES; + } + return [super validateMenuItem:menuItem]; +} + +- (void)grabMouse:(id)sender { + NSWindow *mainWindow = NSApp.mainWindow; + if (mainWindow) { + // If we're about to grab the mouse, move the mouse to the middle of the + // window so that SDL knows that this window is what we want coordinates + // to be relative to. + if (!SDL_GetRelativeMouseMode()) { + NSRect frame = mainWindow.frame; + NSPoint center = NSMakePoint(frame.size.width/2, frame.size.height/2); + center = [mainWindow convertPointToScreen:center]; + CGWarpMouseCursorPosition(center); + mainWindow.subtitle = @"Mouse grabbed"; + } else { + mainWindow.subtitle = @""; + } + } + + SDL_SetRelativeMouseMode(!SDL_GetRelativeMouseMode()); +} + +@end