dingusppc/main_sdl.m
Mihai Parparita 587eb48f61 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 9dd3c4bef8/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.
2023-12-15 15:38:32 -08:00

98 lines
3.7 KiB
Objective-C

/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <Cocoa/Cocoa.h>
#include <SDL.h>
@interface DPPCResponder : NSResponder
@end
static DPPCResponder *dppcResponder;
/** @file SDL-specific main function additions for macOS hosts. */
// Replace Command shortcuts with Control shortcuts in the menu bar, so that
// they're not going to conflict with ones in the guest OS (but we still allow
// some of them to be used in the host OS, e.g. Control+Q to quit).
void remap_appkit_menu_shortcuts(void) {
for (NSMenuItem *menuItem in [NSApp mainMenu].itemArray) {
if (menuItem.hasSubmenu) {
for (NSMenuItem *item in menuItem.submenu.itemArray) {
if (item.keyEquivalentModifierMask & NSEventModifierFlagCommand) {
item.keyEquivalentModifierMask &= ~NSEventModifierFlagCommand;
item.keyEquivalentModifierMask |= NSEventModifierFlagControl;
}
}
}
}
// 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