Files
UnrealEngineUWP/Engine/Source/Programs/IOS/UDKRemote/Classes/MainViewController.mm
Ben Marsh 149375b14b Update copyright notices to 2015.
[CL 2379638 by Ben Marsh in Main branch]
2014-12-07 19:09:38 -05:00

840 lines
23 KiB
Plaintext

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
//
// MainViewController.m
// UDKRemote
//
// Created by jadams on 7/28/10.
//
#import "MainViewController.h"
#import "UDKRemoteAppDelegate.h"
//#import <MediaPlayer/MediaPlayer.h>
#import "Engine.h"
#import "IPhoneAsyncTask.h"
#include <netdb.h> // getaddrinfo, struct addrinfo, AI_NUMERICHOST
/** Size of the ring buffer, MUST BE POWER OF TWO */
#define RING_BUFFER_NUM_ELEMENTS 1024
/** These data types must match up on the other side! */
enum EDataType
{
DT_TouchBegan=0,
DT_TouchMoved=1,
DT_TouchEnded=2,
DT_Tilt=3,
DT_Motion=4,
DT_Gyro=5,
DT_Ping=6,
};
// start all messages with this, the other side can use to help verify the data
#define MESSAGE_MAGIC_ID 0xAB
// version of messages being sent
#define MESSAGE_VERSION 1
/** A single event message to send over the network */
struct FMessage
{
unsigned char MagicTag; // verification byte
unsigned char MessageVersion; // version of the message, for compatibility going forward
unsigned short MessageID;
unsigned char DataType;
unsigned char Handle;
unsigned char DeviceOrientation;
unsigned char UIOrientation;
float Data[12]; // enough space for 4 vectors for CMMotion
};
struct FMessageQueue
{
/** The finger this queue is for */
unsigned char FinderIndex;
/** A queue of messages (0 is always a TouchBegan, queue moves up to a TouchEnd) */
FMessage Queue[64];
};
@interface MainViewController()
- (void)OnMotionTimer:(NSTimer*)Timer;
- (void)OnPingTimer:(NSTimer*)Timer;
- (void)PushData:(EDataType)DataType Data:(float*)Data DataSize:(int)DataSize UniqueTouchHandle:(unsigned char)Handle;
@end
@implementation MainViewController
@synthesize HostNameLabel;
@synthesize ResolvedNameLabel;
@synthesize HelpLabel;
@synthesize NavController;
@synthesize MotionManager;
@synthesize ReferenceAttitude;
@synthesize MotionTimer;
@synthesize PingTimer;
@synthesize ResolvedAddrString;
@synthesize ReceiveData;
@synthesize Background;
/** Ring buffer of elements so that we can go back in time to resend events if needed */
static FMessage GRingBuffer[RING_BUFFER_NUM_ELEMENTS];
static void SocketDataCallback(CFSocketRef Socket, CFSocketCallBackType CallbackType, CFDataRef Addr, const void* Data, void* UserInfo);
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// initialization
SocketAddrData = NULL;
AppDelegate = ((UDKRemoteAppDelegate*)[UIApplication sharedApplication].delegate);
// make sure arrays are NULLed out
bzero(AllTouches, sizeof(AllTouches));
// load the images and make views
for (int Index = 0; Index < ARRAY_COUNT(TouchImageViews); Index++)
{
// load the image
UIImage* Image = [UIImage imageNamed:[NSString stringWithFormat:@"Finger%d.png", Index]];
TouchImageViews[Index] = [[UIImageView alloc] initWithImage:Image];
// add it to as a subview
[self.view addSubview:TouchImageViews[Index]];
TouchImageViews[Index].hidden = YES;
}
// create the send socket
PushSocket = CFSocketCreate(NULL, 0, SOCK_DGRAM, 0, kCFSocketNoCallBack, NULL, NULL);
// create the listen socket
CFSocketContext Context = { 0, self, NULL, NULL, NULL };
ReplySocket = CFSocketCreate(NULL, 0, SOCK_DGRAM, 0, kCFSocketDataCallBack, SocketDataCallback, &Context);
// bind to the port
sockaddr_in ServerAddress;
memset(&ServerAddress, 0, sizeof(ServerAddress));
ServerAddress.sin_len = sizeof(ServerAddress);
ServerAddress.sin_family = AF_INET;
// @todo: we should use port 0 and let it choose a port, then send it to the PC every second or something (using CFSocketCopyAddress to get the address that contains the port)
ServerAddress.sin_port = htons(41764);
ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *AddressData = [NSData dataWithBytes:&ServerAddress length:sizeof(ServerAddress)];
if (CFSocketSetAddress(ReplySocket, (CFDataRef)AddressData) != kCFSocketSuccess)
{
NSLog(@"Failed to set addr");
}
// hook uip RunLoop source for our callbacks, etc
CFRunLoopRef RunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef Source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, ReplySocket, 0);
CFRunLoopAddSource(RunLoop, Source, kCFRunLoopCommonModes);
CFRelease(Source);
// start up a timer to ping other end
self.MotionTimer = [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(OnPingTimer:) userInfo:nil repeats:YES];
// start up CoreMotion if possible
float DeviceVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (DeviceVersion >= 4.0f)
{
// create the motion manager and retain it in a propert
self.MotionManager = [[CMMotionManager alloc] init];
[self.MotionManager release];
// see if the hardware has what it needs for Motion, otherwise, just go back to the accelerometer as pre-4.0
if (MotionManager.deviceMotionAvailable)
{
MotionManager.deviceMotionUpdateInterval = 1.0 / 30.0;
[MotionManager startDeviceMotionUpdates];
// turn on the gyro if it's there
if (MotionManager.gyroAvailable)
{
[MotionManager startGyroUpdates];
}
// start up a timer to pull CM data from
self.MotionTimer = [NSTimer scheduledTimerWithTimeInterval:MotionManager.deviceMotionUpdateInterval target:self selector:@selector(OnMotionTimer:) userInfo:nil repeats:YES];
}
else
{
// if there's no Motion available, toss the MotionManager and use the acceleration callbacks
self.MotionManager = nil;
}
}
// register for accelerometer messages if not using accelerometer
if (self.MotionManager == nil)
{
// @todo: Make this and CMMotion rate be configurable
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0 / 30.0];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
}
}
return self;
}
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// resolve any new addresses
[self UpdateSocketAddr];
}
- (IBAction)showInfo
{
/*
NSURL* URL = [NSURL URLWithString:@"http://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/discovery/B02C36_12682301401197_Surrounded_By_Storms/hd/B02C36_12682301401197_Surrounded_By_Storms.mov"];
MPMoviePlayerController* MoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:URL];
MoviePlayer.fullscreen = YES;
[[MoviePlayer view] setFrame:self.view.frame];
[self.view addSubview:[MoviePlayer view]];
[MoviePlayer play];
*/
[self presentModalViewController:self.NavController animated:YES];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)InterfaceOrientation
{
// does user want to allow orientation changes?
if (AppDelegate.bLockOrientation)
{
// if so, return yes for the current orientation (caller wants at least one to return YES)
return InterfaceOrientation == AppDelegate.LockedOrientation;
}
return YES;
}
- (void)CleanupResolution
{
// make sure the host isn't scheduled anymore
CFHostUnscheduleFromRunLoop(ResolvingHost, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFHostSetClient(ResolvingHost, NULL, NULL);
CFRelease(ResolvingHost);
ResolvingHost = NULL;
}
/**
* Member function when resolution finishes
*/
- (void)OnResolveComplete:(const CFStreamError*) Error
{
// did it succeed?
if (Error->error == noErr)
{
// get the resolved addresses array
Boolean bWasResolved = NO;
CFArrayRef Addresses = CFHostGetAddressing(ResolvingHost, &bWasResolved);
if (bWasResolved)
{
int NumAddresses = CFArrayGetCount(Addresses);
// look for the first IPv4 address
for (int AddressIndex = 0; AddressIndex < NumAddresses; AddressIndex++)
{
// just use the last address (it should be IPv4)
CFDataRef AddressData = (CFDataRef)CFArrayGetValueAtIndex(Addresses, AddressIndex);
// get the addr from the address list, and add a port number
sockaddr* ResolvedSockAddr = (struct sockaddr *)CFDataGetBytePtr(AddressData);
// is this IPv4?
if (ResolvedSockAddr->sa_family == AF_INET)
{
sockaddr_in ResolvedAddrWithPort;
memcpy(&ResolvedAddrWithPort, ResolvedSockAddr, ResolvedSockAddr->sa_len);
ResolvedAddrWithPort.sin_port = htons(AppDelegate.Port);
// make a CFData object usable by CFSocket to send to
SocketAddrData = CFDataCreate(NULL, (UInt8*)&ResolvedAddrWithPort, ResolvedAddrWithPort.sin_len);
// turn it into a string
char AddressBuffer[128];
getnameinfo((sockaddr*)&ResolvedAddrWithPort, ResolvedAddrWithPort.sin_len, AddressBuffer, 128, NULL, 0, NI_NUMERICHOST);
self.ResolvedAddrString = [NSString stringWithFormat:@"%s", AddressBuffer];
// put string into the label
self.ResolvedNameLabel.textColor = [UIColor yellowColor];
self.ResolvedNameLabel.text = [NSString stringWithFormat:@"%@:%d [Waiting for connection...]", self.ResolvedAddrString, AppDelegate.Port];
self.HelpLabel.textColor = [UIColor cyanColor];
if (AppDelegate.bShouldIgnoreTilt && !AppDelegate.bShouldIgnoreTouch)
{
self.HelpLabel.text = @"Sending only TOUCH info...";
}
else if (!AppDelegate.bShouldIgnoreTilt && AppDelegate.bShouldIgnoreTouch)
{
self.HelpLabel.text = @"Sending only TILT info...";
}
else if (AppDelegate.bShouldIgnoreTilt && AppDelegate.bShouldIgnoreTouch)
{
self.HelpLabel.textColor = [UIColor yellowColor];
self.HelpLabel.text = @"Sending nothing, change settings...";
}
else
{
self.HelpLabel.text = @"Sending TOUCH and TILT info...";
}
// send a ping right away to get a reply
bIsConnected = NO;
[self PushData:DT_Ping Data:NULL DataSize:0 UniqueTouchHandle:0];
break;
}
}
}
}
else
{
// update label
self.ResolvedNameLabel.text = @"Failed to resolve!";
self.HelpLabel.text = @"Unable to send data...";
self.HelpLabel.textColor = [UIColor redColor];
}
[self CleanupResolution];
}
/**
* Callback function when resolution finishes
*/
static void MyCFHostClientCallBack(CFHostRef Host, CFHostInfoType TypeInfo, const CFStreamError *Error, void *UserInfo)
{
MainViewController* Controller = (MainViewController*)UserInfo;
[Controller OnResolveComplete:Error];
}
/**
* Resolve a network name to IP address
*/
- (BOOL)UpdateSocketAddr
{
// stop any inflight resolution
if (ResolvingHost)
{
CFHostCancelInfoResolution(ResolvingHost, kCFHostAddresses);
[self CleanupResolution];
}
// kill the old data
if (SocketAddrData)
{
CFRelease(SocketAddrData);
SocketAddrData = NULL;
}
// update the ip addr from the other view
NSString* HostName = AppDelegate.PCAddress;
// if it's the same do nothing
// if ([self.HostNameLabel.text isEqualToString:HostName])
// {
// return YES;
// }
if (!HostName)
{
// update the labels
self.HostNameLabel.text = @"None entered!";
self.ResolvedNameLabel.text = @"";
self.HelpLabel.text = @"Unable to send data...";
self.HelpLabel.textColor = [UIColor yellowColor];
return NO;
}
// update the labels
self.HostNameLabel.text = HostName;
self.ResolvedNameLabel.text = @"Resolving...";
self.HelpLabel.text = @"Unable to send data...";
self.HelpLabel.textColor = [UIColor yellowColor];
// make a new host object to resolve
ResolvingHost = CFHostCreateWithName(NULL, (CFStringRef)HostName);
// set up async resolution callback
CFHostClientContext Context = { 0, self, CFRetain, CFRelease, NULL };
if (!CFHostSetClient(ResolvingHost, MyCFHostClientCallBack, &Context))
{
self.ResolvedNameLabel.text = @"Failed to setup resolution...";
self.HelpLabel.text = @"Unable to send data...";
self.HelpLabel.textColor = [UIColor redColor];
[self CleanupResolution];
return NO;
}
// schedule the async operation
CFHostScheduleWithRunLoop(ResolvingHost, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
// perform asynchronous lookup
CFStreamError Error;
if (!CFHostStartInfoResolution(ResolvingHost, kCFHostAddresses, &Error))
{
self.ResolvedNameLabel.text = @"Failed to start resolution...";
self.HelpLabel.text = @"Unable to send data...";
self.HelpLabel.textColor = [UIColor redColor];
[self CleanupResolution];
return NO;
}
return YES;
}
/**
* Process received data
*/
-(void)OnReceivedData:(NSData*)NetData FromAddr:(const NSData*)Addr
{
// we have a connection if we got any data
bIsConnected = YES;
// reset how long it's been since we got a ping
PingsWithoutReply = 0;
if ([NetData length] == 5)
{
char AddressBuffer[128];
getnameinfo((sockaddr*)[Addr bytes], [Addr length], AddressBuffer, 128, NULL, 0, NI_NUMERICHOST);
// put string into the label
self.ResolvedNameLabel.textColor = [UIColor cyanColor];
self.ResolvedNameLabel.text = [NSString stringWithFormat:@"Connected to %s:%d", AddressBuffer, AppDelegate.Port];
return;
}
static int NumChunksWaiting = 0;
int* IntData = (int*)[NetData bytes];
if ([NetData length] > 4 && IntData[0] == 0xFFEEDDCC)
{
NumChunksWaiting = IntData[1];
[self.ReceiveData setLength:0];
}
// get a chunk
else if (NumChunksWaiting)
{
NumChunksWaiting--;
if (self.ReceiveData == nil)
{
self.ReceiveData = [[NSMutableData alloc] init];
}
[self.ReceiveData appendData:NetData];
// did we get em all?
if (NumChunksWaiting == 0)
{
Background.image = [UIImage imageWithData:ReceiveData];
Background.contentMode = UIViewContentModeScaleToFill;
// Background.bounds = Background.superview.frame;
Background.opaque = YES;
}
}
}
/**
* Callback when data comes in from other end
*/
static void SocketDataCallback(CFSocketRef Socket, CFSocketCallBackType CallbackType, CFDataRef Addr, const void* Data, void* UserInfo)
{
MainViewController* Controller = (MainViewController*)UserInfo;
[Controller OnReceivedData:(NSData*)Data FromAddr:(NSData*)Addr];
}
/**
* Called every once in awhile to handle connections
*/
- (void)OnPingTimer:(NSTimer*)Timer
{
PingsWithoutReply++;
if (bIsConnected && PingsWithoutReply == 3)
{
bIsConnected = NO;
self.ResolvedNameLabel.textColor = [UIColor yellowColor];
self.ResolvedNameLabel.text = [NSString stringWithFormat:@"%@:%d [Waiting for connection...]", self.ResolvedAddrString, AppDelegate.Port];
}
// send a ping to PC
[self PushData:DT_Ping Data:NULL DataSize:0 UniqueTouchHandle:0];
}
/**
* Send data over the network
*
* @param DataType What kind of data to send
* @param Data1 DataType-specific data
* @param Data2 DataType-specific data
*/
- (void)PushData:(EDataType)DataType Data:(float*)Data DataSize:(int)DataSize UniqueTouchHandle:(unsigned char)Handle
{
// can only send if we have a valid address
if (!bIsConnected && DataType != DT_Ping)
{
return;
}
static int RingBufferIndex = 0;
// next message to send
FMessage& Message = GRingBuffer[RingBufferIndex];
// move to next element for next time this function is called
RingBufferIndex = (RingBufferIndex + 1) & (RING_BUFFER_NUM_ELEMENTS - 1);
// setup the data to push
Message.MagicTag = MESSAGE_MAGIC_ID;
Message.MessageVersion = 1;
Message.MessageID = MessageID++;
Message.DataType = DataType;
Message.Handle = Handle;
Message.DeviceOrientation = (unsigned char)[[UIDevice currentDevice] orientation];
Message.UIOrientation = (unsigned char)self.interfaceOrientation;
if (Data && DataSize)
{
memcpy(Message.Data, Data, DataSize);
}
// create NSData if needed
if (PushData == nil)
{
PushData = [[NSMutableData alloc] initWithBytes:&Message length:sizeof(Message)];
}
else
{
// replace the whole range
NSRange Range = { 0, sizeof(FMessage) };
[PushData replaceBytesInRange:Range withBytes:&Message];
}
// send over the network
int NumTimesToSend = 1;
// certain messages are very important, so we send them a few times
// and it's up to the listener to discard extra copies
if (DataType == DT_TouchBegan || DataType == DT_TouchEnded)
{
NumTimesToSend = 10;
}
// send as many times as we want
for (int SendIndex = 0; SendIndex < NumTimesToSend; SendIndex++)
{
CFSocketSendData(PushSocket, SocketAddrData, (CFDataRef)PushData, 0);
}
}
/**
* Returns the unique ID for the given touch
*/
-(unsigned char) GetTouchIndex:(UITouch*)Touch
{
// look for existing touch
for (int Index = 0; Index < ARRAY_COUNT(AllTouches); Index++)
{
if (AllTouches[Index] == Touch)
{
return Index;
}
}
// if we get here, it's a new touch, find a slot
for (int Index = 0; Index < ARRAY_COUNT(AllTouches); Index++)
{
if (AllTouches[Index] == nil)
{
AllTouches[Index] = Touch;
return Index;
}
}
// if we get here, that means we are trying to use more than 5 touches at once, which is an error
return 255;
}
/**
* Process a set of touches generically
*/
- (void)ProcessTouches:(NSSet*)Touches OfType:(EDataType)DataType
{
// send each touch
if (!AppDelegate.bShouldIgnoreTouch)
{
for (UITouch* Touch in Touches)
{
// send the touch coords in 0..1 range
CGPoint Loc = [Touch locationInView:self.view];
float Data[2];
Data[0] = (float)Loc.x / (float)self.view.bounds.size.width;
Data[1] = (float)Loc.y / (float)self.view.bounds.size.height;
unsigned char TouchIndex = [self GetTouchIndex:Touch];
[self PushData:DataType Data:Data DataSize:sizeof(Data) UniqueTouchHandle:TouchIndex];
// draw the "fingerprints"
if (TouchIndex < ARRAY_COUNT(TouchImageViews))
{
UIImageView* TouchImageView = TouchImageViews[TouchIndex];
// put the image under the touch
TouchImageView.center = Loc;
// hide when user lets go
if (DataType == DT_TouchEnded)
{
TouchImageView.hidden = YES;
AllTouches[TouchIndex] = nil;
}
else
{
TouchImageView.hidden = NO;
}
}
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self ProcessTouches:touches OfType:DT_TouchBegan];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self ProcessTouches:touches OfType:DT_TouchMoved];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self ProcessTouches:touches OfType:DT_TouchEnded];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self ProcessTouches:touches OfType:DT_TouchEnded];
}
/**
* Set the current tilt to be the "zero" rotation
*/
- (void)CalibrateTilt
{
if (self.MotionManager != nil)
{
self.ReferenceAttitude = MotionManager.deviceMotion.attitude;
}
else
{
bRecenterPitchAndRoll = YES;
}
}
// UIAccelerometerDelegate method, called when the device accelerates.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// Update any pending threads
[IPhoneAsyncTask TickAsyncTasks];
if (!AppDelegate.bShouldIgnoreTilt)
{
// mimic the UE3 iPhone acceleration code
// how much to filter this acceleration
float VectorFilter = bHasInitializedFilter ? 0.85f : 0.0f;
bHasInitializedFilter = YES;
FilteredAccelerometer[0] = VectorFilter * FilteredAccelerometer[0] + (1.0f - VectorFilter) * acceleration.x;
FilteredAccelerometer[1] = VectorFilter * FilteredAccelerometer[1] + (1.0f - VectorFilter) * acceleration.y;
FilteredAccelerometer[2] = VectorFilter * FilteredAccelerometer[2] + (1.0f - VectorFilter) * acceleration.z;
float Vector[3];
Vector[0] = -FilteredAccelerometer[0];
Vector[1] = -FilteredAccelerometer[1];
Vector[2] = -FilteredAccelerometer[2];
float SquareSum = Vector[0] * Vector[0] + Vector[1] * Vector[1] + Vector[2] * Vector[2];
if (SquareSum > (1.e-8))
{
float InvSqrt = 1.0f / sqrt(SquareSum);
Vector[0] *= InvSqrt;
Vector[1] *= InvSqrt;
Vector[2] *= InvSqrt;
}
// calculate pitch and roll
float Data[5];
Data[0] = acceleration.x;
Data[1] = acceleration.y;
Data[2] = acceleration.z;
float CurrentPitch = atan2(Vector[1], Vector[2]);
float CurrentRoll = -atan2(Vector[0], Vector[2]);
if (bRecenterPitchAndRoll)
{
CenterPitch = CurrentPitch;
CenterRoll = CurrentRoll;
bRecenterPitchAndRoll = NO;
}
Data[3] = CurrentPitch - CenterPitch;
Data[4] = CurrentRoll - CenterRoll;
// send it over
[self PushData:DT_Tilt Data:Data DataSize:sizeof(Data) UniqueTouchHandle:0];
}
/*
// Update the accelerometer graph view
if(!isPaused)
{
[filter addAcceleration:acceleration];
[unfiltered addX:acceleration.x y:acceleration.y z:acceleration.z];
[filtered addX:filter.x y:filter.y z:filter.z];
}
*/
}
- (void)OnMotionTimer:(NSTimer*)Timer
{
// Update any pending threads
[IPhoneAsyncTask TickAsyncTasks];
if (!AppDelegate.bShouldIgnoreTilt)
{
// get the CMMotion data
CMAttitude* CurrentAttitude = MotionManager.deviceMotion.attitude;
CMRotationRate CurrentRotationRate = MotionManager.deviceMotion.rotationRate;
CMAcceleration CurrentGravity = MotionManager.deviceMotion.gravity;
CMAcceleration CurrentUserAcceleration = MotionManager.deviceMotion.userAcceleration;
if (ReferenceAttitude)
{
[CurrentAttitude multiplyByInverseOfAttitude:ReferenceAttitude];
}
// setup the message
float Data[12];
Data[0 * 3 + 0] = (float)CurrentAttitude.pitch;
Data[0 * 3 + 1] = (float)CurrentAttitude.yaw;
Data[0 * 3 + 2] = (float)CurrentAttitude.roll;
Data[1 * 3 + 0] = (float)CurrentRotationRate.x;
Data[1 * 3 + 1] = (float)CurrentRotationRate.y;
Data[1 * 3 + 2] = (float)CurrentRotationRate.z;
Data[2 * 3 + 0] = (float)CurrentGravity.x;
Data[2 * 3 + 1] = (float)CurrentGravity.y;
Data[2 * 3 + 2] = (float)CurrentGravity.z;
Data[3 * 3 + 0] = (float)CurrentUserAcceleration.x;
Data[3 * 3 + 1] = (float)CurrentUserAcceleration.y;
Data[3 * 3 + 2] = (float)CurrentUserAcceleration.z;
// send it!
[self PushData:DT_Motion Data:Data DataSize:sizeof(Data) UniqueTouchHandle:0];
// setup gyro message
if (MotionManager.gyroAvailable)
{
CMRotationRate GyroData = MotionManager.gyroData.rotationRate;
float Gyro[3];
Gyro[0] = (float)GyroData.x;
Gyro[1] = (float)GyroData.y;
Gyro[2] = (float)GyroData.z;
// send it
[self PushData:DT_Gyro Data:Gyro DataSize:sizeof(Gyro) UniqueTouchHandle:0];
}
}
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
self.HostNameLabel = nil;
self.ResolvedNameLabel = nil;
self.HelpLabel = nil;
}
- (void)dealloc
{
[super dealloc];
for (int Index = 0; Index < ARRAY_COUNT(TouchImageViews); Index++)
{
[TouchImageViews[Index] release];
}
[PushData release];
CFRelease(PushSocket);
}
@end