From 153dec0e21530d52b8ee82f6ec588620ec0857d2 Mon Sep 17 00:00:00 2001 From: Jon Lund Steffensen Date: Wed, 16 Aug 2017 21:20:06 -0700 Subject: Change location providers to allow updates Change location provider implementations so it is possible for location providers to dynamically update the location. This commit adds the interfaces and infrastructure in redshift.c but none of the location provides are changed to become dynamic. --- src/location-corelocation.m | 124 ++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 57 deletions(-) (limited to 'src/location-corelocation.m') diff --git a/src/location-corelocation.m b/src/location-corelocation.m index 2f1768d..e33c853 100644 --- a/src/location-corelocation.m +++ b/src/location-corelocation.m @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with Redshift. If not, see . - Copyright (c) 2014 Jon Lund Steffensen + Copyright (c) 2014-2017 Jon Lund Steffensen */ #ifdef HAVE_CONFIG_H @@ -40,6 +40,7 @@ @interface Delegate : NSObject @property (strong, nonatomic) CLLocationManager *locationManager; @property (nonatomic) BOOL success; +@property (nonatomic) BOOL error; @property (nonatomic) float latitude; @property (nonatomic) float longitude; @end @@ -48,58 +49,61 @@ - (void)start { - self.locationManager = [[CLLocationManager alloc] init]; - self.locationManager.delegate = self; + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; - CLAuthorizationStatus authStatus = - [CLLocationManager authorizationStatus]; + CLAuthorizationStatus authStatus = + [CLLocationManager authorizationStatus]; - if (authStatus != kCLAuthorizationStatusNotDetermined && - authStatus != kCLAuthorizationStatusAuthorized) { - fputs(_("Not authorized to obtain location" - " from CoreLocation.\n"), stderr); - CFRunLoopStop(CFRunLoopGetCurrent()); - } + if (authStatus != kCLAuthorizationStatusNotDetermined && + authStatus != kCLAuthorizationStatusAuthorized) { + fputs(_("Not authorized to obtain location" + " from CoreLocation.\n"), stderr); + self.error = YES; + CFRunLoopStop(CFRunLoopGetCurrent()); + } - [self.locationManager startUpdatingLocation]; + [self.locationManager startUpdatingLocation]; } - (void)stop { - [self.locationManager stopUpdatingLocation]; - CFRunLoopStop(CFRunLoopGetCurrent()); + [self.locationManager stopUpdatingLocation]; + CFRunLoopStop(CFRunLoopGetCurrent()); } - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { - CLLocation *newLocation = [locations firstObject]; - self.latitude = newLocation.coordinate.latitude; - self.longitude = newLocation.coordinate.longitude; - self.success = YES; + CLLocation *newLocation = [locations firstObject]; + self.latitude = newLocation.coordinate.latitude; + self.longitude = newLocation.coordinate.longitude; + self.success = YES; - [self stop]; + [self stop]; } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { - fprintf(stderr, _("Error obtaining location from CoreLocation: %s\n"), - [[error localizedDescription] UTF8String]); - [self stop]; + fprintf(stderr, _("Error obtaining location from CoreLocation: %s\n"), + [[error localizedDescription] UTF8String]); + self.error = YES; + [self stop]; } - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { - if (status == kCLAuthorizationStatusNotDetermined) { - fputs(_("Waiting for authorization to obtain location...\n"), - stderr); - } else if (status != kCLAuthorizationStatusAuthorized) { - fputs(_("Request for location was not authorized!\n"), - stderr); - [self stop]; - } + if (status == kCLAuthorizationStatusNotDetermined) { + fputs(_("Waiting for authorization to obtain location...\n"), + stderr); + } else if (status != kCLAuthorizationStatusAuthorized) { + fputs(_("Request for location was not authorized!\n"), + stderr); + self.error = YES; + [self stop]; + } } @end @@ -108,13 +112,13 @@ int location_corelocation_init(void *state) { - return 0; + return 0; } int location_corelocation_start(void *state) { - return 0; + return 0; } void @@ -125,40 +129,46 @@ location_corelocation_free(void *state) void location_corelocation_print_help(FILE *f) { - fputs(_("Use the location as discovered by the Corelocation provider.\n"), f); - fputs("\n", f); + fputs(_("Use the location as discovered by the Corelocation provider.\n"), f); + fputs("\n", f); - fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" - "which means it has to be restarted to take notice after travel.\n"), - "CoreLocation"); - fputs("\n", f); + fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" + "which means it has to be restarted to take notice after travel.\n"), + "CoreLocation"); + fputs("\n", f); } int location_corelocation_set_option(void *state, - const char *key, const char *value) + const char *key, const char *value) { - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; } int -location_corelocation_get_location(void *state, - location_t *location) +location_corelocation_get_fd(void *state) { - int result = -1; - - @autoreleasepool { - Delegate *delegate = [[Delegate alloc] init]; - [delegate performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; - CFRunLoopRun(); - - if (delegate.success) { - location->lat = delegate.latitude; - location->lon = delegate.longitude; - result = 0; - } - } + return -1; +} - return result; +int +location_corelocation_handle(void *state, location_t *location, int *available) +{ + @autoreleasepool { + Delegate *delegate = [[Delegate alloc] init]; + [delegate performSelectorOnMainThread:@selector(start) + withObject:nil waitUntilDone:NO]; + CFRunLoopRun(); + + if (delegate.error) return -1; + + if (delegate.success) { + location->lat = delegate.latitude; + location->lon = delegate.longitude; + *available = 1; + } + } + + return 0; } -- cgit v1.2.3-70-g09d2 From df10508e30ad8a8eab43ef1e3666efa3af1f73ef Mon Sep 17 00:00:00 2001 From: Jon Lund Steffensen Date: Wed, 16 Aug 2017 22:28:38 -0700 Subject: corelocation: Update continuously --- configure.ac | 2 +- src/location-corelocation.h | 29 +++++-- src/location-corelocation.m | 201 ++++++++++++++++++++++++++++++++------------ src/redshift.c | 3 + 4 files changed, 173 insertions(+), 62 deletions(-) (limited to 'src/location-corelocation.m') diff --git a/configure.ac b/configure.ac index 40b4316..14663e2 100644 --- a/configure.ac +++ b/configure.ac @@ -257,7 +257,7 @@ AC_ARG_ENABLE([corelocation], [AC_HELP_STRING([--enable-corelocation], AS_IF([test "x$enable_corelocation" != xno], [ AS_IF([test "x$have_corelocation_h" = xyes], [ CORELOCATION_CFLAGS="" - CORELOCATION_LIBS="-framework Foundation -framework CoreLocation" + CORELOCATION_LIBS="-framework Foundation -framework Cocoa -framework CoreLocation" AC_DEFINE([ENABLE_CORELOCATION], 1, [Define to 1 to enable CoreLocation provider]) AC_MSG_RESULT([yes]) diff --git a/src/location-corelocation.h b/src/location-corelocation.h index 9c276e9..ae1feeb 100644 --- a/src/location-corelocation.h +++ b/src/location-corelocation.h @@ -24,18 +24,33 @@ #include "redshift.h" +typedef struct location_corelocation_private location_corelocation_private_t; -int location_corelocation_init(void *state); -int location_corelocation_start(void *state); -void location_corelocation_free(void *state); +typedef struct { + location_corelocation_private_t *private; + int pipe_fd_read; + int pipe_fd_write; + int available; + int error; + float latitude; + float longitude; +} location_corelocation_state_t; + + +int location_corelocation_init(location_corelocation_state_t *state); +int location_corelocation_start(location_corelocation_state_t *state); +void location_corelocation_free(location_corelocation_state_t *state); void location_corelocation_print_help(FILE *f); -int location_corelocation_set_option(void *state, - const char *key, const char *value); +int location_corelocation_set_option( + location_corelocation_state_t *state, + const char *key, const char *value); -int location_corelocation_get_fd(void *state); +int location_corelocation_get_fd( + location_corelocation_state_t *state); int location_corelocation_handle( - void *state, location_t *location, int *available); + location_corelocation_state_t *state, + location_t *location, int *available); #endif /* ! REDSHIFT_LOCATION_CORELOCATION_H */ diff --git a/src/location-corelocation.m b/src/location-corelocation.m index e33c853..5150839 100644 --- a/src/location-corelocation.m +++ b/src/location-corelocation.m @@ -25,9 +25,11 @@ #import #include "location-corelocation.h" +#include "pipeutils.h" #include "redshift.h" #include +#include #ifdef ENABLE_NLS # include @@ -37,20 +39,25 @@ #endif -@interface Delegate : NSObject +struct location_corelocation_private { + NSThread *thread; + NSLock *lock; +}; + + +@interface LocationDelegate : NSObject @property (strong, nonatomic) CLLocationManager *locationManager; -@property (nonatomic) BOOL success; -@property (nonatomic) BOOL error; -@property (nonatomic) float latitude; -@property (nonatomic) float longitude; +@property (nonatomic) location_corelocation_state_t *state; @end -@implementation Delegate; +@implementation LocationDelegate; - (void)start { self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; + self.locationManager.distanceFilter = 50000; + self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus]; @@ -59,50 +66,106 @@ authStatus != kCLAuthorizationStatusAuthorized) { fputs(_("Not authorized to obtain location" " from CoreLocation.\n"), stderr); - self.error = YES; - CFRunLoopStop(CFRunLoopGetCurrent()); + [self markError]; + } else { + [self.locationManager startUpdatingLocation]; } - - [self.locationManager startUpdatingLocation]; } -- (void)stop +- (void)markError { - [self.locationManager stopUpdatingLocation]; - CFRunLoopStop(CFRunLoopGetCurrent()); + [self.state->private->lock lock]; + + self.state->error = 1; + + [self.state->private->lock unlock]; + + pipeutils_signal(self.state->pipe_fd_write); } - (void)locationManager:(CLLocationManager *)manager - didUpdateLocations:(NSArray *)locations + didUpdateLocations:(NSArray *)locations { CLLocation *newLocation = [locations firstObject]; - self.latitude = newLocation.coordinate.latitude; - self.longitude = newLocation.coordinate.longitude; - self.success = YES; - [self stop]; + [self.state->private->lock lock]; + + self.state->latitude = newLocation.coordinate.latitude; + self.state->longitude = newLocation.coordinate.longitude; + self.state->available = 1; + + [self.state->private->lock unlock]; + + pipeutils_signal(self.state->pipe_fd_write); } - (void)locationManager:(CLLocationManager *)manager - didFailWithError:(NSError *)error + didFailWithError:(NSError *)error { fprintf(stderr, _("Error obtaining location from CoreLocation: %s\n"), [[error localizedDescription] UTF8String]); - self.error = YES; - [self stop]; + [self markError]; } - (void)locationManager:(CLLocationManager *)manager - didChangeAuthorizationStatus:(CLAuthorizationStatus)status + didChangeAuthorizationStatus:(CLAuthorizationStatus)status { if (status == kCLAuthorizationStatusNotDetermined) { - fputs(_("Waiting for authorization to obtain location...\n"), - stderr); + fputs(_("Waiting for authorization to obtain location...\n"), stderr); } else if (status != kCLAuthorizationStatusAuthorized) { - fputs(_("Request for location was not authorized!\n"), - stderr); - self.error = YES; - [self stop]; + fputs(_("Request for location was not authorized!\n"), stderr); + [self markError]; + } +} + +@end + + +// Callback when the pipe is closed. +// +// Stops the run loop causing the thread to end. +static void +pipe_close_callback( + CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) +{ + CFFileDescriptorInvalidate(fdref); + CFRelease(fdref); + + CFRunLoopStop(CFRunLoopGetCurrent()); +} + + +@interface LocationThread : NSThread +@property (nonatomic) location_corelocation_state_t *state; +@end + +@implementation LocationThread; + +// Run loop for location provider thread. +- (void)main +{ + @autoreleasepool { + LocationDelegate *locationDelegate = [[LocationDelegate alloc] init]; + locationDelegate.state = self.state; + + // Start the location delegate on the run loop in this thread. + [locationDelegate performSelector:@selector(start) + withObject:nil afterDelay:0]; + + // Create a callback that is triggered when the pipe is closed. This will + // trigger the main loop to quit and the thread to stop. + CFFileDescriptorRef fdref = CFFileDescriptorCreate( + kCFAllocatorDefault, self.state->pipe_fd_write, false, + pipe_close_callback, NULL); + CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); + CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource( + kCFAllocatorDefault, fdref, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + + // Run the loop + CFRunLoopRun(); + + close(self.state->pipe_fd_write); } } @@ -110,20 +173,56 @@ int -location_corelocation_init(void *state) +location_corelocation_init(location_corelocation_state_t *state) { return 0; } int -location_corelocation_start(void *state) +location_corelocation_start(location_corelocation_state_t *state) { + state->pipe_fd_read = -1; + state->pipe_fd_write = -1; + + state->available = 0; + state->error = 0; + state->latitude = 0; + state->longitude = 0; + + state->private = malloc(sizeof(location_corelocation_private_t)); + if (state->private == NULL) return -1; + + int pipefds[2]; + int r = pipeutils_create_nonblocking(pipefds); + if (r < 0) { + fputs(_("Failed to start CoreLocation provider!\n"), stderr); + free(state->private); + return -1; + } + + state->pipe_fd_read = pipefds[0]; + state->pipe_fd_write = pipefds[1]; + + pipeutils_signal(state->pipe_fd_write); + + state->private->lock = [[NSLock alloc] init]; + + LocationThread *thread = [[LocationThread alloc] init]; + thread.state = state; + [thread start]; + state->private->thread = thread; + return 0; } void -location_corelocation_free(void *state) +location_corelocation_free(location_corelocation_state_t *state) { + if (state->pipe_fd_read != -1) { + close(state->pipe_fd_read); + } + + free(state->private); } void @@ -131,44 +230,38 @@ location_corelocation_print_help(FILE *f) { fputs(_("Use the location as discovered by the Corelocation provider.\n"), f); fputs("\n", f); - - fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" - "which means it has to be restarted to take notice after travel.\n"), - "CoreLocation"); - fputs("\n", f); } int -location_corelocation_set_option(void *state, - const char *key, const char *value) +location_corelocation_set_option( + location_corelocation_state_t *state, const char *key, const char *value) { fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); return -1; } int -location_corelocation_get_fd(void *state) +location_corelocation_get_fd(location_corelocation_state_t *state) { - return -1; + return state->pipe_fd_read; } -int -location_corelocation_handle(void *state, location_t *location, int *available) +int location_corelocation_handle( + location_corelocation_state_t *state, + location_t *location, int *available) { - @autoreleasepool { - Delegate *delegate = [[Delegate alloc] init]; - [delegate performSelectorOnMainThread:@selector(start) - withObject:nil waitUntilDone:NO]; - CFRunLoopRun(); + pipeutils_handle_signal(state->pipe_fd_read); - if (delegate.error) return -1; + [state->private->lock lock]; - if (delegate.success) { - location->lat = delegate.latitude; - location->lon = delegate.longitude; - *available = 1; - } - } + int error = state->error; + location->lat = state->latitude; + location->lon = state->longitude; + *available = state->available; + + [state->private->lock unlock]; + + if (error) return -1; return 0; } diff --git a/src/redshift.c b/src/redshift.c index bf0ab86..e1b9593 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -206,6 +206,9 @@ static const gamma_method_t gamma_methods[] = { /* Union of state data for location providers */ typedef union { location_manual_state_t manual; +#ifdef ENABLE_CORELOCATION + location_corelocation_state_t corelocation; +#endif } location_state_t; -- cgit v1.2.3-70-g09d2