RSS

(root)/iphone/tappity : /source/TappityServer.m (revision 62)

To get this branch, use:
bzr branch /browse/iphone/tappity
Line Revision Contents
1 46
//
2
//  TappityServer.m
3
//  Jigs
4
//
5
//  Created by döme on 25.10.2009.
6
//
7
8 54
/*
9
 * Copyright (c) 2009 Doemoetoer Gulyas.
10
 * All rights reserved.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
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.
22
 *
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.
33
 */
34
35
36 46
#import "TappityServer.h"
37
#import "Tappity.h"
38 47
#import <QuartzCore/QuartzCore.h>
39 48
#import <OpenGLES/ES1/gl.h>
40 50
#import <OpenGLES/EAGL.h>
41 49
#import <CoreLocation/CoreLocation.h>
42 50
#import <objc/runtime.h>
43 46
44 61
#define TAPPITY_MAX_FEEDBACK_SIZE 240
45 60
CGImageRef UIGetScreenImage(void);
46
47
@interface UIImage (ScreenImage)
48
+ (UIImage *)imageWithScreenContents;
49 61
- (UIImage*) scaledToSize: (CGSize) size;
50 60
@end
51
52 50
@interface CLLocationManager (TappityServer)
53
54
- (void) setDelegate_tappity: (id) delegate;
55
- (void) startUpdatingLocation_tappity;
56
- (void) stopUpdatingLocation_tappity;
57
- (void) startUpdatingHeading_tappity;
58
- (void) stopUpdatingHeading_tappity;
59
60
@end
61
62
@interface UIAccelerometer (TappityServer)
63
64
- (void) setUpdateInterval_tappity: (NSTimeInterval) dt;
65
66
@end
67
68
@interface EAGLContext (TappityServer)
69
70
- (BOOL)presentRenderbuffer_tappity:(NSUInteger)target;
71
72
73
@end
74
75
76
@implementation CLLocationManager (TappityServer)
77
78
- (void) setDelegate_tappity: (id) obj
79
{
80
	if ([obj respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)])
81
	{
82
		[[TappityServer sharedServer] setLocationDelegate: obj];
83
	}
84
	if ([obj respondsToSelector: @selector(locationManager:didUpdateHeading:)])
85
	{
86
		[[TappityServer sharedServer] setHeadingDelegate: obj];
87
	}
88 56
	[self setDelegate_tappity: obj];
