5
// Created by döme on 25.10.2009.
9
* Copyright (c) 2009 Doemoetoer Gulyas.
10
* All rights reserved.
12
* Redistribution and use in source and binary forms, with or without
13
* modification, are permitted provided that the following conditions
15
* 1. Redistributions of source code must retain the above copyright
16
* notice, this list of conditions and the following disclaimer.
17
* 2. Redistributions in binary form must reproduce the above copyright
18
* notice, this list of conditions and the following disclaimer in the
19
* documentation and/or other materials provided with the distribution.
20
* 3. The name of the author may not be used to endorse or promote products
21
* derived from this software without specific prior written permission.
23
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
#import "TappityServer.h"
38
#import <QuartzCore/QuartzCore.h>
39
#import <OpenGLES/ES1/gl.h>
40
#import <OpenGLES/EAGL.h>
41
#import <CoreLocation/CoreLocation.h>
42
#import <objc/runtime.h>
45
@interface CLLocationManager (TappityServer)
47
- (void) setDelegate_tappity: (id) delegate;
48
- (void) startUpdatingLocation_tappity;
49
- (void) stopUpdatingLocation_tappity;
50
- (void) startUpdatingHeading_tappity;
51
- (void) stopUpdatingHeading_tappity;
55
@interface UIAccelerometer (TappityServer)
57
- (void) setUpdateInterval_tappity: (NSTimeInterval) dt;
61
@interface EAGLContext (TappityServer)
63
- (BOOL)presentRenderbuffer_tappity:(NSUInteger)target;
69
@implementation CLLocationManager (TappityServer)
71
- (void) setDelegate_tappity: (id) obj
73
if ([obj respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)])
75
[[TappityServer sharedServer] setLocationDelegate: obj];
77
if ([obj respondsToSelector: @selector(locationManager:didUpdateHeading:)])
79
[[TappityServer sharedServer] setHeadingDelegate: obj];
81
[self setDelegate_tappity: obj];
85
- (void) startUpdatingLocation_tappity
87
[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStartUpdatingLocation];
88
[self startUpdatingLocation_tappity];
91
- (void) stopUpdatingLocation_tappity
93
[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStopUpdatingLocation];
94
[self stopUpdatingLocation_tappity];
97
- (void) startUpdatingHeading_tappity
99
[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStartUpdatingHeading];
100
[self startUpdatingHeading_tappity];
103
- (void) stopUpdatingHeading_tappity
105
[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStopUpdatingHeading];
106
[self stopUpdatingHeading_tappity];
112
@implementation UIAccelerometer (TappityServer)
114
- (void) setUpdateInterval_tappity: (NSTimeInterval) dt
116
[[TappityServer sharedServer] setAccelerometerUpdateInterval: dt];
118
[self setUpdateInterval_tappity: dt];
123
@implementation EAGLContext (TappityServer)
125
- (BOOL)presentRenderbuffer_tappity:(NSUInteger)target
127
[TappityServer glGrabPoint];
128
return [self presentRenderbuffer_tappity: target];
134
static void swapMethods(Class c, SEL sela, SEL selb)
136
Method ma = class_getInstanceMethod(c, sela);
137
Method mb = class_getInstanceMethod(c, selb);
138
method_exchangeImplementations(ma, mb);
142
static void catch_pipe(int sig_num)
144
signal(SIGPIPE, catch_pipe);
147
__attribute__((constructor)) void initTappityServer(void)
149
// __builtin_printf("MOOOOOOOOOOOOOOO\n");
151
1. intercept UIAccelerometer
152
2. intercept CLLocationManager
153
class_getInstanceMethod(), method_setImplementation()
156
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
158
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
160
[[TappityServer sharedServer] startBonjourServerWithName: name protocol: @"_tappity._tcp" port: 1234];
162
Class lmc = NSClassFromString(@"CLLocationManager");
165
swapMethods(lmc, @selector(setDelegate:), @selector(setDelegate_tappity:));
166
swapMethods(lmc, @selector(startUpdatingHeading:), @selector(startUpdatingHeading_tappity:));
167
swapMethods(lmc, @selector(startUpdatingLocation:), @selector(startUpdatingLocation_tappity:));
170
Class accc = [UIAccelerometer class];
171
swapMethods(accc, @selector(setUpdateInterval:), @selector(setUpdateInterval_tappity:));
173
Class glc = NSClassFromString(@"EAGLContext");
175
swapMethods(glc, @selector(presentRenderbuffer:), @selector(presentRenderbuffer_tappity:));
180
signal(SIGPIPE, catch_pipe);
187
@implementation TappityServer
196
if (!(self = [super init]))
199
desiredFrameInterval = 0.333;
201
messenger = [[SocketMessenger alloc] init];
202
[messenger setReceiveDataOnMainThread: YES];
203
[messenger setDelegate: self];
208
- (void) sendData: (NSData*) data withIdentifier: (NSInteger) messageId
210
[messenger sendData: data withIdentifier: messageId];
213
- (void) startBonjourServerWithName: (NSString*) name protocol: (NSString*) protocol port: (int) port
215
[messenger startBonjourServerWithName: name protocol: protocol port: port];
218
- (void) connectionWasEstablished: (SocketMessenger*) theMessenger
220
if (locationDelegate)
221
[messenger sendData: [NSData data] withIdentifier: kTapStartUpdatingLocation];
223
[messenger sendData: [NSData data] withIdentifier: kTapStartUpdatingHeading];
225
[self performSelectorOnMainThread: @selector(startScreenieTimer) withObject: nil waitUntilDone: NO];
228
- (void) startScreenieTimer
230
[captureTimer invalidate];
231
captureTimer = [NSTimer scheduledTimerWithTimeInterval: desiredFrameInterval target: self selector: @selector(captureScreen) userInfo: nil repeats: YES];
234
if (!capturedImagesCondition)
235
capturedImagesCondition = [[NSCondition alloc] init];
237
[messenger runThreadWithTarget: self selector: @selector(imageConversionThread:)];
241
static size_t _read_i8(const void* buf, uint8_t* p)
248
static size_t _read_i16(const void* buf, uint16_t* p)
252
*p = CFSwapInt16BigToHost(v);
255
static size_t _read_i32(const void* buf, uint32_t* p)
259
*p = CFSwapInt32BigToHost(v);
262
static size_t _read_i64(const void* buf, uint64_t* p)
266
*p = CFSwapInt64BigToHost(v);
269
static size_t _read_float(const void* buf, float* p)
273
*p = CFConvertFloat32SwappedToHost(v);
277
static size_t _read_gspoint(const void* buf, GSTouchPointRef p)
281
bc += _read_i32(buf+bc, &p->unk0);
282
bc += _read_float(buf+bc, &p->unk1);
283
bc += _read_float(buf+bc, &p->touchSize);
284
bc += _read_float(buf+bc, &p->x);
285
bc += _read_float(buf+bc, &p->y);
286
bc += _read_i32(buf+bc, (uint32_t*)&p->window);
291
- (GSEvent*) gsEventWithData: (NSData*) data
293
GSEvent* gse = [[[GSEvent alloc] init] autorelease];
296
const void* buf = [data bytes];
298
bc += _read_i32(buf+bc, &gse->type0);
299
bc += _read_i32(buf+bc, &gse->type1);
300
bc += _read_i32(buf+bc, &gse->r3);
301
bc += _read_float(buf+bc, &gse->avgX0);
302
bc += _read_float(buf+bc, &gse->avgY0);
303
bc += _read_float(buf+bc, &gse->avgX1);
304
bc += _read_float(buf+bc, &gse->avgY1);
305
bc += _read_i32(buf+bc, &gse->processId);
306
bc += _read_i64(buf+bc, &gse->timestamp);
307
bc += _read_i32(buf+bc, (uint32_t*)&gse->window);
308
bc += _read_i32(buf+bc, &gse->r11);
309
bc += _read_i32(buf+bc, &gse->type12);
310
bc += _read_i32(buf+bc, &gse->gesture13);
311
bc += _read_i32(buf+bc, &gse->gesture14);
312
bc += _read_i16(buf+bc, &gse->numInitialTouches);
313
bc += _read_i16(buf+bc, &gse->numCurrentTouches);
314
bc += _read_i32(buf+bc, &gse->r16);
315
bc += _read_i32(buf+bc, &gse->r17);
316
bc += _read_i32(buf+bc, &gse->r18);
317
bc += _read_i32(buf+bc, &gse->r19);
318
bc += _read_i32(buf+bc, &gse->r20);
319
bc += _read_i32(buf+bc, &gse->r21);
320
bc += _read_i8(buf+bc, &gse->r22_0);
321
bc += _read_i8(buf+bc, &gse->numPoints);
322
bc += _read_i16(buf+bc, &gse->r22_2);
323
for (size_t i = 0; i < gse->numPoints; ++i)
324
bc += _read_gspoint(buf+bc, gse->points + i);
330
- (void) doEventPlayback
332
[[UIApplication sharedApplication] _playbackEvents: queuedEvents atPlaybackRate: 1.0f messageWhenDone: self withSelector: @selector(eventPlaybackDone:)];
334
[queuedEvents autorelease];
336
eventPlaybackBusy = YES;
338
- (void) eventPlaybackDone:(NSDictionary*)detail
340
if ([queuedEvents count])
342
[self doEventPlayback];
345
eventPlaybackBusy = NO;
348
- (void) dataReceived: (NSDictionary*) dataDict
350
uint32_t messageId = [[dataDict objectForKey: SocketMessageIdKey] intValue];
352
// NSLog(@"received messageid %d", messageId);
354
NSData* tapData = [dataDict objectForKey: SocketMessageDataKey];
356
// NSDictionary* tapDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
363
NSLog(@"TOUCH EVENT");
365
GSEvent* gse = [self gsEventWithData: [tapDict objectForKey: TapGSEventKey]];
366
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
367
gse->window = window;
368
for (size_t i = 0; i < gse->numPoints; ++i)
369
gse->points[i].window = window;
371
int phase = [[tapDict objectForKey: TapTouchPhaseKey] intValue];
372
NSSet* touches = [UITouch _createTouchesWithGSEvent: gse phase: phase view: [[UIApplication sharedApplication] keyWindow]];
374
assert([[UIApplication sharedApplication] keyWindow]);
376
NSLog(@"%@", touches);
378
UIEvent* event = [UIEvent alloc];
380
Class touchesEventClass = objc_getClass("UITouchesEvent");
381
if (touchesEventClass && ![[event class] isEqual: touchesEventClass])
383
event = [touchesEventClass alloc];
386
[event _initWithEvent: gse touches: touches];
390
[[UIApplication sharedApplication] sendEvent: event];
396
case kTapRecordedEvent:
398
NSDictionary* eventDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
401
queuedEvents = [[NSMutableArray alloc] init];
403
[queuedEvents addObject: eventDict];
405
if (!eventPlaybackBusy)
407
[self doEventPlayback];
411
case kTapAcceleration:
413
NSArray* accel = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
415
UIAccelerometer* accm = [UIAccelerometer sharedAccelerometer];
416
float x = [[accel objectAtIndex: 0] floatValue];
417
float y = [[accel objectAtIndex: 1] floatValue];
418
float z = [[accel objectAtIndex: 2] floatValue];
419
[accm _acceleratedInX: x y: y z: z timestamp: [[accel objectAtIndex: 3] doubleValue]];
425
static CLLocation* oldLoc = nil;
426
CLLocation* loc = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
427
if ([locationDelegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)])
429
[locationDelegate locationManager: (id)self didUpdateToLocation: loc fromLocation: oldLoc];
436
CLHeading* hdg = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
437
if ([headingDelegate respondsToSelector: @selector(locationManager:didUpdateHeading:)])
439
[headingDelegate locationManager: (id)self didUpdateHeading: hdg];
443
case kTapStartSendingVisualFeedback:
445
NSLog(@"enabling visual uplink");
446
enableVisualFeedback = YES;
455
- (UIImage *) imageFromView: (UIView *) theView
457
UIGraphicsBeginImageContext(theView.frame.size);
458
CGContextRef context = UIGraphicsGetCurrentContext();
459
// [theView.layer drawInContext:context];
460
[theView.layer renderInContext:context];
461
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
462
UIGraphicsEndImageContext();
466
- (void) convertAndSendImage: (UIImage*) img
468
[capturedImagesCondition lock];
470
if (!capturedImagesQueue)
471
capturedImagesQueue = [[NSMutableArray alloc] init];
473
[capturedImagesQueue addObject: img];
475
[capturedImagesCondition signal];
476
[capturedImagesCondition unlock];
480
- (void) captureScreen
482
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
484
if ((now < lastGlUpdate + 2.0*desiredFrameInterval))
487
// return if any gl has been updated at all
488
if (lastGlUpdate || !enableVisualFeedback)
491
[self performSelectorOnMainThread: @selector(getWindowToCapture) withObject: nil waitUntilDone: NO];
493
UIWindow* window = windowToCapture;
495
UIImage* img = [self imageFromView: window];
497
lastQuartzUpdate = now;
500
[self convertAndSendImage: img];
503
- (void) imageConversionThread: (id) info
505
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
512
while ([messenger threadActive: info])
514
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
516
[capturedImagesCondition lock];
517
while (![capturedImagesQueue count])
518
[capturedImagesCondition wait];
522
UIImage* img = [[[capturedImagesQueue objectAtIndex: 0] retain] autorelease];
523
[capturedImagesQueue removeObjectAtIndex: 0];
525
[capturedImagesCondition unlock];
527
NSData* imgData = UIImagePNGRepresentation(img);
530
[messenger sendData: imgData withIdentifier: kTapRawScreenshot];
538
[messenger threadWillExit: info];
552
- (void) getWindowToCapture
556
[windowToCapture release];
557
windowToCapture = [[[UIApplication sharedApplication] keyWindow] retain];
560
- (void) screenieThread: (id) info
562
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
570
while ([messenger threadActive: info])
572
[self captureScreen];
576
[messenger threadWillExit: info];
588
- (void) setAccelerometerUpdateInterval: (NSTimeInterval) dt
590
CFSwappedFloat64 sdt = CFConvertFloat64HostToSwapped(dt);
591
NSData* data = [NSData dataWithBytes: &sdt length: sizeof(CFSwappedFloat64)];
592
[messenger sendData: data withIdentifier: kTapAccelerometerUpdateInterval];
596
- (void)performTouches: (NSSet*) touches inView: (UIView *)view
598
UITouch *touch = [[UITouch alloc] initInView:view];
599
UIEvent *eventDown = [[UIEvent alloc] initWithTouch:touch];
601
[touch.view touchesBegan:[eventDown allTouches] withEvent:eventDown];
603
[touch setPhase:UITouchPhaseEnded];
604
UIEvent *eventUp = [[UIEvent alloc] initWithTouch:touch];
606
[touch.view touchesEnded:[eventUp allTouches] withEvent:eventUp];
614
static UIImage* imageFromGLData(NSData* data)
616
// make data provider with data.
617
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [data bytes], [data length], NULL);
619
// prep the ingredients
620
int bitsPerComponent = 8;
621
int bitsPerPixel = 32;
622
int bytesPerRow = 4 * 320;
623
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
624
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
625
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
628
CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
630
// then make the uiimage from that
631
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
635
static NSData* dataFromGL(void)
637
NSInteger myDataLength = 320 * 480 * 4;
639
NSMutableData* data = [NSMutableData dataWithLength: myDataLength];
641
// allocate array and read pixels into it.
642
void* buffer = [data mutableBytes];
643
glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
646
void* rowbuf = calloc(1, bpr);
649
for (size_t i = 0; i < 480/2; ++i)
651
memcpy(rowbuf, buffer + i*bpr, bpr);
652
memcpy(buffer + i*bpr, buffer + (480-i-1)*bpr, bpr);
653
memcpy(buffer + (480-i-1)*bpr, rowbuf, bpr);
663
if (!enableVisualFeedback)
666
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
667
if (lastGlUpdate + desiredFrameInterval < now)
669
NSData* rawData = dataFromGL();
670
UIImage* image = imageFromGLData(rawData);
672
NSData* data = UIImagePNGRepresentation(image);
674
[messenger sendData: data withIdentifier: kTapRawScreenshot];
682
[[self sharedServer] glGrabPoint];
687
static id sharedInstance = nil;
689
sharedInstance = [[TappityServer alloc] init];
692
return sharedInstance;
698
[captureTimer invalidate];
700
[messenger terminateConnection];
703
[capturedImagesCondition release];
704
[capturedImagesQueue release];
709
@synthesize locationDelegate, headingDelegate;