mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 733650 - Use smslib as the device orientation backend on the mac. r=jdm
This commit is contained in:
parent
d89616f66d
commit
498d78622c
@ -52,6 +52,7 @@ EXPORT_LIBRARY = 1
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
CMMSRCS = \
|
||||
smslib.mm \
|
||||
nsDeviceMotionSystem.mm \
|
||||
$(NULL)
|
||||
|
||||
|
@ -42,8 +42,8 @@
|
||||
#include <sys/resource.h>
|
||||
#include <sys/vm.h>
|
||||
|
||||
#define MODEL_NAME_LENGTH 64
|
||||
static char gModelName[MODEL_NAME_LENGTH];
|
||||
#import "smslib.h"
|
||||
#define MEAN_GRAVITY 9.80665
|
||||
|
||||
nsDeviceMotionSystem::nsDeviceMotionSystem()
|
||||
{
|
||||
@ -53,24 +53,6 @@ nsDeviceMotionSystem::~nsDeviceMotionSystem()
|
||||
{
|
||||
}
|
||||
|
||||
// Data format returned from IOConnectMethodStructureIStructureO.
|
||||
// I am not sure what the other bits in this structure are,
|
||||
// or if there are any, but this has to be 40 bytes long or
|
||||
// the call to read fails.
|
||||
//
|
||||
// Since we make the SmsData struct larger than any members we plan to access we
|
||||
// keep track of the the size of the part of the struct we plan to access for
|
||||
// use in bounds checking.
|
||||
#define SMSDATA_PADDING_SIZE 34
|
||||
typedef struct
|
||||
{
|
||||
PRInt16 x;
|
||||
PRInt16 y;
|
||||
PRInt16 z;
|
||||
PRInt8 unknown[SMSDATA_PADDING_SIZE];
|
||||
} SmsData;
|
||||
#define SMSDATA_USED_SIZE (sizeof(SmsData) - SMSDATA_PADDING_SIZE)
|
||||
|
||||
void
|
||||
nsDeviceMotionSystem::UpdateHandler(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
@ -79,112 +61,19 @@ nsDeviceMotionSystem::UpdateHandler(nsITimer *aTimer, void *aClosure)
|
||||
NS_ERROR("no self");
|
||||
return;
|
||||
}
|
||||
sms_acceleration accel;
|
||||
smsGetData(&accel);
|
||||
|
||||
size_t bufferLen = sizeof(SmsData);
|
||||
|
||||
void * input = malloc(bufferLen);
|
||||
void * output = malloc(bufferLen);
|
||||
|
||||
if (!input || !output)
|
||||
return;
|
||||
|
||||
memset(input, 0, bufferLen);
|
||||
memset(output, 0, bufferLen);
|
||||
|
||||
size_t structureOutputSize = bufferLen;
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4)
|
||||
kern_return_t result = ::IOConnectMethodStructureIStructureO(self->mSmsConnection,
|
||||
5, /* Magic number for SMCMotionSensor */
|
||||
bufferLen,
|
||||
(IOByteCount*)&structureOutputSize,
|
||||
input,
|
||||
output);
|
||||
#else
|
||||
kern_return_t result = ::IOConnectCallStructMethod((mach_port_t)self->mSmsConnection,
|
||||
5, /* Magic number for SMCMotionSensor */
|
||||
input,
|
||||
bufferLen,
|
||||
output,
|
||||
&structureOutputSize);
|
||||
#endif
|
||||
|
||||
if ((result != kIOReturnSuccess) || (structureOutputSize < SMSDATA_USED_SIZE)) {
|
||||
free(input);
|
||||
free(output);
|
||||
return;
|
||||
}
|
||||
|
||||
SmsData *data = (SmsData*) output;
|
||||
|
||||
float xf, yf, zf;
|
||||
|
||||
// we want to normalize the return result from the chip to
|
||||
// something between -1 and 1 where 0 is the balance point.
|
||||
|
||||
const int normalizeFactor = 250.5;
|
||||
|
||||
if (!strcmp(gModelName, "MacBookPro5,1")) {
|
||||
xf = ((float)data->x) / normalizeFactor;
|
||||
yf = (((float)data->y) / normalizeFactor) * -1;
|
||||
zf = ((float)data->z) / normalizeFactor;
|
||||
}
|
||||
else if (!strcmp(gModelName, "MacBookPro5,3")) {
|
||||
xf = ((float)data->y) / normalizeFactor;
|
||||
yf = (((float)data->x) / normalizeFactor) * -1;
|
||||
zf = (((float)data->z) / normalizeFactor) * -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xf = (((float)data->x) / normalizeFactor) * -1;
|
||||
yf = ((float)data->y) / normalizeFactor;
|
||||
zf = ((float)data->z) / normalizeFactor;
|
||||
}
|
||||
|
||||
free(input);
|
||||
free(output);
|
||||
|
||||
self->DeviceMotionChanged(nsIDeviceMotionData::TYPE_ACCELERATION, xf, yf, zf );
|
||||
self->DeviceMotionChanged(nsIDeviceMotionData::TYPE_ACCELERATION,
|
||||
accel.x * MEAN_GRAVITY,
|
||||
accel.y * MEAN_GRAVITY,
|
||||
accel.z * MEAN_GRAVITY);
|
||||
}
|
||||
|
||||
void nsDeviceMotionSystem::Startup()
|
||||
{
|
||||
// we can fail, and that just means the caller will not see any changes.
|
||||
|
||||
mach_port_t port;
|
||||
kern_return_t result = ::IOMasterPort(MACH_PORT_NULL, &port);
|
||||
if (result != kIOReturnSuccess)
|
||||
return;
|
||||
|
||||
CFMutableDictionaryRef dict = ::IOServiceMatching("SMCMotionSensor");
|
||||
if (!dict)
|
||||
return;
|
||||
|
||||
io_iterator_t iter;
|
||||
result = ::IOServiceGetMatchingServices(port, dict, &iter);
|
||||
if (result != kIOReturnSuccess)
|
||||
return;
|
||||
|
||||
io_object_t device = ::IOIteratorNext(iter);
|
||||
|
||||
::IOObjectRelease(iter);
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
result = ::IOServiceOpen(device, mach_task_self(), 0, &mSmsConnection);
|
||||
::IOObjectRelease(device);
|
||||
|
||||
if (result != kIOReturnSuccess)
|
||||
return;
|
||||
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
|
||||
/* get the version of the hardware we are running on. */
|
||||
int mib[2];
|
||||
size_t len = MODEL_NAME_LENGTH;
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_MODEL;
|
||||
sysctl(mib, 2, gModelName, &len, NULL, 0);
|
||||
smsStartup(nil, nil);
|
||||
smsLoadCalibration();
|
||||
|
||||
mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (mUpdateTimer)
|
||||
@ -196,12 +85,11 @@ void nsDeviceMotionSystem::Startup()
|
||||
|
||||
void nsDeviceMotionSystem::Shutdown()
|
||||
{
|
||||
if (mSmsConnection)
|
||||
::IOServiceClose(mSmsConnection);
|
||||
|
||||
if (mUpdateTimer) {
|
||||
mUpdateTimer->Cancel();
|
||||
mUpdateTimer = nsnull;
|
||||
}
|
||||
|
||||
smsShutdown();
|
||||
}
|
||||
|
||||
|
159
dom/system/cocoa/smslib.h
Normal file
159
dom/system/cocoa/smslib.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* smslib.h
|
||||
*
|
||||
* SMSLib Sudden Motion Sensor Access Library
|
||||
* Copyright (c) 2010 Suitable Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by: Daniel Griscom
|
||||
* Suitable Systems
|
||||
* http://www.suitable.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal with the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the names of Suitable Systems nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this Software without specific prior written permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
*
|
||||
* For more information about SMSLib, see
|
||||
* <http://www.suitable.com/tools/smslib.html>
|
||||
* or contact
|
||||
* Daniel Griscom
|
||||
* Suitable Systems
|
||||
* 1 Centre Street, Suite 204
|
||||
* Wakefield, MA 01880
|
||||
* (781) 665-0053
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define SMSLIB_VERSION "1.8"
|
||||
|
||||
#pragma mark Structure definitions
|
||||
|
||||
// Structure for specifying a 3-axis acceleration. 0.0 means "zero gravities",
|
||||
// 1.0 means "one gravity".
|
||||
typedef struct sms_acceleration {
|
||||
float x; // Right-left acceleration (positive is rightwards)
|
||||
float y; // Front-rear acceleration (positive is rearwards)
|
||||
float z; // Up-down acceleration (positive is upwards)
|
||||
} sms_acceleration;
|
||||
|
||||
// Structure for specifying a calibration.
|
||||
typedef struct sms_calibration {
|
||||
float zeros[3]; // Zero points for three axes (X, Y, Z)
|
||||
float onegs[3]; // One gravity values for three axes
|
||||
} sms_calibration;
|
||||
|
||||
#pragma mark Return value definitions
|
||||
|
||||
// These are the return values for accelStartup(), giving the
|
||||
// various stages where the most successful attempt at accessing
|
||||
// the accelerometer failed. The higher the value, the further along the
|
||||
// software progressed before failing. The options are:
|
||||
// - Didn't match model name
|
||||
#define SMS_FAIL_MODEL (-7)
|
||||
// - Failure getting dictionary matching desired services
|
||||
#define SMS_FAIL_DICTIONARY (-6)
|
||||
// - Failure getting list of services
|
||||
#define SMS_FAIL_LIST_SERVICES (-5)
|
||||
// - Failure if list of services is empty. The process generally fails
|
||||
// here if run on a machine without a Sudden Motion Sensor.
|
||||
#define SMS_FAIL_NO_SERVICES (-4)
|
||||
// - Failure if error opening device.
|
||||
#define SMS_FAIL_OPENING (-3)
|
||||
// - Failure if opened, but didn't get a connection
|
||||
#define SMS_FAIL_CONNECTION (-2)
|
||||
// - Failure if couldn't access connction using given function and size. This
|
||||
// is where the process would probably fail with a change in Apple's API.
|
||||
// Driver problems often also cause failures here.
|
||||
#define SMS_FAIL_ACCESS (-1)
|
||||
// - Success!
|
||||
#define SMS_SUCCESS (0)
|
||||
|
||||
#pragma mark Function declarations
|
||||
|
||||
// This starts up the accelerometer code, trying each possible sensor
|
||||
// specification. Note that for logging purposes it
|
||||
// takes an object and a selector; the object's selector is then invoked
|
||||
// with a single NSString as argument giving progress messages. Example
|
||||
// logging method:
|
||||
// - (void)logMessage: (NSString *)theString
|
||||
// which would be used in accelStartup's invocation thusly:
|
||||
// result = accelStartup(self, @selector(logMessage:));
|
||||
// If the object is nil, then no logging is done. Sets calibation from built-in
|
||||
// value table. Returns ACCEL_SUCCESS for success, and other (negative)
|
||||
// values for various failures (returns value indicating result of
|
||||
// most successful trial).
|
||||
int smsStartup(id logObject, SEL logSelector);
|
||||
|
||||
// This starts up the library in debug mode, ignoring the actual hardware.
|
||||
// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
|
||||
// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
|
||||
// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
|
||||
// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
|
||||
// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
|
||||
int smsDebugStartup(id logObject, SEL logSelector);
|
||||
|
||||
// Returns the current calibration values.
|
||||
void smsGetCalibration(sms_calibration *calibrationRecord);
|
||||
|
||||
// Sets the calibration, but does NOT store it as a preference. If the argument
|
||||
// is nil then the current calibration is set from the built-in value table.
|
||||
void smsSetCalibration(sms_calibration *calibrationRecord);
|
||||
|
||||
// Stores the current calibration values as a stored preference.
|
||||
void smsStoreCalibration(void);
|
||||
|
||||
// Loads the stored preference values into the current calibration.
|
||||
// Returns YES if successful.
|
||||
BOOL smsLoadCalibration(void);
|
||||
|
||||
// Deletes any stored calibration, and then takes the current calibration values
|
||||
// from the built-in value table.
|
||||
void smsDeleteCalibration(void);
|
||||
|
||||
// Fills in the accel record with calibrated acceleration data. Takes
|
||||
// 1-2ms to return a value. Returns 0 if success, error number if failure.
|
||||
int smsGetData(sms_acceleration *accel);
|
||||
|
||||
// Fills in the accel record with uncalibrated acceleration data.
|
||||
// Returns 0 if success, error number if failure.
|
||||
int smsGetUncalibratedData(sms_acceleration *accel);
|
||||
|
||||
// Returns the length of a raw block of data for the current type of sensor.
|
||||
int smsGetBufferLength(void);
|
||||
|
||||
// Takes a pointer to accelGetRawLength() bytes; sets those bytes
|
||||
// to return value from sensor. Make darn sure the buffer length is right!
|
||||
void smsGetBufferData(char *buffer);
|
||||
|
||||
// This returns an NSString describing the current calibration in
|
||||
// human-readable form. Also include a description of the machine.
|
||||
NSString *smsGetCalibrationDescription(void);
|
||||
|
||||
// Shuts down the accelerometer.
|
||||
void smsShutdown(void);
|
||||
|
937
dom/system/cocoa/smslib.mm
Normal file
937
dom/system/cocoa/smslib.mm
Normal file
@ -0,0 +1,937 @@
|
||||
/*
|
||||
* smslib.m
|
||||
*
|
||||
* SMSLib Sudden Motion Sensor Access Library
|
||||
* Copyright (c) 2010 Suitable Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by: Daniel Griscom
|
||||
* Suitable Systems
|
||||
* http://www.suitable.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal with the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the names of Suitable Systems nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this Software without specific prior written permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
*
|
||||
* For more information about SMSLib, see
|
||||
* <http://www.suitable.com/tools/smslib.html>
|
||||
* or contact
|
||||
* Daniel Griscom
|
||||
* Suitable Systems
|
||||
* 1 Centre Street, Suite 204
|
||||
* Wakefield, MA 01880
|
||||
* (781) 665-0053
|
||||
*
|
||||
*/
|
||||
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <math.h>
|
||||
#import "smslib.h"
|
||||
|
||||
#pragma mark Internal structures
|
||||
|
||||
// Represents a single axis of a type of sensor.
|
||||
typedef struct axisStruct {
|
||||
int enabled; // Non-zero if axis is valid in this sensor
|
||||
int index; // Location in struct of first byte
|
||||
int size; // Number of bytes
|
||||
float zerog; // Value meaning "zero g"
|
||||
float oneg; // Change in value meaning "increase of one g"
|
||||
// (can be negative if axis sensor reversed)
|
||||
} axisStruct;
|
||||
|
||||
// Represents the configuration of a type of sensor.
|
||||
typedef struct sensorSpec {
|
||||
char *model; // Prefix of model to be tested
|
||||
char *name; // Name of device to be read
|
||||
unsigned int function; // Kernel function index
|
||||
int recordSize; // Size of record to be sent/received
|
||||
axisStruct axes[3]; // Description of three axes (X, Y, Z)
|
||||
} sensorSpec;
|
||||
|
||||
// Configuration of all known types of sensors. The configurations are
|
||||
// tried in order until one succeeds in returning data.
|
||||
// All default values are set here, but each axis' zerog and oneg values
|
||||
// may be changed to saved (calibrated) values.
|
||||
//
|
||||
// These values came from SeisMaCalibrate calibration reports. In general I've
|
||||
// found the following:
|
||||
// - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs
|
||||
// are different (and in one case two axes are swapped)
|
||||
// - PowerBooks and iBooks all have sensors centered on 0, and reading
|
||||
// 50-53 steps per gravity (but with differing polarities!)
|
||||
// - PowerBooks and iBooks of the same model all have the same axis polarities
|
||||
// - PowerBook and iBook access methods are model- and OS version-specific
|
||||
//
|
||||
// So, the sequence of tests is:
|
||||
// - Try model-specific access methods. Note that the test is for a match to the
|
||||
// beginning of the model name, e.g. the record with model name "MacBook"
|
||||
// matches computer models "MacBookPro1,2" and "MacBook1,1" (and ""
|
||||
// matches any model).
|
||||
// - If no model-specific record's access fails, then try each model-independent
|
||||
// access method in order, stopping when one works.
|
||||
static const sensorSpec sensors[] = {
|
||||
// ****** Model-dependent methods ******
|
||||
// The PowerBook5,6 is one of the G4 models that seems to lose
|
||||
// SMS access until the next reboot.
|
||||
{"PowerBook5,6", "IOI2CMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, -51.5},
|
||||
{1, 2, 1, 0, -51.5}
|
||||
}
|
||||
},
|
||||
// The PowerBook5,7 is one of the G4 models that seems to lose
|
||||
// SMS access until the next reboot.
|
||||
{"PowerBook5,7", "IOI2CMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, 51.5}
|
||||
}
|
||||
},
|
||||
// Access seems to be reliable on the PowerBook5,8
|
||||
{"PowerBook5,8", "PMUMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, -51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, -51.5}
|
||||
}
|
||||
},
|
||||
// Access seems to be reliable on the PowerBook5,9
|
||||
{"PowerBook5,9", "PMUMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, -51.5},
|
||||
{1, 2, 1, 0, -51.5}
|
||||
}
|
||||
},
|
||||
// The PowerBook6,7 is one of the G4 models that seems to lose
|
||||
// SMS access until the next reboot.
|
||||
{"PowerBook6,7", "IOI2CMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, 51.5}
|
||||
}
|
||||
},
|
||||
// The PowerBook6,8 is one of the G4 models that seems to lose
|
||||
// SMS access until the next reboot.
|
||||
{"PowerBook6,8", "IOI2CMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, 51.5}
|
||||
}
|
||||
},
|
||||
// MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes.
|
||||
{"MacBookPro2,1", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, 251},
|
||||
{1, 2, 2, 0, -251},
|
||||
{1, 4, 2, 0, -251}
|
||||
}
|
||||
},
|
||||
// MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07.
|
||||
// NOTE! The 17" machines have the signs of their X and Y axes reversed
|
||||
// from this calibration, but there's no clear way to discriminate between
|
||||
// the two machines.
|
||||
{"MacBookPro3,1", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 2, 2, 0, 251},
|
||||
{1, 4, 2, 0, -251}
|
||||
}
|
||||
},
|
||||
// ... specs?
|
||||
{"MacBook5,2", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 2, 2, 0, 251},
|
||||
{1, 4, 2, 0, -251}
|
||||
}
|
||||
},
|
||||
// ... specs?
|
||||
{"MacBookPro5,1", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 2, 2, 0, -251},
|
||||
{1, 4, 2, 0, 251}
|
||||
}
|
||||
},
|
||||
// ... specs?
|
||||
{"MacBookPro5,2", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 2, 2, 0, -251},
|
||||
{1, 4, 2, 0, 251}
|
||||
}
|
||||
},
|
||||
// This is speculative, based on a single user's report. Looks like the X and Y axes
|
||||
// are swapped. This is true for no other known Appple laptop.
|
||||
{"MacBookPro5,3", "SMCMotionSensor", 5, 40, {
|
||||
{1, 2, 2, 0, -251},
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 4, 2, 0, -251}
|
||||
}
|
||||
},
|
||||
// ... specs?
|
||||
{"MacBookPro5,4", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, -251},
|
||||
{1, 2, 2, 0, -251},
|
||||
{1, 4, 2, 0, 251}
|
||||
}
|
||||
},
|
||||
// ****** Model-independent methods ******
|
||||
// Seen once with PowerBook6,8 under system 10.3.9; I suspect
|
||||
// other G4-based 10.3.* systems might use this
|
||||
{"", "IOI2CMotionSensor", 24, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, 51.5}
|
||||
}
|
||||
},
|
||||
// PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8
|
||||
// under OS X 10.4.*
|
||||
{"", "IOI2CMotionSensor", 21, 60, {
|
||||
{1, 0, 1, 0, 51.5},
|
||||
{1, 1, 1, 0, 51.5},
|
||||
{1, 2, 1, 0, 51.5}
|
||||
}
|
||||
},
|
||||
// PowerBook5,8 , PowerBook5,9 under OS X 10.4.*
|
||||
{"", "PMUMotionSensor", 21, 60, {
|
||||
// Each has two out of three gains negative, but it's different
|
||||
// for the different models. So, this will be right in two out
|
||||
// of three axis for either model.
|
||||
{1, 0, 1, 0, -51.5},
|
||||
{1, 1, 1, -6, -51.5},
|
||||
{1, 2, 1, 0, -51.5}
|
||||
}
|
||||
},
|
||||
// All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15")
|
||||
// is Kionix KXM52-1050 three-axis accelerometer chip. Data is at
|
||||
// http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models
|
||||
// that use this are:
|
||||
// MacBook1,1
|
||||
// MacBook2,1
|
||||
// MacBook3,1
|
||||
// MacBook4,1
|
||||
// MacBook5,1
|
||||
// MacBook6,1
|
||||
// MacBookAir1,1
|
||||
// MacBookPro1,1
|
||||
// MacBookPro1,2
|
||||
// MacBookPro4,1
|
||||
// MacBookPro5,5
|
||||
{"", "SMCMotionSensor", 5, 40, {
|
||||
{1, 0, 2, 0, 251},
|
||||
{1, 2, 2, 0, 251},
|
||||
{1, 4, 2, 0, 251}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define SENSOR_COUNT (sizeof(sensors)/sizeof(sensorSpec))
|
||||
|
||||
#pragma mark Internal prototypes
|
||||
|
||||
static int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector);
|
||||
static float getAxis(int which, int calibrated);
|
||||
static int signExtend(int value, int size);
|
||||
static NSString *getModelName(void);
|
||||
static NSString *getOSVersion(void);
|
||||
static BOOL loadCalibration(void);
|
||||
static void storeCalibration(void);
|
||||
static void defaultCalibration(void);
|
||||
static void deleteCalibration(void);
|
||||
static int prefIntRead(NSString *prefName, BOOL *success);
|
||||
static void prefIntWrite(NSString *prefName, int prefValue);
|
||||
static float prefFloatRead(NSString *prefName, BOOL *success);
|
||||
static void prefFloatWrite(NSString *prefName, float prefValue);
|
||||
static void prefDelete(NSString *prefName);
|
||||
static void prefSynchronize(void);
|
||||
// static long getMicroseconds(void);
|
||||
float fakeData(NSTimeInterval time);
|
||||
|
||||
#pragma mark Static variables
|
||||
|
||||
static int debugging = NO; // True if debugging (synthetic data)
|
||||
static io_connect_t connection; // Connection for reading accel values
|
||||
static int running = NO; // True if we successfully started
|
||||
static int sensorNum = 0; // The current index into sensors[]
|
||||
static char *serviceName; // The name of the current service
|
||||
static char *iRecord, *oRecord; // Pointers to read/write records for sensor
|
||||
static int recordSize; // Size of read/write records
|
||||
static unsigned int function; // Which kernel function should be used
|
||||
static float zeros[3]; // X, Y and Z zero calibration values
|
||||
static float onegs[3]; // X, Y and Z one-g calibration values
|
||||
|
||||
#pragma mark Defines
|
||||
|
||||
// Pattern for building axis letter from axis number
|
||||
#define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z")
|
||||
// Name of configuration for given axis' zero (axis specified by integer)
|
||||
#define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)]
|
||||
// Name of configuration for given axis' oneg (axis specified by integer)
|
||||
#define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)]
|
||||
// Name of "Is calibrated" preference
|
||||
#define CALIBRATED_NAME (@"Calibrated")
|
||||
// Application domain for SeisMac library
|
||||
#define APP_ID ((CFStringRef)@"com.suitable.SeisMacLib")
|
||||
|
||||
// These #defines make the accelStartup code a LOT easier to read.
|
||||
#define LOG(message) \
|
||||
if (logObject) { \
|
||||
[logObject performSelector:logSelector withObject:message]; \
|
||||
}
|
||||
#define LOG_ARG(format, var1) \
|
||||
if (logObject) { \
|
||||
[logObject performSelector:logSelector \
|
||||
withObject:[NSString stringWithFormat:format, var1]]; \
|
||||
}
|
||||
#define LOG_2ARG(format, var1, var2) \
|
||||
if (logObject) { \
|
||||
[logObject performSelector:logSelector \
|
||||
withObject:[NSString stringWithFormat:format, var1, var2]]; \
|
||||
}
|
||||
#define LOG_3ARG(format, var1, var2, var3) \
|
||||
if (logObject) { \
|
||||
[logObject performSelector:logSelector \
|
||||
withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \
|
||||
}
|
||||
|
||||
#pragma mark Function definitions
|
||||
|
||||
// This starts up the accelerometer code, trying each possible sensor
|
||||
// specification. Note that for logging purposes it
|
||||
// takes an object and a selector; the object's selector is then invoked
|
||||
// with a single NSString as argument giving progress messages. Example
|
||||
// logging method:
|
||||
// - (void)logMessage: (NSString *)theString
|
||||
// which would be used in accelStartup's invocation thusly:
|
||||
// result = accelStartup(self, @selector(logMessage:));
|
||||
// If the object is nil, then no logging is done. Sets calibation from built-in
|
||||
// value table. Returns ACCEL_SUCCESS for success, and other (negative)
|
||||
// values for various failures (returns value indicating result of
|
||||
// most successful trial).
|
||||
int smsStartup(id logObject, SEL logSelector) {
|
||||
io_iterator_t iterator;
|
||||
io_object_t device;
|
||||
kern_return_t result;
|
||||
sms_acceleration accel;
|
||||
int failure_result = SMS_FAIL_MODEL;
|
||||
|
||||
running = NO;
|
||||
debugging = NO;
|
||||
|
||||
NSString *modelName = getModelName();
|
||||
|
||||
LOG_ARG(@"Machine model: %@\n", modelName);
|
||||
LOG_ARG(@"OS X version: %@\n", getOSVersion());
|
||||
LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION);
|
||||
|
||||
for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) {
|
||||
|
||||
// Set up all specs for this type of sensor
|
||||
serviceName = sensors[sensorNum].name;
|
||||
recordSize = sensors[sensorNum].recordSize;
|
||||
function = sensors[sensorNum].function;
|
||||
|
||||
LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n",
|
||||
serviceName, function, recordSize);
|
||||
|
||||
NSString *targetName = [NSString stringWithCString:sensors[sensorNum].model
|
||||
encoding:NSMacOSRomanStringEncoding];
|
||||
LOG_ARG(@" Comparing model name to target \"%@\": ", targetName);
|
||||
if ([targetName length] == 0 || [modelName hasPrefix:targetName]) {
|
||||
LOG(@"success.\n");
|
||||
} else {
|
||||
LOG(@"failure.\n");
|
||||
// Don't need to increment failure_result.
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(@" Fetching dictionary for service: ");
|
||||
CFMutableDictionaryRef dict = IOServiceMatching(serviceName);
|
||||
|
||||
if (dict) {
|
||||
LOG(@"success.\n");
|
||||
} else {
|
||||
LOG(@"failure.\n");
|
||||
if (failure_result < SMS_FAIL_DICTIONARY) {
|
||||
failure_result = SMS_FAIL_DICTIONARY;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(@" Getting list of matching services: ");
|
||||
result = IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||
dict,
|
||||
&iterator);
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
LOG(@"success.\n");
|
||||
} else {
|
||||
LOG_ARG(@"failure, with return value 0x%x.\n", result);
|
||||
if (failure_result < SMS_FAIL_LIST_SERVICES) {
|
||||
failure_result = SMS_FAIL_LIST_SERVICES;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(@" Getting first device in list: ");
|
||||
device = IOIteratorNext(iterator);
|
||||
|
||||
if (device == 0) {
|
||||
LOG(@"failure.\n");
|
||||
if (failure_result < SMS_FAIL_NO_SERVICES) {
|
||||
failure_result = SMS_FAIL_NO_SERVICES;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
LOG(@"success.\n");
|
||||
LOG(@" Opening device: ");
|
||||
}
|
||||
|
||||
result = IOServiceOpen(device, mach_task_self(), 0, &connection);
|
||||
|
||||
if (result != KERN_SUCCESS) {
|
||||
LOG_ARG(@"failure, with return value 0x%x.\n", result);
|
||||
IOObjectRelease(device);
|
||||
if (failure_result < SMS_FAIL_OPENING) {
|
||||
failure_result = SMS_FAIL_OPENING;
|
||||
}
|
||||
continue;
|
||||
} else if (connection == 0) {
|
||||
LOG_ARG(@"'success', but didn't get a connection.\n", result);
|
||||
IOObjectRelease(device);
|
||||
if (failure_result < SMS_FAIL_CONNECTION) {
|
||||
failure_result = SMS_FAIL_CONNECTION;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
IOObjectRelease(device);
|
||||
LOG(@"success.\n");
|
||||
}
|
||||
LOG(@" Testing device.\n");
|
||||
|
||||
defaultCalibration();
|
||||
|
||||
iRecord = (char*) malloc(recordSize);
|
||||
oRecord = (char*) malloc(recordSize);
|
||||
|
||||
running = YES;
|
||||
result = getData(&accel, true, logObject, logSelector);
|
||||
running = NO;
|
||||
|
||||
if (result) {
|
||||
LOG_ARG(@" Failure testing device, with result 0x%x.\n", result);
|
||||
free(iRecord);
|
||||
iRecord = 0;
|
||||
free(oRecord);
|
||||
oRecord = 0;
|
||||
if (failure_result < SMS_FAIL_ACCESS) {
|
||||
failure_result = SMS_FAIL_ACCESS;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
LOG(@" Success testing device!\n");
|
||||
running = YES;
|
||||
return SMS_SUCCESS;
|
||||
}
|
||||
}
|
||||
return failure_result;
|
||||
}
|
||||
|
||||
// This starts up the library in debug mode, ignoring the actual hardware.
|
||||
// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
|
||||
// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
|
||||
// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
|
||||
// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
|
||||
// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
|
||||
int smsDebugStartup(id logObject, SEL logSelector) {
|
||||
LOG(@"Starting up in debug mode\n");
|
||||
debugging = YES;
|
||||
return SMS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns the current calibration values.
|
||||
void smsGetCalibration(sms_calibration *calibrationRecord) {
|
||||
int x;
|
||||
|
||||
for (x = 0; x < 3; x++) {
|
||||
calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]);
|
||||
calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the calibration, but does NOT store it as a preference. If the argument
|
||||
// is nil then the current calibration is set from the built-in value table.
|
||||
void smsSetCalibration(sms_calibration *calibrationRecord) {
|
||||
int x;
|
||||
|
||||
if (!debugging) {
|
||||
if (calibrationRecord) {
|
||||
for (x = 0; x < 3; x++) {
|
||||
zeros[x] = calibrationRecord->zeros[x];
|
||||
onegs[x] = calibrationRecord->onegs[x];
|
||||
}
|
||||
} else {
|
||||
defaultCalibration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stores the current calibration values as a stored preference.
|
||||
void smsStoreCalibration(void) {
|
||||
if (!debugging)
|
||||
storeCalibration();
|
||||
}
|
||||
|
||||
// Loads the stored preference values into the current calibration.
|
||||
// Returns YES if successful.
|
||||
BOOL smsLoadCalibration(void) {
|
||||
if (debugging) {
|
||||
return YES;
|
||||
} else if (loadCalibration()) {
|
||||
return YES;
|
||||
} else {
|
||||
defaultCalibration();
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes any stored calibration, and then takes the current calibration values
|
||||
// from the built-in value table.
|
||||
void smsDeleteCalibration(void) {
|
||||
if (!debugging) {
|
||||
deleteCalibration();
|
||||
defaultCalibration();
|
||||
}
|
||||
}
|
||||
|
||||
// Fills in the accel record with calibrated acceleration data. Takes
|
||||
// 1-2ms to return a value. Returns 0 if success, error number if failure.
|
||||
int smsGetData(sms_acceleration *accel) {
|
||||
NSTimeInterval time;
|
||||
if (debugging) {
|
||||
usleep(1500); // Usually takes 1-2 milliseconds
|
||||
time = [NSDate timeIntervalSinceReferenceDate];
|
||||
accel->x = fakeData(time)/5;
|
||||
accel->y = fakeData(time - 1)/5;
|
||||
accel->z = fakeData(time - 2)/5 + 1.0;
|
||||
return true;
|
||||
} else {
|
||||
return getData(accel, true, nil, nil);
|
||||
}
|
||||
}
|
||||
|
||||
// Fills in the accel record with uncalibrated acceleration data.
|
||||
// Returns 0 if success, error number if failure.
|
||||
int smsGetUncalibratedData(sms_acceleration *accel) {
|
||||
NSTimeInterval time;
|
||||
if (debugging) {
|
||||
usleep(1500); // Usually takes 1-2 milliseconds
|
||||
time = [NSDate timeIntervalSinceReferenceDate];
|
||||
accel->x = fakeData(time) * 256 / 5;
|
||||
accel->y = fakeData(time - 1) * 256 / 5;
|
||||
accel->z = fakeData(time - 2) * 256 / 5 + 256;
|
||||
return true;
|
||||
} else {
|
||||
return getData(accel, false, nil, nil);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the length of a raw block of data for the current type of sensor.
|
||||
int smsGetBufferLength(void) {
|
||||
if (debugging) {
|
||||
return 0;
|
||||
} else if (running) {
|
||||
return sensors[sensorNum].recordSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a pointer to accelGetRawLength() bytes; sets those bytes
|
||||
// to return value from sensor. Make darn sure the buffer length is right!
|
||||
void smsGetBufferData(char *buffer) {
|
||||
IOItemCount iSize = recordSize;
|
||||
IOByteCount oSize = recordSize;
|
||||
kern_return_t result;
|
||||
|
||||
if (debugging || running == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(iRecord, 1, iSize);
|
||||
memset(buffer, 0, oSize);
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
||||
const size_t InStructSize = recordSize;
|
||||
size_t OutStructSize = recordSize;
|
||||
result = IOConnectCallStructMethod(connection,
|
||||
function, // magic kernel function number
|
||||
(const void *)iRecord,
|
||||
InStructSize,
|
||||
(void *)buffer,
|
||||
&OutStructSize
|
||||
);
|
||||
#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
|
||||
result = IOConnectMethodStructureIStructureO(connection,
|
||||
function, // magic kernel function number
|
||||
iSize,
|
||||
&oSize,
|
||||
iRecord,
|
||||
buffer
|
||||
);
|
||||
#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
|
||||
|
||||
if (result != KERN_SUCCESS) {
|
||||
running = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// This returns an NSString describing the current calibration in
|
||||
// human-readable form. Also include a description of the machine.
|
||||
NSString *smsGetCalibrationDescription(void) {
|
||||
BOOL success;
|
||||
NSMutableString *s = [[NSMutableString alloc] init];
|
||||
|
||||
if (debugging) {
|
||||
[s release];
|
||||
return @"Debugging!";
|
||||
}
|
||||
|
||||
[s appendString:@"---- SeisMac Calibration Record ----\n \n"];
|
||||
[s appendFormat:@"Machine model: %@\n",
|
||||
getModelName()];
|
||||
[s appendFormat:@"OS X build: %@\n",
|
||||
getOSVersion()];
|
||||
[s appendFormat:@"SeisMacLib version %s, record %d\n \n",
|
||||
SMSLIB_VERSION, sensorNum];
|
||||
[s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n",
|
||||
serviceName, function, recordSize];
|
||||
if (prefIntRead(CALIBRATED_NAME, &success) && success) {
|
||||
[s appendString:@"Calibration values (from calibration):\n"];
|
||||
} else {
|
||||
[s appendString:@"Calibration values (from defaults):\n"];
|
||||
}
|
||||
[s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]];
|
||||
[s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]];
|
||||
[s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]];
|
||||
[s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]];
|
||||
[s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]];
|
||||
[s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]];
|
||||
[s appendString:@"---- End Record ----\n"];
|
||||
return s;
|
||||
}
|
||||
|
||||
// Shuts down the accelerometer.
|
||||
void smsShutdown(void) {
|
||||
if (!debugging) {
|
||||
running = NO;
|
||||
if (iRecord) free(iRecord);
|
||||
if (oRecord) free(oRecord);
|
||||
IOServiceClose(connection);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Internal functions
|
||||
|
||||
// Loads the current calibration from the stored preferences.
|
||||
// Returns true iff successful.
|
||||
BOOL loadCalibration(void) {
|
||||
BOOL thisSuccess, allSuccess;
|
||||
int x;
|
||||
|
||||
prefSynchronize();
|
||||
|
||||
if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) {
|
||||
// Calibrated. Set all values from saved values.
|
||||
allSuccess = YES;
|
||||
for (x = 0; x < 3; x++) {
|
||||
zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess);
|
||||
allSuccess &= thisSuccess;
|
||||
onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess);
|
||||
allSuccess &= thisSuccess;
|
||||
}
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Stores the current calibration into the stored preferences.
|
||||
static void storeCalibration(void) {
|
||||
int x;
|
||||
prefIntWrite(CALIBRATED_NAME, 1);
|
||||
for (x = 0; x < 3; x++) {
|
||||
prefFloatWrite(ZERO_NAME(x), zeros[x]);
|
||||
prefFloatWrite(ONEG_NAME(x), onegs[x]);
|
||||
}
|
||||
prefSynchronize();
|
||||
}
|
||||
|
||||
|
||||
// Sets the calibration to its default values.
|
||||
void defaultCalibration(void) {
|
||||
int x;
|
||||
for (x = 0; x < 3; x++) {
|
||||
zeros[x] = sensors[sensorNum].axes[x].zerog;
|
||||
onegs[x] = sensors[sensorNum].axes[x].oneg;
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes the stored preferences.
|
||||
static void deleteCalibration(void) {
|
||||
int x;
|
||||
|
||||
prefDelete(CALIBRATED_NAME);
|
||||
for (x = 0; x < 3; x++) {
|
||||
prefDelete(ZERO_NAME(x));
|
||||
prefDelete(ONEG_NAME(x));
|
||||
}
|
||||
prefSynchronize();
|
||||
}
|
||||
|
||||
// Read a named floating point value from the stored preferences. Sets
|
||||
// the success boolean based on, you guessed it, whether it succeeds.
|
||||
static float prefFloatRead(NSString *prefName, BOOL *success) {
|
||||
float result = 0.0f;
|
||||
|
||||
CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName,
|
||||
APP_ID);
|
||||
// If there isn't such a preference, fail
|
||||
if (ref == NULL) {
|
||||
*success = NO;
|
||||
return result;
|
||||
}
|
||||
CFTypeID typeID = CFGetTypeID(ref);
|
||||
// Is it a number?
|
||||
if (typeID == CFNumberGetTypeID()) {
|
||||
// Is it a floating point number?
|
||||
if (CFNumberIsFloatType((CFNumberRef)ref)) {
|
||||
// Yup: grab it.
|
||||
*success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result);
|
||||
} else {
|
||||
// Nope: grab as an integer, and convert to a float.
|
||||
long num;
|
||||
if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) {
|
||||
result = num;
|
||||
*success = YES;
|
||||
} else {
|
||||
*success = NO;
|
||||
}
|
||||
}
|
||||
// Or is it a string (e.g. set by the command line "defaults" command)?
|
||||
} else if (typeID == CFStringGetTypeID()) {
|
||||
result = (float)CFStringGetDoubleValue((CFStringRef)ref);
|
||||
*success = YES;
|
||||
} else {
|
||||
// Can't convert to a number: fail.
|
||||
*success = NO;
|
||||
}
|
||||
CFRelease(ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Writes a named floating point value to the stored preferences.
|
||||
static void prefFloatWrite(NSString *prefName, float prefValue) {
|
||||
CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault,
|
||||
kCFNumberFloatType,
|
||||
&prefValue);
|
||||
CFPreferencesSetAppValue((CFStringRef)prefName,
|
||||
cfFloat,
|
||||
APP_ID);
|
||||
CFRelease(cfFloat);
|
||||
}
|
||||
|
||||
// Reads a named integer value from the stored preferences.
|
||||
static int prefIntRead(NSString *prefName, BOOL *success) {
|
||||
Boolean internalSuccess;
|
||||
CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName,
|
||||
APP_ID,
|
||||
&internalSuccess);
|
||||
*success = internalSuccess;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Writes a named integer value to the stored preferences.
|
||||
static void prefIntWrite(NSString *prefName, int prefValue) {
|
||||
CFPreferencesSetAppValue((CFStringRef)prefName,
|
||||
(CFNumberRef)[NSNumber numberWithInt:prefValue],
|
||||
APP_ID);
|
||||
}
|
||||
|
||||
// Deletes the named preference values.
|
||||
static void prefDelete(NSString *prefName) {
|
||||
CFPreferencesSetAppValue((CFStringRef)prefName,
|
||||
NULL,
|
||||
APP_ID);
|
||||
}
|
||||
|
||||
// Synchronizes the local preferences with the stored preferences.
|
||||
static void prefSynchronize(void) {
|
||||
CFPreferencesAppSynchronize(APP_ID);
|
||||
}
|
||||
|
||||
// Internal version of accelGetData, with logging
|
||||
int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector) {
|
||||
IOItemCount iSize = recordSize;
|
||||
IOByteCount oSize = recordSize;
|
||||
kern_return_t result;
|
||||
|
||||
if (running == NO) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(iRecord, 1, iSize);
|
||||
memset(oRecord, 0, oSize);
|
||||
|
||||
LOG_2ARG(@" Querying device: ",
|
||||
sensors[sensorNum].function, sensors[sensorNum].recordSize);
|
||||
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
||||
const size_t InStructSize = recordSize;
|
||||
size_t OutStructSize = recordSize;
|
||||
result = IOConnectCallStructMethod(connection,
|
||||
function, // magic kernel function number
|
||||
(const void *)iRecord,
|
||||
InStructSize,
|
||||
(void *)oRecord,
|
||||
&OutStructSize
|
||||
);
|
||||
#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
|
||||
result = IOConnectMethodStructureIStructureO(connection,
|
||||
function, // magic kernel function number
|
||||
iSize,
|
||||
&oSize,
|
||||
iRecord,
|
||||
oRecord
|
||||
);
|
||||
#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
|
||||
|
||||
if (result != KERN_SUCCESS) {
|
||||
LOG(@"failed.\n");
|
||||
running = NO;
|
||||
return result;
|
||||
} else {
|
||||
LOG(@"succeeded.\n");
|
||||
|
||||
accel->x = getAxis(0, calibrated);
|
||||
accel->y = getAxis(1, calibrated);
|
||||
accel->z = getAxis(2, calibrated);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Given the returned record, extracts the value of the given axis. If
|
||||
// calibrated, then zero G is 0.0, and one G is 1.0.
|
||||
float getAxis(int which, int calibrated) {
|
||||
// Get various values (to make code cleaner)
|
||||
int indx = sensors[sensorNum].axes[which].index;
|
||||
int size = sensors[sensorNum].axes[which].size;
|
||||
float zerog = zeros[which];
|
||||
float oneg = onegs[which];
|
||||
// Storage for value to be returned
|
||||
int value = 0;
|
||||
|
||||
// Although the values in the returned record should have the proper
|
||||
// endianness, we still have to get it into the proper end of value.
|
||||
#if (BYTE_ORDER == BIG_ENDIAN)
|
||||
// On PowerPC processors
|
||||
memcpy(((char *)&value) + (sizeof(int) - size), &oRecord[indx], size);
|
||||
#endif
|
||||
#if (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
// On Intel processors
|
||||
memcpy(&value, &oRecord[indx], size);
|
||||
#endif
|
||||
|
||||
value = signExtend(value, size);
|
||||
|
||||
if (calibrated) {
|
||||
// Scale and shift for zero.
|
||||
return ((float)(value - zerog)) / oneg;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Extends the sign, given the length of the value.
|
||||
int signExtend(int value, int size) {
|
||||
// Extend sign
|
||||
switch (size) {
|
||||
case 1:
|
||||
if (value & 0x00000080)
|
||||
value |= 0xffffff00;
|
||||
break;
|
||||
case 2:
|
||||
if (value & 0x00008000)
|
||||
value |= 0xffff0000;
|
||||
break;
|
||||
case 3:
|
||||
if (value & 0x00800000)
|
||||
value |= 0xff000000;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Returns the model name of the computer (e.g. "MacBookPro1,1")
|
||||
NSString *getModelName(void) {
|
||||
char model[32];
|
||||
size_t len = sizeof(model);
|
||||
int name[2] = {CTL_HW, HW_MODEL};
|
||||
NSString *result;
|
||||
|
||||
if (sysctl(name, 2, &model, &len, NULL, 0) == 0) {
|
||||
result = [NSString stringWithFormat:@"%s", model];
|
||||
} else {
|
||||
result = @"";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)")
|
||||
NSString *getOSVersion(void) {
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:
|
||||
@"/System/Library/CoreServices/SystemVersion.plist"];
|
||||
NSString *versionString = [dict objectForKey:@"ProductVersion"];
|
||||
NSString *buildString = [dict objectForKey:@"ProductBuildVersion"];
|
||||
NSString *wholeString = [NSString stringWithFormat:@"%@ (build %@)",
|
||||
versionString, buildString];
|
||||
return wholeString;
|
||||
}
|
||||
|
||||
// Returns time within the current second in microseconds.
|
||||
// long getMicroseconds() {
|
||||
// struct timeval t;
|
||||
// gettimeofday(&t, 0);
|
||||
// return t.tv_usec;
|
||||
//}
|
||||
|
||||
// Returns fake data given the time. Range is +/-1.
|
||||
float fakeData(NSTimeInterval time) {
|
||||
long secs = lround(floor(time));
|
||||
int secsMod3 = secs % 3;
|
||||
double angle = time * 10 * M_PI * 2;
|
||||
double mag = exp(-(time - (secs - secsMod3)) * 2);
|
||||
return sin(angle) * mag;
|
||||
}
|
||||
|
@ -94,6 +94,7 @@
|
||||
<li><a href="about:license#skia">Skia License</a></li>
|
||||
<li><a href="about:license#snappy">Snappy License</a></li>
|
||||
<li><a href="about:license#sparkle">Sparkle License</a></li>
|
||||
<li><a href="about:license#suitable">Suitable Systems License</a></li>
|
||||
<li><a href="about:license#sunsoft">SunSoft License</a></li>
|
||||
<li><a href="about:license#ucal">University of California License</a></li>
|
||||
<li><a href="about:license#ucambridge">University of Cambridge License</a></li>
|
||||
@ -2234,6 +2235,59 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
</pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1><a id="suitable"></a>Suitable Systems License</h1>
|
||||
|
||||
<p>This license applies to certain files in the directory
|
||||
<span class="path">dom/system/cocoa/</span>.</p>
|
||||
|
||||
<pre>
|
||||
SMSLib Sudden Motion Sensor Access Library
|
||||
Copyright (c) 2010 Suitable Systems
|
||||
All rights reserved.
|
||||
|
||||
Developed by: Daniel Griscom
|
||||
Suitable Systems
|
||||
http://www.suitable.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal with the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the names of Suitable Systems nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this Software without specific prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
|
||||
For more information about SMSLib, see
|
||||
<http://www.suitable.com/tools/smslib.html>
|
||||
or contact
|
||||
Daniel Griscom
|
||||
Suitable Systems
|
||||
1 Centre Street, Suite 204
|
||||
Wakefield, MA 01880
|
||||
(781) 665-0053
|
||||
</pre>
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user