Skip to content

Instantly share code, notes, and snippets.

@CodaFi
Last active August 29, 2015 14:03
Show Gist options
  • Save CodaFi/81f6b38e8ab684e0c099 to your computer and use it in GitHub Desktop.
Save CodaFi/81f6b38e8ab684e0c099 to your computer and use it in GitHub Desktop.
Accelerometer data from the sudden motion sensor.
//
// NUIAccelerometer.h
// NUIKit
//
// Created by Robert Widmann on 7/5/14.
// Copyright (c) 2014 CodaFi. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <NUIKit/NUIKitDefines.h>
/// A structure that contains the acceleration values for all 3 axes.
typedef struct NUIAccelerationData {
double x, y, z;
} NUIAccelerationData;
/// Defines acceleration data where all of the values are 0.
NUIKIT_EXTERN const NUIAccelerationData NUIAccelerationDataZero;
/// The NUIAccelerometer class lets you register to receive acceleration-related data from the
/// onboard hardware. As a device moves, its hardware reports linear acceleration changes along the
/// primary axes in three-dimensional space. You can use this data to detect both the current
/// orientation of the device (relative to the ground) and any instantaneous changes to that
/// orientation. You might use instantaneous changes as input to a game or to initiate some action
/// in your application.
///
/// You do not create accelerometer data directly. Instead, you use the shared NUIAccelerometer
/// object to specify the interval at which you want to receive events, a callback queue, and a
/// callback handler.
///
/// For some Macintosh models (notably desktops), a Sudden Motion Sensor may not be available. In
/// addition, laptops and desktops that contain SSDs often lack sudden motion sensors. In that case
/// any attempts to initialize an NUIAccelerometer object will fail and return nil.
@interface NUIAccelerometer : NSObject {
@private
dispatch_queue_t _queue;
dispatch_source_t _timer;
struct {
unsigned int started:1;
} _accelerometerFlags;
}
/// Returns the shared accelerometer object.
///
/// If the system does not support an accelerometer, or one is not provided, this method returns nil.
+ (NUIAccelerometer *)sharedAccelerometer;
/// The interval at which to deliver acceleration data to the delegate.
///
/// This property is measured in seconds. While the value of this property is not capped at any
/// lower or upper bound, it is important to specify a value that is not too frequent, as repeatedly
/// polling the hardware can be taxing on battery life.
@property (nonatomic) NSTimeInterval updateInterval;
/// A Boolean value that indicates whether an accelerometer is available on the device.
@property (nonatomic, readonly, getter=isAccelerometerAvailable) BOOL accelerometerAvailable;
/// A Boolean value that indicates whether accelerometer updates are currently happening.
///
/// This property indicates whether -startAccelerometerUpdatesToQueue:withHandler: or
/// -startAccelerometerUpdates has been called since the last time stopAccelerometerUpdates was
/// called.
@property (nonatomic, readonly, getter=isAccelerometerActive) BOOL accelerometerActive;
/// The latest sample of accelerometer data.
///
/// If no accelerometer data is available, the value of this property is NUIAccelerationDataZero.
/// An app that is receiving accelerometer data after calling -startAccelerometerUpdates
/// periodically checks the value of this property and processes the acceleration data.
@property (nonatomic, readonly) NUIAccelerationData accelerometerData;
/// Starts accelerometer updates without a handler.
///
/// Invoking this method is the same as providing a nil queue and nil handler to
/// -startAccelerometerUpdatesToQueue:withHandler:.
- (void)startAccelerometerUpdates;
/// Starts accelerometer updates on an operation queue and with a specified handler.
///
/// If no queue is provided, an internal background queue will be created. If no handler block is
/// provided, the accelerometerData property will continue to update until accelerometer updates
/// are stopped.
- (void)startAccelerometerUpdatesToQueue:(dispatch_queue_t)queue withHandler:(void (^)(NUIAccelerationData accelerometerData, NSTimeInterval timestamp))handler;
/// Stops accelerometer updates.
- (void)stopAccelerometerUpdates;
@end
//
// NUIAccelerometer.m
// NUIKit
//
// Created by Robert Widmann on 7/5/14.
// Copyright (c) 2014 CodaFi. All rights reserved.
//
#import "NUIAccelerometer.h"
#import <IOKit/IOKitLib.h>
const NUIAccelerationData NUIAccelerationDataZero = (NUIAccelerationData){0};
static mach_port_t NUIAccelerometerSensorPort;
@interface NUIAccelerometer ()
@property (nonatomic) NUIAccelerationData accelerometerData;
@end
@implementation NUIAccelerometer
+ (void)initialize {
if (self.class != NUIAccelerometer.class) {
return;
}
}
+ (NUIAccelerometer *)sharedAccelerometer {
static NUIAccelerometer *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[NUIAccelerometer alloc] initOnce];
});
return instance;
}
- (instancetype)init {
NSAssert(0, @"Manually initializing an accelerometer is forbidden. Use +[NUIAccelerometer sharedAccelerometer] instead.");
return nil;
}
- (instancetype)initOnce {
self = [super init];
if (self.isAccelerometerAvailable) {
return self;
}
NUI_RELEASE_MRCONLY(self);
return nil;
}
- (void)dealloc {
[self stopAccelerometerUpdates];
NUI_DEALLOC_MRCONLY;
}
- (void)startAccelerometerUpdates {
[self startAccelerometerUpdatesToQueue:NULL withHandler:NULL];
}
- (void)startAccelerometerUpdatesToQueue:(dispatch_queue_t)queue withHandler:(void (^)(NUIAccelerationData, NSTimeInterval))handler {
if (_accelerometerFlags.started || self.updateInterval <= 0.0) {
return;
}
_accelerometerFlags.started = true;
[self stopAccelerometerUpdates];
if (queue != nil && handler != NULL) {
dispatch_retain(queue);
_queue = queue;
} else {
_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_PRIORITY_DEFAULT);
}
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
if (handler == NULL) {
NUI_WEAKIFY(self);
handler = ^(NUIAccelerationData data, NSTimeInterval _) {
NUI_STRONGIFY(self);
self.accelerometerData = data;
};
}
NUI_WEAKIFY(self);
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, self.updateInterval * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
dispatch_source_set_event_handler(_timer, ^{
NUI_STRONGIFY(self);
NUIAccelerationData inputStructure, outputStructure;
size_t inSize = sizeof(NUIAccelerationData), outSize = sizeof(NUIAccelerationData);
int result = IOConnectCallStructMethod(NUIDefaultAccelerometerSensorPort(), 5, &inputStructure, inSize, &outputStructure, &outSize);
if(result != KERN_SUCCESS) {
return;
}
self.accelerometerData = outputStructure;
handler(outputStructure, CACurrentMediaTime());
});
}
- (void)stopAccelerometerUpdates {
if (_timer != NULL) {
dispatch_source_cancel(_timer);
dispatch_release(_timer);
_timer = NULL;
}
if (_queue) {
dispatch_release(_queue);
_queue = NULL;
}
_accelerometerFlags.started = false;
}
- (void)setUpdateInterval:(NSTimeInterval)updateInterval {
if (!_accelerometerFlags.started) {
_updateInterval = updateInterval;
}
}
- (BOOL)isAccelerometerAvailable {
return NUIDefaultAccelerometerSensorPort() != 0;
}
- (BOOL)isAccelerometerActive {
return _accelerometerFlags.started;
}
static mach_port_t NUIDefaultAccelerometerSensorPort() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mach_port_t dataPort;
io_iterator_t iterator;
io_object_t aDevice;
int r;
r = IOMasterPort(MACH_PORT_NULL, &dataPort);
if (r != KERN_SUCCESS) {
NSCAssert(0, @"NUIAccelerometer Internal Error: Could not grab the master port.");
return;
}
CFMutableDictionaryRef servicesMatching = IOServiceMatching("SMCMotionSensor");
r = IOServiceGetMatchingServices(kIOMasterPortDefault, servicesMatching, &iterator);
if (r != KERN_SUCCESS) {
return;
}
aDevice = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if(aDevice == 0) {
return;
}
r = IOServiceOpen(aDevice, mach_task_self(), 0, &dataPort);
IOObjectRelease(aDevice);
if(r != KERN_SUCCESS) {
NSCAssert(0, @"NUIAccelerometer Internal Error: Could not open connection to accelerometer.");
return;
}
NUIAccelerometerSensorPort = dataPort;
});
return NUIAccelerometerSensorPort;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p; update interval %.4g;", self.class, self, self.updateInterval];
}
@end
//
// NUIKitDefines.h
// NUIKit
//
// Created by Robert Widmann on 7/7/13.
// Copyright (c) 2013 CodaFi. All rights reserved.
// Released under the MIT license.
//
#import <Availability.h>
#ifdef __cplusplus
#define NUIKIT_EXTERN extern "C"
#define NUIKIT_PRIVATE_EXTERN __attribute__((visibility("hidden"))) extern "C"
#define NUIKIT_PRIVATE __attribute__((visibility("hidden")))
#else
#define NUIKIT_EXTERN extern
#define NUIKIT_PRIVATE_EXTERN __attribute__((visibility("hidden"))) extern
#define NUIKIT_PRIVATE __attribute__((visibility("hidden")))
#endif
#define NUIKIT_STATIC_INLINE static inline
#ifndef NUI_RELEASE_MRCONLY
#if __has_feature(objc_arc)
#define NUI_RELEASE_MRCONLY(x) x = nil
#else
#define NUI_RELEASE_MRCONLY(x) [x release], x = nil
#endif
#endif
#ifndef NUI_AUTORELEASE_MRCONLY
#if __has_feature(objc_arc)
#define NUI_AUTORELEASE_MRCONLY(x)
#else
#define NUI_AUTORELEASE_MRCONLY(x) [x autorelease]
#endif
#endif
#ifndef NUI_RETAIN_MRCONLY
#if __has_feature(objc_arc)
#define NUI_RETAIN_MRCONLY(x) x
#else
#define NUI_RETAIN_MRCONLY(x) [x retain]
#endif
#endif
#ifndef NUI_BRIDGE
#if __has_feature(objc_arc)
#define NUI_BRIDGE(x) (__bridge x)
#else
#define NUI_BRIDGE(x) (x)
#endif
#endif
#ifndef NUI_DEALLOC_MRCONLY
#if __has_feature(objc_arc)
#define NUI_DEALLOC_MRCONLY
#else
#define NUI_DEALLOC_MRCONLY [super dealloc]
#endif
#endif
#ifndef NUI_SAFE_ATOMIC_RETVAL
#if __has_feature(objc_arc)
#define NUI_SAFE_ATOMIC_RETVAL(x) x
#else
#define NUI_SAFE_ATOMIC_RETVAL(x) [[x retain]autorelease]
#endif
#endif
#ifndef NUI_WEAKIFY
#if __has_feature(objc_arc)
#define NUI_WEAKIFY(x) __weak __typeof__(x) _nui_weakSelf = x
#else
#define NUI_WEAKIFY(x) __block __typeof__(x) _nui_weakSelf = x
#endif
#endif
#ifndef NUI_STRONGIFY
#if __has_feature(objc_arc)
#define NUI_STRONGIFY(x) __strong __typeof__(x) x = _nui_weakSelf
#else
#define NUI_STRONGIFY(x) __typeof__(x) x = _nui_weakSelf
#endif
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment