RSS

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

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 50
- (void) dataReceived: (NSDictionary*) dataDict
356 46
{
357 52
	uint32_t messageId = [[dataDict objectForKey: SocketMessageIdKey] intValue];
358 51
	
359 53
//	NSLog(@"received messageid %d", messageId);
360 50
361 52
	NSData* tapData = [dataDict objectForKey: SocketMessageDataKey];
362 50
	
363
//	NSDictionary* tapDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
364
	switch(messageId)
365 46
	{
366 49
/*
367 46
		case kTapTouchEvent:
368
		{
369
			NSLog(@"TOUCH EVENT");
370
			
371
			GSEvent* gse = [self gsEventWithData: [tapDict objectForKey: TapGSEventKey]];
372
			UIWindow* window = [[UIApplication sharedApplication] keyWindow];
373
			gse->window = window;
374
			for (size_t i = 0; i < gse->numPoints; ++i)
375
				gse->points[i].window = window;
376
			
377
			int phase = [[tapDict objectForKey: TapTouchPhaseKey] intValue];
378
			NSSet* touches = [UITouch _createTouchesWithGSEvent: gse phase: phase view: [[UIApplication sharedApplication] keyWindow]];
379
			
380
			assert([[UIApplication sharedApplication] keyWindow]);
381
			
382
			NSLog(@"%@", touches);
383
384
			UIEvent* event = [UIEvent alloc];
385
			
386
			Class touchesEventClass = objc_getClass("UITouchesEvent");
387
			if (touchesEventClass && ![[event class] isEqual: touchesEventClass])
388
			{
389
				event = [touchesEventClass alloc];
390
			}
391
			
392
			[event _initWithEvent: gse touches: touches];
393
			
394
			assert(event);
395
			
396
			[[UIApplication sharedApplication] sendEvent: event];
397
						
398
			
399
			break;
400
		}
401 49
*/
402 46
		case kTapRecordedEvent:
403 49
		{			
404 50
			NSDictionary* eventDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
405 46
			
406 49
			if (!queuedEvents)
407
				queuedEvents = [[NSMutableArray alloc] init];
408 46
409 49
			[queuedEvents addObject: eventDict];
410
			
411
			if (!eventPlaybackBusy)
412
			{
413
				[self doEventPlayback];
414
			}
415 46
			break;
416
		}
417 48
		case kTapAcceleration:
418 49
		{			
419 50
			NSArray* accel = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL];
420 48
			
421
			UIAccelerometer* accm = [UIAccelerometer sharedAccelerometer];
422
			float x = [[accel objectAtIndex: 0] floatValue];
423
			float y = [[accel objectAtIndex: 1] floatValue];
424
			float z = [[accel objectAtIndex: 2] floatValue];
425
			[accm _acceleratedInX: x y: y z: z timestamp: [[accel objectAtIndex: 3] doubleValue]];
426
			
427
			break;
428
		}
429 49
		case kTapLocation:
430
		{
431
			static CLLocation* oldLoc = nil;
432 50
			CLLocation* loc = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
433 49
			if ([locationDelegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)])
434
			{
435
				[locationDelegate locationManager: (id)self didUpdateToLocation: loc fromLocation: oldLoc];
436
			}
437
			oldLoc = loc;
438
			break;
439
		}
440
		case kTapCompass:
441
		{
442 50
			CLHeading* hdg = [NSKeyedUnarchiver unarchiveObjectWithData: tapData];
443 49
			if ([headingDelegate respondsToSelector: @selector(locationManager:didUpdateHeading:)])
444
			{
445
				[headingDelegate locationManager: (id)self didUpdateHeading: hdg];
446
			}
447
			break;
448
		}
449 51
		case kTapStartSendingVisualFeedback:
450
		{
451
			NSLog(@"enabling visual uplink");
452
			enableVisualFeedback = YES;
453
			break;
454
		}
455 46
	}
456
	
457 47
	[tapData release];
458
	
459
}
460
461
- (UIImage *) imageFromView: (UIView *) theView
462
{
463
	UIGraphicsBeginImageContext(theView.frame.size);
464
	CGContextRef context = UIGraphicsGetCurrentContext();
465
//	[theView.layer drawInContext:context];
466
	[theView.layer renderInContext:context];
467
	UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
468
	UIGraphicsEndImageContext();
469
	return theImage;
470
}
471
472
- (void) convertAndSendImage: (UIImage*) img
473
{		
474
	[capturedImagesCondition lock];
475
476
	if (!capturedImagesQueue)
477
		capturedImagesQueue = [[NSMutableArray alloc] init];
478
479
	[capturedImagesQueue addObject: img];
480
	
481
	[capturedImagesCondition signal];
482
	[capturedImagesCondition unlock];
483
		
484
}
485
486
- (void) captureScreen
487
{
488 48
	NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
489
	
490 60
	
491
	
492 51
	if ((now < lastGlUpdate + 2.0*desiredFrameInterval))
493 48
		return;
494
		
495
	// return if any gl has been updated at all
496 51
	if (lastGlUpdate || !enableVisualFeedback)
497 48
		return;
498
	
499 47
	[self performSelectorOnMainThread: @selector(getWindowToCapture) withObject: nil waitUntilDone: NO];
500
501
	UIWindow* window = windowToCapture;
502
	
503
	UIImage* img = [self imageFromView: window];
504 60
//	UIImage* img = [UIImage imageWithScreenContents];
505 47
	
506 48
	lastQuartzUpdate = now;
507
	
508 47
	if (img)
509
		[self convertAndSendImage: img];
510
}
511
512 61
513 47
- (void) imageConversionThread: (id) info
514
{
515
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
516
517
	@synchronized(self)
518
	{
519
		[self retain];
520
	}
521
522 53
	while ([messenger threadActive: info])
523 47
	{
524
		NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
525
526
		[capturedImagesCondition lock];
527
			while (![capturedImagesQueue count])
528
				[capturedImagesCondition wait];
529
				
530
		//NSLog(@"sending");
531
		
532
		UIImage* img = [[[capturedImagesQueue objectAtIndex: 0] retain] autorelease];
533
		[capturedImagesQueue removeObjectAtIndex: 0];
534
		
535
		[capturedImagesCondition unlock];
536 61
		
537
		CGRect imgRect = {CGPointZero, [img size]};
538
		
539
		float aspect = imgRect.size.width/imgRect.size.height;
540
		CGSize newSize = CGSizeMake(
541
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE*aspect),
542
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE/aspect));
543
		
544
		img = [img scaledToSize: newSize];
545 47
546
		NSData* imgData = UIImagePNGRepresentation(img);
547
	
548
		if (imgData)
549 61
		{
550
			NSString* rstr = NSStringFromCGRect(imgRect);
551
			NSDictionary* imgDict = [NSDictionary dictionaryWithObjectsAndKeys: imgData, TapImageDataKey, rstr, TapImageRectKey, rstr, TapReferenceRectKey, nil];
552
			NSData* data = [NSPropertyListSerialization dataFromPropertyList: imgDict format:NSPropertyListBinaryFormat_v1_0 errorDescription: nil];
553
			[messenger sendData: data withIdentifier: kTapPlacedScreenshot];
554
		}
555 47
		
556
		[pool drain];
557
558
	}
559
	
560
561 53
	[messenger threadWillExit: info];
562 47
563
	@synchronized(self)
564
	{
565
		[self release];
566
	}
567
568
	[info release];
569
	[pool drain];
570
}
571
572
573
574
575
- (void) getWindowToCapture
576
{
577
	@synchronized(self)
578
	{
579
		[windowToCapture release];
580
		windowToCapture = [[[UIApplication sharedApplication] keyWindow] retain];
581
	}
582
}
583
- (void) screenieThread: (id) info
584
{
585
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
586
587
	@synchronized(self)
588
	{
589
		[self retain];
590
	}
591
592
593 53
	while ([messenger threadActive: info])
594 47
	{
595
		[self captureScreen];
596
		usleep(200);
597
	}
598
599 53
	[messenger threadWillExit: info];
600 48
601 47
	@synchronized(self)
602
	{
603
		[self release];
604
	}
605
606
	[info release];
607
	[pool drain];
608
}
609
610 46
611 50
- (void) setAccelerometerUpdateInterval: (NSTimeInterval) dt
612
{
613
	CFSwappedFloat64 sdt = CFConvertFloat64HostToSwapped(dt);
614
	NSData* data = [NSData dataWithBytes: &sdt length: sizeof(CFSwappedFloat64)];
615 53
	[messenger sendData: data withIdentifier: kTapAccelerometerUpdateInterval];
616 50
}
617 46
618
/*
619
- (void)performTouches: (NSSet*) touches inView: (UIView *)view
620
{
621
    UITouch *touch = [[UITouch alloc] initInView:view];
622
    UIEvent *eventDown = [[UIEvent alloc] initWithTouch:touch];
623
    
624
    [touch.view touchesBegan:[eventDown allTouches] withEvent:eventDown];
625
    
626
    [touch setPhase:UITouchPhaseEnded];
627
    UIEvent *eventUp = [[UIEvent alloc] initWithTouch:touch];
628
    
629
    [touch.view touchesEnded:[eventUp allTouches] withEvent:eventUp];
630
    
631
    [eventDown release];
632
    [eventUp release];
633
    [touch release];
634
}
635
*/
636
637 61
static UIImage* imageFromGLData(NSData* data, GLint* viewport)
638 48
{
639
    // make data provider with data.
640
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [data bytes], [data length], NULL);
641
   
642
    // prep the ingredients
643
    int bitsPerComponent = 8;
644
    int bitsPerPixel = 32;
645 61
    int bytesPerRow = 4 * viewport[2];
646 48
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
647
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
648
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
649
   
650
    // make the cgimage
651 61
    CGImageRef imageRef = CGImageCreate(viewport[2], viewport[3], bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
652 48
   
653
    // then make the uiimage from that
654
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
655
    return myImage;
656
}
657
658 61
static NSData* dataFromGL(GLint* viewport)
659 48
{
660 61
    NSInteger myDataLength = viewport[2] * viewport[3] * 4;
661 48
	
662
	NSMutableData* data = [NSMutableData dataWithLength: myDataLength];
663
   
664
    // allocate array and read pixels into it.
665
    void* buffer = [data mutableBytes];
666 61
    glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, buffer);
667 48
	
668 61
	size_t bpr = 4*viewport[2];
669 48
	void* rowbuf = calloc(1, bpr);
670
	
671
	// swap image in Y
672 61
	for (size_t i = 0; i < viewport[3]/2; ++i)
673 48
	{
674
		memcpy(rowbuf, buffer + i*bpr, bpr);
675 61
		memcpy(buffer + i*bpr, buffer + (viewport[3]-i-1)*bpr, bpr);
676
		memcpy(buffer + (viewport[3]-i-1)*bpr, rowbuf, bpr);
677 48
	}
678
	
679
	free(rowbuf);
680
 
681
	return data;
682
}
683
684
- (void) glGrabPoint
685
{
686 53
	if (!enableVisualFeedback)
687 48
		return;
688
689
	NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
690
	if (lastGlUpdate + desiredFrameInterval < now)
691
	{
692 61
		
693
		GLint viewport[4];
694
		glGetIntegerv(GL_VIEWPORT, viewport);
695
696
		NSData* rawData = dataFromGL(viewport);
697
		
698
		float aspect = (float)viewport[2]/(float)viewport[3];
699
		CGSize newSize = CGSizeMake(
700
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE*aspect),
701
			MIN(TAPPITY_MAX_FEEDBACK_SIZE, TAPPITY_MAX_FEEDBACK_SIZE/aspect));
702
703
		UIImage* image = [imageFromGLData(rawData, viewport) scaledToSize: newSize];
704
		
705
		NSData* imgData = UIImagePNGRepresentation(image);
706
		if (imgData)
707
		{
708
			CGRect imgRect = CGRectMake(viewport[0], viewport[1], viewport[2], viewport[3]);
709
			NSString* rstr = NSStringFromCGRect(imgRect);
710
			NSDictionary* imgDict = [NSDictionary dictionaryWithObjectsAndKeys: imgData, TapImageDataKey, rstr, TapImageRectKey, NSStringFromCGRect(CGRectZero), TapReferenceRectKey, nil];
711
			NSData* data = [NSPropertyListSerialization dataFromPropertyList: imgDict format:NSPropertyListBinaryFormat_v1_0 errorDescription: nil];
712
			[messenger sendData: data withIdentifier: kTapPlacedScreenshot];
713
		}
714 48
		
715
		lastGlUpdate = now;
716
	}
