From 7b3f0aeab2030d4f2a0ff478856f21c2214c0f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 4 Jun 2024 11:51:09 +0200 Subject: [PATCH] iOS: Implement accelerometer support --- CMakeLists.txt | 2 +- ios/Controls.h | 4 ++++ ios/Controls.mm | 8 ++++++++ ios/ViewController.mm | 30 +++++++++++++++++++++++++++++- ios/ViewControllerMetal.mm | 29 +++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4220504f4f..021cf8b3e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1325,7 +1325,7 @@ elseif(IOS AND NOT LIBRETRO) Common/Battery/AppleBatteryClient.m ) - set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreText -framework CoreVideo -framework CoreMedia -framework CoreServices -framework Metal -framework IOSurface" ) + set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework CoreMotion -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreText -framework CoreVideo -framework CoreMedia -framework CoreServices -framework Metal -framework IOSurface" ) if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") endif() diff --git a/ios/Controls.h b/ios/Controls.h index cf4112f9d2..8c5707f28c 100644 --- a/ios/Controls.h +++ b/ios/Controls.h @@ -3,6 +3,8 @@ #include #import +#import + #include "iCade/iCadeState.h" #include "Common/Input/InputState.h" @@ -38,3 +40,5 @@ private: double lastSelectPress = 0.0f; double lastStartPress = 0.0f; }; + +void ProcessAccelerometerData(CMAccelerometerData *accData); \ No newline at end of file diff --git a/ios/Controls.mm b/ios/Controls.mm index 6fcbd90521..fc28499f5f 100644 --- a/ios/Controls.mm +++ b/ios/Controls.mm @@ -358,3 +358,11 @@ void ICadeTracker::ButtonUp(iCadeState button) { NativeKey(key); } } + +void ProcessAccelerometerData(CMAccelerometerData *accData) { + CMAcceleration acc = accData.acceleration; + // INFO_LOG(SYSTEM, "%f %f %f", acc.x, acc.y, acc.z); + + // Might need to change these for portrait or inverse landscape + NativeAccelerometer(-acc.x, -acc.y, -acc.z); +} diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 8870b99fe5..e713e5ecf3 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -109,6 +109,8 @@ id sharedViewController; //@property (nonatomic) iCadeReaderView* iCadeView; @property (nonatomic) GCController *gameController __attribute__((weak_import)); +@property (strong, nonatomic) CMMotionManager *motionManager; +@property (strong, nonatomic) NSOperationQueue *accelerometerQueue; @end @@ -124,6 +126,9 @@ id sharedViewController; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidConnect:) name:GCControllerDidConnectNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil]; } + self.accelerometerQueue = [[NSOperationQueue alloc] init]; + self.accelerometerQueue.name = @"AccelerometerQueue"; + self.accelerometerQueue.maxConcurrentOperationCount = 1; return self; } @@ -269,7 +274,9 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) { [mBackGestureRecognizer setEdges:UIRectEdgeLeft]; [[self view] addGestureRecognizer:mBackGestureRecognizer]; - INFO_LOG(G3D, "Done with viewDidLoad. Next up, OpenGL"); + // Initialize the motion manager for accelerometer control. + self.motionManager = [[CMMotionManager alloc] init]; + INFO_LOG(G3D, "Done with viewDidLoad."); } - (void)handleSwipeFrom:(UIScreenEdgePanGestureRecognizer *)recognizer @@ -291,6 +298,21 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) { - (void)didBecomeActive { INFO_LOG(SYSTEM, "didBecomeActive begin"); + if (self.motionManager.accelerometerAvailable) { + self.motionManager.accelerometerUpdateInterval = 1.0 / 60.0; + INFO_LOG(G3D, "Starting accelerometer updates."); + + [self.motionManager startAccelerometerUpdatesToQueue:self.accelerometerQueue + withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { + if (error) { + NSLog(@"Accelerometer error: %@", error); + return; + } + ProcessAccelerometerData(accelerometerData); + }]; + } else { + INFO_LOG(G3D, "No accelerometer available, not starting updates."); + } [self runGLRenderLoop]; INFO_LOG(SYSTEM, "didBecomeActive end"); } @@ -298,6 +320,12 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) { - (void)willResignActive { INFO_LOG(SYSTEM, "willResignActive begin"); [self requestExitGLRenderLoop]; + + // Stop accelerometer updates + if (self.motionManager.accelerometerActive) { + INFO_LOG(G3D, "Stopping accelerometer updates"); + [self.motionManager stopAccelerometerUpdates]; + } INFO_LOG(SYSTEM, "willResignActive end"); } diff --git a/ios/ViewControllerMetal.mm b/ios/ViewControllerMetal.mm index 2a5976cf46..8e0a138bda 100644 --- a/ios/ViewControllerMetal.mm +++ b/ios/ViewControllerMetal.mm @@ -213,6 +213,8 @@ static std::thread g_renderLoopThread; } @property (nonatomic) GCController *gameController __attribute__((weak_import)); +@property (strong, nonatomic) CMMotionManager *motionManager; +@property (strong, nonatomic) NSOperationQueue *accelerometerQueue; @end // @interface @@ -228,6 +230,9 @@ static std::thread g_renderLoopThread; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidConnect:) name:GCControllerDidConnectNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil]; } + self.accelerometerQueue = [[NSOperationQueue alloc] init]; + self.accelerometerQueue.name = @"AccelerometerQueue"; + self.accelerometerQueue.maxConcurrentOperationCount = 1; return self; } @@ -338,7 +343,21 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye // These two are forwarded from the appDelegate - (void)didBecomeActive { INFO_LOG(G3D, "didBecomeActive GL"); + if (self.motionManager.accelerometerAvailable) { + self.motionManager.accelerometerUpdateInterval = 1.0 / 60.0; + INFO_LOG(G3D, "Starting accelerometer updates."); + [self.motionManager startAccelerometerUpdatesToQueue:self.accelerometerQueue + withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { + if (error) { + NSLog(@"Accelerometer error: %@", error); + return; + } + ProcessAccelerometerData(accelerometerData); + }]; + } else { + INFO_LOG(G3D, "No accelerometer available, not starting updates."); + } // Spin up the emu thread. It will in turn spin up the Vulkan render thread // on its own. [self runVulkanRenderLoop]; @@ -347,6 +366,12 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye - (void)willResignActive { INFO_LOG(G3D, "willResignActive GL"); [self requestExitVulkanRenderLoop]; + + // Stop accelerometer updates + if (self.motionManager.accelerometerActive) { + INFO_LOG(G3D, "Stopping accelerometer updates"); + [self.motionManager stopAccelerometerUpdates]; + } } - (void)shutdown @@ -419,6 +444,9 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye UIScreenEdgePanGestureRecognizer *mBackGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:) ]; [mBackGestureRecognizer setEdges:UIRectEdgeLeft]; [[self view] addGestureRecognizer:mBackGestureRecognizer]; + + // Initialize the motion manager for accelerometer control. + self.motionManager = [[CMMotionManager alloc] init]; } // Allow device rotation to resize the swapchain @@ -444,6 +472,7 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear: animated]; + INFO_LOG(G3D, "viewWillDisappear"); } - (void)viewDidAppear:(BOOL)animated {