/* RetroArch - A frontend for libretro. * Copyright (C) 2025 - Joseph Mattiello * * RetroArch 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 Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch 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 RetroArch. * If not, see . */ #import #include "../../location_driver.h" #include "../../retroarch.h" #include "../../verbosity.h" @interface CoreLocationManager : NSObject @property (strong, nonatomic) CLLocationManager *locationManager; @property (assign) double latitude; @property (assign) double longitude; @property (assign) bool authorized; @end @implementation CoreLocationManager + (instancetype)sharedInstance { static CoreLocationManager *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[CoreLocationManager alloc] init]; }); return sharedInstance; } - (instancetype)init { self = [super init]; if (self) { _locationManager = [[CLLocationManager alloc] init]; _locationManager.delegate = self; _locationManager.desiredAccuracy = kCLLocationAccuracyBest; _authorized = false; } return self; } - (void)requestAuthorization { CLAuthorizationStatus status; if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) status = [_locationManager authorizationStatus]; else status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusNotDetermined) [_locationManager requestWhenInUseAuthorization]; else [self locationManager:_locationManager didChangeAuthorizationStatus:status]; } - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location = [locations lastObject]; self.latitude = location.coordinate.latitude; self.longitude = location.coordinate.longitude; } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { RARCH_WARN("[Location] Location manager failed with error: %s.\n", error.description.UTF8String); NSLog(@"Location manager failed with error: %@", error); } - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { #if TARGET_OS_OSX if (@available(macOS 10.12, *)) self.authorized = (status == kCLAuthorizationStatusAuthorizedAlways); #elif TARGET_OS_IPHONE if (@available(iOS 8.0, tvOS 9.0, *)) self.authorized = (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways); #endif #if !TARGET_OS_TV if (self.authorized) [_locationManager startUpdatingLocation]; #endif } @end typedef struct corelocation { CoreLocationManager *manager; } corelocation_t; static void *corelocation_init(void) { corelocation_t *corelocation = (corelocation_t*)calloc(1, sizeof(*corelocation)); if (!corelocation) return NULL; corelocation->manager = [CoreLocationManager sharedInstance]; [corelocation->manager requestAuthorization]; return corelocation; } static void corelocation_free(void *data) { corelocation_t *corelocation = (corelocation_t*)data; if (!corelocation) return; free(corelocation); } static bool corelocation_start(void *data) { corelocation_t *corelocation = (corelocation_t*)data; if (!corelocation || !corelocation->manager.authorized) return false; #if !TARGET_OS_TV [corelocation->manager.locationManager startUpdatingLocation]; #endif return true; } static void corelocation_stop(void *data) { corelocation_t *corelocation = (corelocation_t*)data; if (corelocation) [corelocation->manager.locationManager stopUpdatingLocation]; } static bool corelocation_get_position(void *data, double *lat, double *lon, double *horiz_accuracy, double *vert_accuracy) { corelocation_t *corelocation = (corelocation_t*)data; if (!corelocation || !corelocation->manager.authorized) return false; #if TARGET_OS_TV CLLocation *location = [corelocation->manager.locationManager location]; *lat = location.coordinate.latitude; *lon = location.coordinate.longitude; #else *lat = corelocation->manager.latitude; *lon = corelocation->manager.longitude; #endif *horiz_accuracy = 0.0; // CoreLocation doesn't provide this directly *vert_accuracy = 0.0; // CoreLocation doesn't provide this directly return true; } static void corelocation_set_interval(void *data, unsigned interval_ms, unsigned interval_distance) { corelocation_t *corelocation = (corelocation_t*)data; if (!corelocation) return; corelocation->manager.locationManager.distanceFilter = interval_distance; corelocation->manager.locationManager.desiredAccuracy = kCLLocationAccuracyBest; } location_driver_t location_corelocation = { corelocation_init, corelocation_free, corelocation_start, corelocation_stop, corelocation_get_position, corelocation_set_interval, "corelocation", };