717
}
718
719
+ (void) glGrabPoint
720
{
721
	[[self sharedServer] glGrabPoint];
722
}
723
724
+ (id) sharedServer
725
{
726
	static id sharedInstance = nil;
727
	if (!sharedInstance)
728
		sharedInstance = [[TappityServer alloc] init];
729
	
730
	
731
	return sharedInstance;
732
}
733
734 46
- (void) dealloc
735
{
736 53
737 48
	[captureTimer invalidate];
738 53
	
739
	[messenger terminateConnection];
740
	[messenger release];
741
	
742 48
	[capturedImagesCondition release];
743
	[capturedImagesQueue release];
744
745 46
	[super dealloc];
746
}
747
748 49
@synthesize locationDelegate, headingDelegate;
749
750 46
@end
751 61
752
@implementation UIImage (ScreenImage)
753
754
+ (UIImage *)imageWithScreenContents
755
{
756
    CGImageRef cgScreen = UIGetScreenImage();
757
    if (cgScreen) {
758
        UIImage *result = [UIImage imageWithCGImage:cgScreen];
759
        CGImageRelease(cgScreen);
760
        return result;
761
    }
762
    return nil;
763
}
764
765
CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, CGSize size)
766
{
767
	int width = size.width;
768
	int height = size.height;
769
	 
770
	// Declare the number of bytes per row. Each pixel in the bitmap in this
771
	// example is represented by 4 bytes; 8 bits each of red, green, blue, and
772
	// alpha.
773
	size_t bitmapBytesPerRow   = (width * 4);
774
	size_t bitmapByteCount     = (bitmapBytesPerRow * height);
775
	 
776
	// Allocate memory for image data. This is the destination in memory
777
	// where any drawing to the bitmap context will be rendered.
778
	void* bitmapData = malloc( bitmapByteCount );
779
	if (bitmapData == NULL)
780
	{
781
		return nil;
782
	}
783
	 
784
	// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
785
	// per component. Regardless of what the source image format is
786
	// (CMYK, Grayscale, and so on) it will be converted over to the format
787
	// specified here by CGBitmapContextCreate.
788
	CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
789
	CGContextRef context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
790
	colorspace,kCGImageAlphaPremultipliedFirst);
791
	CGColorSpaceRelease(colorspace);
792
	 
793
	if (context == NULL)
794
	// error creating context
795
	{
796
		free(bitmapData);
797
		return nil;
798
	}
799
	 
800
	// Draw the image to the bitmap context. Once we draw, the memory
801
	// allocated for the context for rendering will then contain the
802
	// raw image data in the specified color space.
803
	CGContextDrawImage(context, CGRectMake(0,0,width, height), image);
804
	 
805
	CGImageRef imgRef = CGBitmapContextCreateImage(context);
806
	CGContextRelease(context);
807
	free(bitmapData);
808
	 
809
	return imgRef;
810
}
811
812
813
- (UIImage*) scaledToSize: (CGSize) size
814
{
815
	CGImageRef imgref = CreateScaledCGImageFromCGImage([self CGImage], size);
816
	if (!imgref)
817
		return nil;
818
		
819
	UIImage* img = [UIImage imageWithCGImage: imgref];
820
	
821
	CGImageRelease(imgref);
822
		
823
	return img;
824
}
825
826
@end
827
828

Loggerhead 1.17 is a web-based interface for Bazaar branches