89
90 50
}
91
92
- (void) startUpdatingLocation_tappity
93
{
94
	[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStartUpdatingLocation];
95 56
	[self startUpdatingLocation_tappity];
96 50
}
97
98
- (void) stopUpdatingLocation_tappity
99
{
100
	[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStopUpdatingLocation];
101 56
	[self stopUpdatingLocation_tappity];
102 50
}
103
104
- (void) startUpdatingHeading_tappity
105
{
106
	[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStartUpdatingHeading];
107 56
	[self startUpdatingHeading_tappity];
108 50
}
109
110
- (void) stopUpdatingHeading_tappity
111
{
112
	[[TappityServer sharedServer] sendData: [NSData data] withIdentifier: kTapStopUpdatingHeading];
113 56
	[self stopUpdatingHeading_tappity];
114 50
}
115
116
117
@end
118
119
@implementation UIAccelerometer (TappityServer)
120
121
- (void) setUpdateInterval_tappity: (NSTimeInterval) dt
122
{
123
	[[TappityServer sharedServer] setAccelerometerUpdateInterval: dt];
124
	
125
	[self setUpdateInterval_tappity: dt];
126
}
127
128
@end
129
130
@implementation EAGLContext (TappityServer)
131
132
- (BOOL)presentRenderbuffer_tappity:(NSUInteger)target
133
{
134
	[TappityServer glGrabPoint];
135
	return [self presentRenderbuffer_tappity: target];
136
}
137
138
139
@end
140
141
static void swapMethods(Class c, SEL sela, SEL selb)
142
{
143
	Method ma = class_getInstanceMethod(c, sela);
144
	Method mb = class_getInstanceMethod(c, selb);
145
	method_exchangeImplementations(ma, mb);
146
	
147
}
148
149
static void catch_pipe(int sig_num)
150
{
151
    signal(SIGPIPE, catch_pipe);
152
}
153
154
__attribute__((constructor)) void initTappityServer(void)
155
{
156
//	__builtin_printf("MOOOOOOOOOOOOOOO\n");
157
	/* TODO:
158
		1. intercept UIAccelerometer
159
		2. intercept CLLocationManager
160
		class_getInstanceMethod(), method_setImplementation()
161
	*/
162
163
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
164
	
165
	NSString* name = [[NSBundle mainBundle] bundleIdentifier];
166
	
167 52
	[[TappityServer sharedServer] startBonjourServerWithName: name protocol: @"_tappity._tcp" port: 1234];
168 50
	
169
	Class lmc = NSClassFromString(@"CLLocationManager");
170
	if (lmc)
171
	{
172
		swapMethods(lmc, @selector(setDelegate:), @selector(setDelegate_tappity:));
173
		swapMethods(lmc, @selector(startUpdatingHeading:), @selector(startUpdatingHeading_tappity:));
174
		swapMethods(lmc, @selector(startUpdatingLocation:), @selector(startUpdatingLocation_tappity:));
175
	}
176
177
	Class accc = [UIAccelerometer class];
178
	swapMethods(accc, @selector(setUpdateInterval:), @selector(setUpdateInterval_tappity:));
179
180
	Class glc = NSClassFromString(@"EAGLContext");
181
	if (glc)
182
		swapMethods(glc, @selector(presentRenderbuffer:), @selector(presentRenderbuffer_tappity:));
183
184
	
185
	[pool drain];
186
	
187
	signal(SIGPIPE, catch_pipe);
188
189
}
190
191
192
193 46
194
@implementation TappityServer
195
196 50
+ (void) initialize
197
{
198
	
199
}
200
201 48
- (id) init
202
{
203
	if (!(self = [super init]))
204
		return nil;
205
206
	desiredFrameInterval = 0.333;
207
	
208 53
	messenger = [[SocketMessenger alloc] init];
209
	[messenger setReceiveDataOnMainThread: YES];
210
	[messenger setDelegate: self];
211 48
	
212
	return self;
213
}
214 46
215 56
- (void) sendData: (NSData*) data withIdentifier: (NSInteger) messageId
216
{
217
	[messenger sendData: data withIdentifier: messageId];
218
}
219
220 53
- (void) startBonjourServerWithName: (NSString*) name protocol: (NSString*) protocol port: (int) port
221
{
222
	[messenger startBonjourServerWithName: name protocol: protocol port: port];
223
}
224
225
- (void) connectionWasEstablished: (SocketMessenger*) theMessenger
226 46
{
227 56
	if (locationDelegate)
228
		[messenger sendData: [NSData data] withIdentifier: kTapStartUpdatingLocation];
229
	if (headingDelegate)
230
		[messenger sendData: [NSData data] withIdentifier: kTapStartUpdatingHeading];
231
232 52
	[self performSelectorOnMainThread: @selector(startScreenieTimer) withObject: nil waitUntilDone: NO];
233 46
}
234
235 47
- (void) startScreenieTimer
236
{
237 49
	[captureTimer invalidate];
238 48
	captureTimer = [NSTimer scheduledTimerWithTimeInterval: desiredFrameInterval target: self selector: @selector(captureScreen) userInfo: nil repeats: YES];
239 47
	
240
	
241
	if (!capturedImagesCondition)
242
		capturedImagesCondition = [[NSCondition alloc] init];
243
244 53
	[messenger runThreadWithTarget: self selector: @selector(imageConversionThread:)];
245 47
}
246
247 49
/*
248 46
static size_t _read_i8(const void* buf, uint8_t* p)
249
{
250
	uint8_t v = 0;
251
	memcpy(&v, buf, 1);
252
	*p = v;
253
	return 1;
254
}
255
static size_t _read_i16(const void* buf, uint16_t* p)
256
{
257
	uint16_t v = 0;
258
	memcpy(&v, buf, 2);
259
	*p = CFSwapInt16BigToHost(v);
260
	return 2;
261
}
262
static size_t _read_i32(const void* buf, uint32_t* p)
263
{
264
	uint32_t v = 0;
265
	memcpy(&v, buf, 4);
266
	*p = CFSwapInt32BigToHost(v);
267
	return 4;
268
}
269
static size_t _read_i64(const void* buf, uint64_t* p)
270
{
271
	uint64_t v = 0;
272
	memcpy(&v, buf, 8);
273
	*p = CFSwapInt64BigToHost(v);
274
	return 8;
275
}
276
static size_t _read_float(const void* buf, float* p)
277
{
278
	CFSwappedFloat32 v;
279
	memcpy(&v, buf, 4);
280
	*p = CFConvertFloat32SwappedToHost(v);
281
	return 4;
282
}
283
284
static size_t _read_gspoint(const void* buf, GSTouchPointRef p)
285
{
286
	size_t bc = 0;
287
288
	bc += _read_i32(buf+bc, &p->unk0);
289
	bc += _read_float(buf+bc, &p->unk1);
290
	bc += _read_float(buf+bc, &p->touchSize);
291
	bc += _read_float(buf+bc, &p->x);
292
	bc += _read_float(buf+bc, &p->y);
293
	bc += _read_i32(buf+bc, (uint32_t*)&p->window);
294
	
295
	return bc;
296
}
297
298
- (GSEvent*) gsEventWithData: (NSData*) data
299
{
300
	GSEvent* gse = [[[GSEvent alloc] init] autorelease];
301
	
302
	size_t bc = 0;
303
	const void* buf = [data bytes];
304
	
305
	bc += _read_i32(buf+bc, &gse->type0);
306
	bc += _read_i32(buf+bc, &gse->type1);
307
	bc += _read_i32(buf+bc, &gse->r3);
308
	bc += _read_float(buf+bc, &gse->avgX0);
309
	bc += _read_float(buf+bc, &gse->avgY0);
310
	bc += _read_float(buf+bc, &gse->avgX1);
311
	bc += _read_float(buf+bc, &gse->avgY1);
312
	bc += _read_i32(buf+bc, &gse->processId);
313
	bc += _read_i64(buf+bc, &gse->timestamp);
314
	bc += _read_i32(buf+bc, (uint32_t*)&gse->window);
315
	bc += _read_i32(buf+bc, &gse->r11);
316
	bc += _read_i32(buf+bc, &gse->type12);
317
	bc += _read_i32(buf+bc, &gse->gesture13);
318
	bc += _read_i32(buf+bc, &gse->gesture14);
319
	bc += _read_i16(buf+bc, &gse->numInitialTouches);
320
	bc += _read_i16(buf+bc, &gse->numCurrentTouches);
321
	bc += _read_i32(buf+bc, &gse->r16);
322
	bc += _read_i32(buf+bc, &gse->r17);
323
	bc += _read_i32(buf+bc, &gse->r18);
324
	bc += _read_i32(buf+bc, &gse->r19);
325
	bc += _read_i32(buf+bc, &gse->r20);
326
	bc += _read_i32(buf+bc, &gse->r21);
327
	bc += _read_i8(buf+bc, &gse->r22_0);
328
	bc += _read_i8(buf+bc, &gse->numPoints);
329
	bc += _read_i16(buf+bc, &gse->r22_2);
330
	for (size_t i = 0; i < gse->numPoints; ++i)
331
		bc += _read_gspoint(buf+bc, gse->points + i);
332
333
	return gse;
334
}
335 49
*/
336
337
- (void) doEventPlayback
338
{
339
	[[UIApplication sharedApplication] _playbackEvents: queuedEvents atPlaybackRate: 1.0f messageWhenDone: self withSelector: @selector(eventPlaybackDone:)];
340
	
341
	[queuedEvents autorelease];
342
	queuedEvents = nil;
343
	eventPlaybackBusy = YES;
344
}
345
- (void) eventPlaybackDone:(NSDictionary*)detail
346
{
347
	if ([queuedEvents count])
348
	{
349
		[self doEventPlayback];
350
	}
351
	else
352
		eventPlaybackBusy = NO;
353
}
354 46
355 62
- (NSDictionary*) rescaleTouches: (NSDictionary*) event
356
{
357
	
358
	/*
359
		in "Data"
360
		+33		num touches
361
		+48		x-loc 1st touch
362
		+52		y-loc 1st touch
363
		+72		x-loc 2nd touch
364
		+76		y-loc 2nd touch
365
		
366
		presumably touches at offsets +24 per touch
367
	*/
368
369
	
370
	long type = [[event objectForKey: @"Type"] integerValue];
371
		
372
	if (type == 3001) // touch event
373
	{
374
		NSMutableDictionary* mdict = [event mutableCopy];
375
376
		CGRect targetScreen = [[UIScreen mainScreen] bounds];
377
		CGRect sourceScreen = CGRectFromString([mdict objectForKey: @"UIScreen bounds"]);
378
		
379
		float xoff0 = sourceScreen.origin.x;
380
		float xoff1 = targetScreen.origin.x;
381
		float yoff0 = sourceScreen.origin.y;
382
		float yoff1 = targetScreen.origin.y;
383
		float xs0 = sourceScreen.size.width;
384
		float xs1 = targetScreen.size.width;
385
		float ys0 = sourceScreen.size.height;
386
		float ys1 = targetScreen.size.height;
387
		
388
		CGPoint wloc = CGPointMake([[[mdict objectForKey: @"WindowLocation"] objectForKey: @"X"] floatValue], [[[mdict objectForKey: @"WindowLocation"] objectForKey: @"Y"] floatValue]);
389
		CGPoint loc = CGPointMake([[[mdict objectForKey: @"Location"] objectForKey: @"X"] floatValue], [[[mdict objectForKey: @"Location"] objectForKey: @"Y"] floatValue]);
390
		
391
		wloc.x = (wloc.x - xoff0)*(xs1/xs0) + xoff1;
392
		wloc.y = (wloc.y - yoff0)*(ys1/ys0) + yoff1;
393
		loc.x = (loc.x - xoff0)*(xs1/xs0) + xoff1;
394
		loc.y = (loc.y - yoff0)*(ys1/ys0) + yoff1;
395
		
396
		[mdict setObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithFloat: wloc.x], @"X", [NSNumber numberWithFloat: wloc.y], @"Y", nil] forKey: @"WindowLocation"];
