aboutsummaryrefslogtreecommitdiffstats
path: root/src/location-corelocation.m
diff options
context:
space:
mode:
authorJon Lund Steffensen <jonlst@gmail.com>2017-08-19 11:56:11 -0700
committerGitHub <noreply@github.com>2017-08-19 11:56:11 -0700
commit71e1de2d33f0c9d57da82f1291ed728b8dbff09f (patch)
tree0e76ea1834bc423e4376bc8e112d9c85213ed91f /src/location-corelocation.m
parentMerge pull request #498 from jonls/osx-travis (diff)
parentAppveyor, Travis: Test run redshift after build (diff)
downloadredshift-ng-71e1de2d33f0c9d57da82f1291ed728b8dbff09f.tar.gz
redshift-ng-71e1de2d33f0c9d57da82f1291ed728b8dbff09f.tar.bz2
redshift-ng-71e1de2d33f0c9d57da82f1291ed728b8dbff09f.tar.xz
Merge pull request #500 from jonls/continuous-location
Continuous location updates
Diffstat (limited to 'src/location-corelocation.m')
-rw-r--r--src/location-corelocation.m245
1 files changed, 174 insertions, 71 deletions
diff --git a/src/location-corelocation.m b/src/location-corelocation.m
index 2f1768d..5150839 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 <http://www.gnu.org/licenses/>.
- Copyright (c) 2014 Jon Lund Steffensen <jonlst@gmail.com>
+ Copyright (c) 2014-2017 Jon Lund Steffensen <jonlst@gmail.com>
*/
#ifdef HAVE_CONFIG_H
@@ -25,9 +25,11 @@
#import <CoreLocation/CoreLocation.h>
#include "location-corelocation.h"
+#include "pipeutils.h"
#include "redshift.h"
#include <stdio.h>
+#include <unistd.h>
#ifdef ENABLE_NLS
# include <libintl.h>
@@ -37,128 +39,229 @@
#endif
-@interface Delegate : NSObject <CLLocationManagerDelegate>
+struct location_corelocation_private {
+ NSThread *thread;
+ NSLock *lock;
+};
+
+
+@interface LocationDelegate : NSObject <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
-@property (nonatomic) BOOL success;
-@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 = [[CLLocationManager alloc] init];
+ self.locationManager.delegate = self;
+ self.locationManager.distanceFilter = 50000;
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
+
+ CLAuthorizationStatus authStatus =
+ [CLLocationManager authorizationStatus];
+
+ if (authStatus != kCLAuthorizationStatusNotDetermined &&
+ authStatus != kCLAuthorizationStatusAuthorized) {
+ fputs(_("Not authorized to obtain location"
+ " from CoreLocation.\n"), stderr);
+ [self markError];
+ } else {
+ [self.locationManager startUpdatingLocation];
+ }
+}
- CLAuthorizationStatus authStatus =
- [CLLocationManager authorizationStatus];
+- (void)markError
+{
+ [self.state->private->lock lock];
- if (authStatus != kCLAuthorizationStatusNotDetermined &&
- authStatus != kCLAuthorizationStatusAuthorized) {
- fputs(_("Not authorized to obtain location"
- " from CoreLocation.\n"), stderr);
- CFRunLoopStop(CFRunLoopGetCurrent());
- }
+ self.state->error = 1;
- [self.locationManager startUpdatingLocation];
-}
+ [self.state->private->lock unlock];
-- (void)stop
-{
- [self.locationManager stopUpdatingLocation];
- CFRunLoopStop(CFRunLoopGetCurrent());
+ 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;
+ CLLocation *newLocation = [locations firstObject];
+
+ [self.state->private->lock lock];
+
+ self.state->latitude = newLocation.coordinate.latitude;
+ self.state->longitude = newLocation.coordinate.longitude;
+ self.state->available = 1;
- [self stop];
+ [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 stop];
+ fprintf(stderr, _("Error obtaining location from CoreLocation: %s\n"),
+ [[error localizedDescription] UTF8String]);
+ [self markError];
}
- (void)locationManager:(CLLocationManager *)manager
- didChangeAuthorizationStatus:(CLAuthorizationStatus)status
+ 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 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
{
- 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];
- }
+ @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);
+ }
}
@end
int
-location_corelocation_init(void *state)
+location_corelocation_init(location_corelocation_state_t *state)
{
- return 0;
+ return 0;
}
int
-location_corelocation_start(void *state)
+location_corelocation_start(location_corelocation_state_t *state)
{
- return 0;
+ 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
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);
+ fputs(_("Use the location as discovered by the Corelocation provider.\n"), f);
+ 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;
+ fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key);
+ return -1;
}
int
-location_corelocation_get_location(void *state,
- location_t *location)
+location_corelocation_get_fd(location_corelocation_state_t *state)
{
- int result = -1;
+ return state->pipe_fd_read;
+}
+
+int location_corelocation_handle(
+ location_corelocation_state_t *state,
+ location_t *location, int *available)
+{
+ pipeutils_handle_signal(state->pipe_fd_read);
+
+ [state->private->lock lock];
+
+ int error = state->error;
+ location->lat = state->latitude;
+ location->lon = state->longitude;
+ *available = state->available;
- @autoreleasepool {
- Delegate *delegate = [[Delegate alloc] init];
- [delegate performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
- CFRunLoopRun();
+ [state->private->lock unlock];
- if (delegate.success) {
- location->lat = delegate.latitude;
- location->lon = delegate.longitude;
- result = 0;
- }
- }
+ if (error) return -1;
- return result;
+ return 0;
}