397
		[mdict setObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithFloat: loc.x], @"X", [NSNumber numberWithFloat: loc.y], @"Y", nil] forKey: @"Location"];
398
		
399
		NSMutableData* data = [[event objectForKey: @"Data"] mutableCopy];
400
		void* bytes = [data mutableBytes];
401
		
402
		uint8_t ntouches = ((uint8_t*)bytes)[33];
403
		
404
		for (size_t i = 0; i < ntouches; ++i)
405
		{
406
			size_t ii = 48 + i*24;
407
			float x = *((float*)(bytes + ii));
408
			float y = *((float*)(bytes + ii+4));
409
			
410
			x = (x - xoff0)*(xs1/xs0) + xoff1;
411
			y = (y - yoff0)*(ys1/ys0) + yoff1;
412
			
413
			*((float*)(bytes + ii)) = x;
414
			*((float*)(bytes + ii+4)) = y;
415
416
		}
417
		
418
		[mdict setObject: data forKey: @"Data"];
419
		
420
		[mdict removeObjectForKey: @"UIScreen bounds"];
421
		event = mdict;
422
	}
423
	
424
	return event;
425
426
}
427
428 50
- (void) dataReceived: (NSDictionary*) dataDict
429 46
{
430 52
	uint32_t messageId = [[dataDict objectForKey: SocketMessageIdKey] intValue];
431 51
	
432 53
//	NSLog(@"received messageid %d", messageId);
433 50
434 52
	NSData* tapData = [dataDict objectForKey: SocketMessageDataKey];
435 50
	
436
//	NSDictionary* tapDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
437
	switch(messageId)
438 46
	{
439 49
/*
440 46
		case kTapTouchEvent:
441
		{
442
			NSLog(@"TOUCH EVENT");
443
			
444
			GSEvent* gse = [self gsEventWithData: [tapDict objectForKey: TapGSEventKey]];
445
			UIWindow* window = [[UIApplication sharedApplication] keyWindow];
446
			gse->window = window;
447
			for (size_t i = 0; i < gse->numPoints; ++i)
448
				gse->points[i].window = window;
449
			
450
			int phase = [[tapDict objectForKey: TapTouchPhaseKey] intValue];
451
			NSSet* touches = [UITouch _createTouchesWithGSEvent: gse phase: phase view: [[UIApplication sharedApplication] keyWindow]];
452
			
453
			assert([[UIApplication sharedApplication] keyWindow]);
454
			
455
			NSLog(@"%@", touches);
456
457
			UIEvent* event = [UIEvent alloc];
458
			
459
			Class touchesEventClass = objc_getClass("UITouchesEvent");
460
			if (touchesEventClass && ![[event class] isEqual: touchesEventClass])
461
			{
462
				event = [touchesEventClass alloc];
463
			}
464
			
465
			[event _initWithEvent: gse touches: touches];
466
			
467
			assert(event);
468
			
469
			[[UIApplication sharedApplication] sendEvent: event];
470
						
471
			
472
			break;
473
		}
474 49
*/
475 46
		case kTapRecordedEvent:
476 49
		{			
477 50
			NSDictionary* eventDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
478 46
			
479 49
			if (!queuedEvents)
480
				queuedEvents = [[NSMutableArray alloc] init];
481 46
482 62
			eventDict = [self rescaleTouches: eventDict];
483
484 49
			[queuedEvents addObject: eventDict];
485
			
486
			if (!eventPlaybackBusy)
487
			{
488
				[self doEventPlayback];
489
			}
490 46
			break;
491
		}
492 48
		case kTapAcceleration:
493 49
		{			
494 50
			NSArray* accel = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
495 48
			
496
			UIAccelerometer* accm = [UIAccelerometer sharedAccelerometer];
497
			float x = [[accel objectAtIndex: 0] floatValue];
498
			float y = [[accel objectAtIndex: 1] floatValue];
499
			float z = [[accel objectAtIndex: 2] floatValue];
500
			[accm _acceleratedInX: x y: y z: z timestamp: [[accel objectAtIndex: 3] doubleValue]];
501
			
502
			break;
503
		}
504 49
		case kTapLocation:
505
		{
506
			static CLLocation* oldLoc = nil;
507 50
			CLLocation* loc = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
508 49
			if ([locationDelegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)])
509
			{
510
				[locationDelegate locationManager: (id)self didUpdateToLocation: loc fromLocation: oldLoc];
511
			}
512
			oldLoc = loc;
513
			break;
514
		}
515
		case kTapCompass:
516
		{
517 50
			CLHeading* hdg = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
518 49
			if ([headingDelegate respondsToSelector: @selector(locationManager:didUpdateHeading:)])
519
			{
520
				[headingDelegate locationManager: (id)self didUpdateHeading: hdg];
521
			}
522
			break;
523
		}
524 51
		case kTapStartSendingVisualFeedback:
525
		{
526
			NSLog(@"enabling visual uplink");
527
			enableVisualFeedback = YES;
528
			break;
529
		}
530 46
	}
531
	
532 47
	[tapData release];
533
	
534
}
535
536
- (UIImage *) imageFromView: (UIView *) theView
537
{
538
	UIGraphicsBeginImageContext(theView.frame.size);
539
	CGContextRef context = UIGraphicsGetCurrentContext();
540
//	[theView.layer drawInContext:context];
541
	[theView.layer renderInContext:context];
542
	UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
543
	UIGraphicsEndImageContext();
544
	return theImage;
545
}
546
547
- (void) convertAndSendImage: (UIImage*) img
548
{		
549
	[capturedImagesCondition lock];
550
551
	if (!capturedImagesQueue)
552
		capturedImagesQueue = [[NSMutableArray alloc] init];
553
554
	[capturedImagesQueue addObject: img];
555
	
556
	[capturedImagesCondition signal];
557
	[capturedImagesCondition unlock];
558
		
559
}
560
561
- (void) captureScreen
562
{
563 48
	NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
564
	
565 60
	
566
	
567 51
	if ((now < lastGlUpdate + 2.0*desiredFrameInterval))
568 48
		return;
569
		
570
	// return if any gl has been updated at all
571 51
	if (lastGlUpdate || !enableVisualFeedback)
572 48
		return;
573
	
574 47
	[self performSelectorOnMainThread: @selector(getWindowToCapture) withObject: nil waitUntilDone: NO];
575
576
	UIWindow* window = windowToCapture;
577
	
578
	UIImage* img = [self imageFromView: window];
579 60
//	UIImage* img = [UIImage imageWithScreenContents];
580 47
	
581 48
	lastQuartzUpdate = now;
582
	
583 47
	if (img)
584
		[self convertAndSendImage: img];
585
}
586
587 61
588 47
- (void) imageConversionThread: (id) info
589
{
590
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
591
592
	@synchronized(self)
593
	{
594
		[self retain];
595
	}
596
597 53
	while ([messenger threadActive: info])
598 47
	{
599
		NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
600
601
		[capturedImagesCondition lock];
602
			while (![capturedImagesQueue count])
603
				[capturedImagesCondition wait];
604
				
605
		//NSLog(@"sending");
606
		
607
		UIImage* img = [[[capturedImagesQueue objectAtIndex: 0] retain] autorelease];
608
		[capturedImagesQueue removeObjectAtIndex: 0];
609
		
610
		[capturedImagesCondition unlock];
611 61
		
612
		CGRect imgRect = {CGPointZero, [img size]};
613
		
614
		float aspect = imgRect.size.width/imgRect.size.height;
615
		CGSize newSize = CGSizeMake(
616
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE*aspect),
617
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE/aspect));
618
		
619
		img = [img scaledToSize: newSize];
620 47
621
		NSData* imgData = UIImagePNGRepresentation(img);
622
	
623
		if (imgData)
624 61
		{
625
			NSString* rstr = NSStringFromCGRect(imgRect);
626
			NSDictionary* imgDict = [NSDictionary dictionaryWithObjectsAndKeys: imgData, TapImageDataKey, rstr, TapImageRectKey, rstr, TapReferenceRectKey, nil];
627
			NSData* data = [NSPropertyListSerialization dataFromPropertyList: imgDict format:NSPropertyListBinaryFormat_v1_0 errorDescription: nil];
628
			[messenger sendData: data withIdentifier: kTapPlacedScreenshot];
629
		}
630 47
		
631
		[pool drain];
632
633
	}
634
	
635
636 53
	[messenger threadWillExit: info];
637 47
638
	@synchronized(self)
639
	{
640
		[self release];
641
	}
642
643
	[info release];
644
	[pool drain];
645
}
646
647
648
649
650
- (void) getWindowToCapture
651
{
652
	@synchronized(self)
653
	{
654
		[windowToCapture release];
655
		windowToCapture = [[[UIApplication sharedApplication] keyWindow] retain];
656
	}
657
}
658
- (void) screenieThread: (id) info
659
{
660
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
661
662
	@synchronized(self)
663
	{
664
		[self retain];
665
	}
666
667
668 53
	while ([messenger threadActive: info])
669 47
	{
670
		[self captureScreen];
671
		usleep(200);
672
	}
673
674 53
	[messenger threadWillExit: info];
675 48
676 47
	@synchronized(self)
677
	{
678
		[self release];
679
	}
680
681
	[info release];
682
	[pool drain];
683
}
684
685 46
686 50
- (void) setAccelerometerUpdateInterval: (NSTimeInterval) dt
687
{
688
	CFSwappedFloat64 sdt = CFConvertFloat64HostToSwapped(dt);
689
	NSData* data = [NSData dataWithBytes: &sdt length: sizeof(CFSwappedFloat64)];
690 53
	[messenger sendData: data withIdentifier: kTapAccelerometerUpdateInterval];
691 50
}
692 46
693
/*
694
- (void)performTouches: (NSSet*) touches inView: (UIView *)view
695
{
696
    UITouch *touch = [[UITouch alloc] initInView:view];
697
    UIEvent *eventDown = [[UIEvent alloc] initWithTouch:touch];
698
    
699
    [touch.view touchesBegan:[eventDown allTouches] withEvent:eventDown];
700
    
701
    [touch setPhase:UITouchPhaseEnded];
702
    UIEvent *eventUp = [[UIEvent alloc] initWithTouch:touch];
703
    
704
    [touch.view touchesEnded:[eventUp allTouches] withEvent:eventUp];
705
    
706
    [eventDown release];
707
    [eventUp release];
708
    [touch release];
709
}
710
*/
711
712 61
static UIImage* imageFromGLData(NSData* data, GLint* viewport)
713 48
{
714
    // make data provider with data.
715
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [data bytes], [data length], NULL);
716
   
717
    // prep the ingredients
718
    int bitsPerComponent = 8;
719
    int bitsPerPixel = 32;
720 61
    int bytesPerRow = 4 * viewport[2];
721 48
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
722
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
723
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
724
   
725
    // make the cgimage
726 61
    CGImageRef imageRef = CGImageCreate(viewport[2], viewport[3], bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
727 48
   
728
    // then make the uiimage from that
729
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
730
    return myImage;
731
}
732
733 61
static NSData* dataFromGL(GLint* viewport)
734 48
{
735 61
    NSInteger myDataLength = viewport[2] * viewport[3] * 4;
736 48
	
737
	NSMutableData* data = [NSMutableData dataWithLength: myDataLength];
738
   
739
    // allocate array and read pixels into it.
740
    void* buffer = [data mutableBytes];
741 61
    glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, buffer);
742 48
	
743 61
	size_t bpr = 4*viewport[2];
744 48
	void* rowbuf = calloc(1, bpr);
745
	
746
	// swap image in Y
747 61
	for (size_t i = 0; i < viewport[3]/2; ++i)
748 48
	{
749
		memcpy(rowbuf, buffer + i*bpr, bpr);
750 61
		memcpy(buffer + i*bpr, buffer + (viewport[3]-i-1)*bpr, bpr);
751
		memcpy(buffer + (viewport[3]-i-1)*bpr, rowbuf, bpr);
752 48
	}
753
	
754
	free(rowbuf);
755
 
756
	return data;
757
}
758
759
- (void) glGrabPoint
760
{
761 53
	if (!enableVisualFeedback)
762 48
		return;
763
764
	NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
765
	if (lastGlUpdate + desiredFrameInterval < now)
766
	{
767 61
		
768
		GLint viewport[4];
769
		glGetIntegerv(GL_VIEWPORT, viewport);
770
771
		NSData* rawData = dataFromGL(viewport);
772
		
773
		float aspect = (float)viewport[2]/(float)viewport[3];
774
		CGSize newSize = CGSizeMake(
775
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE*aspect),
776
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE/aspect));
777
778
		UIImage* image = [imageFromGLData(rawData, viewport) scaledToSize: newSize];
779
		
780
		NSData* imgData = UIImagePNGRepresentation(image);
781
		if (imgData)
782
		{
783
			CGRect imgRect = CGRectMake(viewport[0], viewport[1], viewport[2], viewport[3]);
784
			NSString* rstr = NSStringFromCGRect(imgRect);
785
			NSDictionary* imgDict = [NSDictionary dictionaryWithObjectsAndKeys: imgData, TapImageDataKey, rstr, TapImageRectKey, NSStringFromCGRect(CGRectZero), TapReferenceRectKey, nil];
786
			NSData* data = [NSPropertyListSerialization dataFromPropertyList: imgDict format:NSPropertyListBinaryFormat_v1_0 errorDescription: nil];
787
			[messenger sendData: data withIdentifier: kTapPlacedScreenshot];
788
		}
789 48
		
790
		lastGlUpdate = now;
791
	}
792
}
793
794
+ (void) glGrabPoint
795
{
796
	[[self sharedServer] glGrabPoint];
797
}
798
799
+ (id) sharedServer
800
{
801
	static id sharedInstance = nil;
802
	if (!sharedInstance)
803
		sharedInstance = [[TappityServer alloc] init];
804
	
805
	
806
	return sharedInstance;
807
}
808
809 46
- (void) dealloc
810
{
811 53
812 48
	[captureTimer invalidate];
813 53
	
814
	[messenger terminateConnection];
815
	[messenger release];
816
	
817 48
	[capturedImagesCondition release];
818
	[capturedImagesQueue release];
819
820 46
	[super dealloc];
821
}
822
823 49
@synthesize locationDelegate, headingDelegate;
824
825 46
@end
826 61
827
@implementation UIImage (ScreenImage)
828
829
+ (UIImage *)imageWithScreenContents
830
{
831
    CGImageRef cgScreen = UIGetScreenImage();
832
    if (cgScreen) {
833
        UIImage *result = [UIImage imageWithCGImage:cgScreen];
834
        CGImageRelease(cgScreen);
835
        return result;
836
    }
837
    return nil;
838
}
839
840
CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, CGSize size)
841
{
842
	int width = size.width;
843
	int height = size.height;
844
	 
845
	// Declare the number of bytes per row. Each pixel in the bitmap in this
846
	// example is represented by 4 bytes; 8 bits each of red, green, blue, and
847
	// alpha.
848
	size_t bitmapBytesPerRow   = (width * 4);
849
	size_t bitmapByteCount     = (bitmapBytesPerRow * height);
850
	 
851
	// Allocate memory for image data. This is the destination in memory
852
	// where any drawing to the bitmap context will be rendered.
853
	void* bitmapData = malloc( bitmapByteCount );
854
	if (bitmapData == NULL)
855
	{
856
		return nil;
857
	}
858
	 
859
	// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
860
	// per component. Regardless of what the source image format is
861
	// (CMYK, Grayscale, and so on) it will be converted over to the format
862
	// specified here by CGBitmapContextCreate.
863
	CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
864
	CGContextRef context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
865
	colorspace,kCGImageAlphaPremultipliedFirst);
866
	CGColorSpaceRelease(colorspace);
867
	 
868
	if (context == NULL)
869
	// error creating context
870
	{
871
		free(bitmapData);
872
		return nil;
873
	}
874
	 
875
	// Draw the image to the bitmap context. Once we draw, the memory
876
	// allocated for the context for rendering will then contain the
877
	// raw image data in the specified color space.
878
	CGContextDrawImage(context, CGRectMake(0,0,width, height), image);
879
	 
880
	CGImageRef imgRef = CGBitmapContextCreateImage(context);
881
	CGContextRelease(context);
882
	free(bitmapData);
883
	 
884
	return imgRef;
885
}
886
887
888
- (UIImage*) scaledToSize: (CGSize) size
889
{
890
	CGImageRef imgref = CreateScaledCGImageFromCGImage([self CGImage], size);
891
	if (!imgref)
892
		return nil;
893
		
894
	UIImage* img = [UIImage imageWithCGImage: imgref];
895
	
896
	CGImageRelease(imgref);
897
		
898
	return img;
899
}
900
901
@end
902
903

Loggerhead 1.17 is a web-based interface for Bazaar branches