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 | // Copyright 2009 __MyCompanyName__. All rights reserved. |
|
| 7 | // |
|
| 8 | ||
| 9 | #import "TappityServer.h" |
|
| 10 | #import "Tappity.h" |
|
| 11 | 47 | #import <QuartzCore/QuartzCore.h> |
| 12 | 48 | #import <OpenGLES/ES1/gl.h> |
| 13 | 49 | #import <CoreLocation/CoreLocation.h> |
| 14 | 46 | |
| 15 | ||
| 16 | static void _setStandardSocketOpts(int socket) |
|
| 17 | { |
|
| 18 | int yes = 1; |
|
| 19 | setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); |
|
| 20 | int timeout = 2000; |
|
| 21 | setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); |
|
| 22 | setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); |
|
| 23 | } |
|
| 24 | ||
| 25 | ||
| 26 | @implementation TappityServer |
|
| 27 | ||
| 28 | 48 | - (id) init |
| 29 | { |
|
| 30 | if (!(self = [super init])) |
|
| 31 | return nil; |
|
| 32 | ||
| 33 | desiredFrameInterval = 0.333; |
|
| 34 | |
|
| 35 | receiveDataOnMainThread = YES; |
|
| 36 | |
|
| 37 | return self; |
|
| 38 | } |
|
| 39 | 46 | |
| 40 | - (void) acceptThread: (id) info |
|
| 41 | { |
|
| 42 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 43 | ||
| 44 | @synchronized(self) |
|
| 45 | { |
|
| 46 | [self retain]; |
|
| 47 | } |
|
| 48 | ||
| 49 | while ([self threadActive: info]) |
|
| 50 | { |
|
| 51 | ||
| 52 | struct timeval tv; |
|
| 53 | fd_set readfds; |
|
| 54 | fd_set writefds; |
|
| 55 | fd_set errorfds; |
|
| 56 | int maxSocket = -1; |
|
| 57 | ||
| 58 | tv.tv_sec = 1; |
|
| 59 | tv.tv_usec = 0; |
|
| 60 | ||
| 61 | FD_ZERO(&readfds); |
|
| 62 | FD_ZERO(&writefds); |
|
| 63 | FD_ZERO(&errorfds); |
|
| 64 | ||
| 65 | FD_SET(listenSocket, &readfds); |
|
| 66 | FD_SET(listenSocket, &errorfds); |
|
| 67 | maxSocket = MAX(maxSocket, listenSocket); |
|
| 68 | |
|
| 69 | if (maxSocket < 0) |
|
| 70 | { |
|
| 71 | printf("No sockets, sleeping for a bit...\n"); |
|
| 72 | usleep(1000000); |
|
| 73 | continue; |
|
| 74 | } |
|
| 75 | |
|
| 76 | //NSLog(@"listening for connection..."); |
|
| 77 | ||
| 78 | if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0) |
|
| 79 | { |
|
| 80 | perror("select"); |
|
| 81 | break; |
|
| 82 | } |
|
| 83 | ||
| 84 | if (FD_ISSET(listenSocket, &readfds)) |
|
| 85 | { |
|
| 86 | NSLog(@"Accepting connection..."); |
|
| 87 | // accept |
|
| 88 | socklen_t sinSize = sizeof(struct sockaddr_in); |
|
| 89 | struct sockaddr_in peerAddress; |
|
| 90 | int newSocket = accept(listenSocket, (struct sockaddr *)&peerAddress, &sinSize); |
|
| 91 | |
|
| 92 | _setStandardSocketOpts(newSocket); |
|
| 93 | ||
| 94 | |
|
| 95 | if (newSocket == -1) |
|
| 96 | { |
|
| 97 | if (errno == EWOULDBLOCK) |
|
| 98 | { |
|
| 99 | } |
|
| 100 | else |
|
| 101 | { |
|
| 102 | printf("Error accepting connection.\n"); |
|
| 103 | break; |
|
| 104 | } |
|
| 105 | } |
|
| 106 | |
|
| 107 | if (commsSocket) |
|
| 108 | close(commsSocket); |
|
| 109 | ||
| 110 | // add socket to sockets list |
|
| 111 | commsSocket = newSocket; |
|
| 112 | |
|
| 113 | |
|
| 114 | 48 | [netService stop]; |
| 115 | [netService release]; |
|
| 116 | netService = nil; |
|
| 117 | ||
| 118 | 47 | if (!sendLock) |
| 119 | sendLock = [[NSCondition alloc] init]; |
|
| 120 | |
|
| 121 | [self runThreadWithSelector: @selector(sendingThread:)]; |
|
| 122 | ||
| 123 | 46 | [self runThreadWithSelector: @selector(receivingThread:)]; |
| 124 | |
|
| 125 | 47 | [self performSelectorOnMainThread: @selector(startScreenieTimer) withObject: nil waitUntilDone: NO]; |
| 126 | ||
| 127 | //[self runThreadWithSelector: @selector(screenieThread:)]; |
|
| 128 | |
|
| 129 | 48 | |
| 130 | 46 | NSLog(@"Accepted connection."); |
| 131 | } |
|
| 132 | ||
| 133 | } |
|
| 134 | ||
| 135 | @synchronized(self) |
|
| 136 | { |
|
| 137 | [self release]; |
|
| 138 | } |
|
| 139 | ||
| 140 | [info release]; |
|
| 141 | [pool drain]; |
|
| 142 | } |
|
| 143 | ||
| 144 | 47 | - (void) startScreenieTimer |
| 145 | { |
|
| 146 | 49 | [captureTimer invalidate]; |
| 147 | 48 | captureTimer = [NSTimer scheduledTimerWithTimeInterval: desiredFrameInterval target: self selector: @selector(captureScreen) userInfo: nil repeats: YES]; |
| 148 | 47 | |
| 149 | |
|
| 150 | if (!capturedImagesCondition) |
|
| 151 | capturedImagesCondition = [[NSCondition alloc] init]; |
|
| 152 | ||
| 153 | [self runThreadWithSelector: @selector(imageConversionThread:)]; |
|
| 154 | } |
|
| 155 | ||
| 156 | 46 | - (BOOL) enableBonjourWithDomain:(NSString*)domain applicationProtocol:(NSString*)protocol name:(NSString*)name |
| 157 | { |
|
| 158 | if(![domain length]) |
|
| 159 | domain = @""; //Will use default Bonjour registration doamins, typically just ".local" |
|
| 160 | if(![name length]) |
|
| 161 | name = @""; //Will use default Bonjour name, e.g. the name assigned to the device in iTunes |
|
| 162 | |
|
| 163 | assert([protocol length] && listenSocket); |
|
| 164 | |
|
| 165 | NSLog(@"tappity port: %d", ntohs(myAddress.sin_port)); |
|
| 166 | ||
| 167 | netService = [[NSNetService alloc] initWithDomain: domain type: protocol name: name port: ntohs(myAddress.sin_port)]; |
|
| 168 | if(netService == nil) |
|
| 169 | return NO; |
|
| 170 | |
|
| 171 | [netService setDelegate:self]; |
|
| 172 | // [netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; |
|
| 173 | [netService publish]; |
|
| 174 | |
|
| 175 | return YES; |
|
| 176 | } |
|
| 177 | ||
| 178 | 49 | - (void) startListening |
| 179 | 46 | { |
| 180 | myAddress.sin_family = AF_INET; // host byte order |
|
| 181 | myAddress.sin_port = htons(1234); // short, network byte order, any port |
|
| 182 | myAddress.sin_addr.s_addr = htonl(INADDR_ANY); // auto-fill with my IP |
|
| 183 | ||
| 184 | listenSocket = socket(PF_INET, SOCK_STREAM, 0); |
|
| 185 | assert(listenSocket != -1); |
|
| 186 | ||
| 187 | _setStandardSocketOpts(listenSocket); |
|
| 188 | ||
| 189 | int err = bind(self->listenSocket, (struct sockaddr *)&myAddress, sizeof(myAddress)); |
|
| 190 | assert(-1 != err); |
|
| 191 | |
|
| 192 | err = listen(self->listenSocket, 1); |
|
| 193 | assert(-1 != err); |
|
| 194 | ||
| 195 | [self runThreadWithSelector: @selector(acceptThread:)]; |
|
| 196 | |
|
| 197 | 48 | if([self enableBonjourWithDomain: @"" applicationProtocol: @"_tappity._tcp" name: serviceName]) |
| 198 | 46 | NSLog(@"tappity bounjour advertisments up and running"); |
| 199 | ||
| 200 | } |
|
| 201 | ||
| 202 | 49 | - (void) startWithName: (NSString*) name |
| 203 | { |
|
| 204 | NSLog(@"starting tappity server"); |
|
| 205 | |
|
| 206 | [serviceName release]; |
|
| 207 | serviceName = [name retain]; |
|
| 208 | |
|
| 209 | [self startListening]; |
|
| 210 | ||
| 211 | } |
|
| 212 | ||
| 213 | /* |
|
| 214 | 46 | static size_t _read_i8(const void* buf, uint8_t* p) |
| 215 | { |
|
| 216 | uint8_t v = 0; |
|
| 217 | memcpy(&v, buf, 1); |
|
| 218 | *p = v; |
|
| 219 | return 1; |
|
| 220 | } |
|
| 221 | static size_t _read_i16(const void* buf, uint16_t* p) |
|
| 222 | { |
|
| 223 | uint16_t v = 0; |
|
| 224 | memcpy(&v, buf, 2); |
|
| 225 | *p = CFSwapInt16BigToHost(v); |
|
| 226 | return 2; |
|
| 227 | } |
|
| 228 | static size_t _read_i32(const void* buf, uint32_t* p) |
|
| 229 | { |
|
| 230 | uint32_t v = 0; |
|
| 231 | memcpy(&v, buf, 4); |
|
| 232 | *p = CFSwapInt32BigToHost(v); |
|
| 233 | return 4; |
|
| 234 | } |
|
| 235 | static size_t _read_i64(const void* buf, uint64_t* p) |
|
| 236 | { |
|
| 237 | uint64_t v = 0; |
|
| 238 | memcpy(&v, buf, 8); |
|
| 239 | *p = CFSwapInt64BigToHost(v); |
|
| 240 | return 8; |
|
| 241 | } |
|
| 242 | static size_t _read_float(const void* buf, float* p) |
|
| 243 | { |
|
| 244 | CFSwappedFloat32 v; |
|
| 245 | memcpy(&v, buf, 4); |
|
| 246 | *p = CFConvertFloat32SwappedToHost(v); |
|
| 247 | return 4; |
|
| 248 | } |
|
| 249 | ||
| 250 | static size_t _read_gspoint(const void* buf, GSTouchPointRef p) |
|
| 251 | { |
|
| 252 | size_t bc = 0; |
|
| 253 | ||
| 254 | bc += _read_i32(buf+bc, &p->unk0); |
|
| 255 | bc += _read_float(buf+bc, &p->unk1); |
|
| 256 | bc += _read_float(buf+bc, &p->touchSize); |
|
| 257 | bc += _read_float(buf+bc, &p->x); |
|
| 258 | bc += _read_float(buf+bc, &p->y); |
|
| 259 | bc += _read_i32(buf+bc, (uint32_t*)&p->window); |
|
| 260 | |
|
| 261 | return bc; |
|
| 262 | } |
|
| 263 | ||
| 264 | - (GSEvent*) gsEventWithData: (NSData*) data |
|
| 265 | { |
|
| 266 | GSEvent* gse = [[[GSEvent alloc] init] autorelease]; |
|
| 267 | |
|
| 268 | size_t bc = 0; |
|
| 269 | const void* buf = [data bytes]; |
|
| 270 | |
|
| 271 | bc += _read_i32(buf+bc, &gse->type0); |
|
| 272 | bc += _read_i32(buf+bc, &gse->type1); |
|
| 273 | bc += _read_i32(buf+bc, &gse->r3); |
|
| 274 | bc += _read_float(buf+bc, &gse->avgX0); |
|
| 275 | bc += _read_float(buf+bc, &gse->avgY0); |
|
| 276 | bc += _read_float(buf+bc, &gse->avgX1); |
|
| 277 | bc += _read_float(buf+bc, &gse->avgY1); |
|
| 278 | bc += _read_i32(buf+bc, &gse->processId); |
|
| 279 | bc += _read_i64(buf+bc, &gse->timestamp); |
|
| 280 | bc += _read_i32(buf+bc, (uint32_t*)&gse->window); |
|
| 281 | bc += _read_i32(buf+bc, &gse->r11); |
|
| 282 | bc += _read_i32(buf+bc, &gse->type12); |
|
| 283 | bc += _read_i32(buf+bc, &gse->gesture13); |
|
| 284 | bc += _read_i32(buf+bc, &gse->gesture14); |
|
| 285 | bc += _read_i16(buf+bc, &gse->numInitialTouches); |
|
| 286 | bc += _read_i16(buf+bc, &gse->numCurrentTouches); |
|
| 287 | bc += _read_i32(buf+bc, &gse->r16); |
|
| 288 | bc += _read_i32(buf+bc, &gse->r17); |
|
| 289 | bc += _read_i32(buf+bc, &gse->r18); |
|
| 290 | bc += _read_i32(buf+bc, &gse->r19); |
|
| 291 | bc += _read_i32(buf+bc, &gse->r20); |
|
| 292 | bc += _read_i32(buf+bc, &gse->r21); |
|
| 293 | bc += _read_i8(buf+bc, &gse->r22_0); |
|
| 294 | bc += _read_i8(buf+bc, &gse->numPoints); |
|
| 295 | bc += _read_i16(buf+bc, &gse->r22_2); |
|
| 296 | for (size_t i = 0; i < gse->numPoints; ++i) |
|
| 297 | bc += _read_gspoint(buf+bc, gse->points + i); |
|
| 298 | ||
| 299 | return gse; |
|
| 300 | } |
|
| 301 | 49 | */ |
| 302 | ||
| 303 | - (void) doEventPlayback |
|
| 304 | { |
|
| 305 | [[UIApplication sharedApplication] _playbackEvents: queuedEvents atPlaybackRate: 1.0f messageWhenDone: self withSelector: @selector(eventPlaybackDone:)]; |
|
| 306 | |
|
| 307 | [queuedEvents autorelease]; |
|
| 308 | queuedEvents = nil; |
|
| 309 | eventPlaybackBusy = YES; |
|
| 310 | } |
|
| 311 | - (void) eventPlaybackDone:(NSDictionary*)detail |
|
| 312 | { |
|
| 313 | if ([queuedEvents count]) |
|
| 314 | { |
|
| 315 | [self doEventPlayback]; |
|
| 316 | } |
|
| 317 | else |
|
| 318 | eventPlaybackBusy = NO; |
|
| 319 | } |
|
| 320 | 46 | |
| 321 | 47 | - (void) dataReceived: (NSData*) tapData |
| 322 | 46 | { |
| 323 | assert([tapData length] > 4); |
|
| 324 | |
|
| 325 | |
|
| 326 | NSDictionary* tapDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL]; |
|
| 327 | assert(tapDict); |
|
| 328 | switch([[tapDict objectForKey: TapIdKey] intValue]) |
|
| 329 | { |
|
| 330 | 49 | /* |
| 331 | 46 | case kTapTouchEvent: |
| 332 | { |
|
| 333 | NSLog(@"TOUCH EVENT"); |
|
| 334 | |
|
| 335 | GSEvent* gse = [self gsEventWithData: [tapDict objectForKey: TapGSEventKey]]; |
|
| 336 | UIWindow* window = [[UIApplication sharedApplication] keyWindow]; |
|
| 337 | gse->window = window; |
|
| 338 | for (size_t i = 0; i < gse->numPoints; ++i) |
|
| 339 | gse->points[i].window = window; |
|
| 340 | |
|
| 341 | int phase = [[tapDict objectForKey: TapTouchPhaseKey] intValue]; |
|
| 342 | NSSet* touches = [UITouch _createTouchesWithGSEvent: gse phase: phase view: [[UIApplication sharedApplication] keyWindow]]; |
|
| 343 | |
|
| 344 | assert([[UIApplication sharedApplication] keyWindow]); |
|
| 345 | |
|
| 346 | NSLog(@"%@", touches); |
|
| 347 | ||
| 348 | UIEvent* event = [UIEvent alloc]; |
|
| 349 | |
|
| 350 | Class touchesEventClass = objc_getClass("UITouchesEvent"); |
|
| 351 | if (touchesEventClass && ![[event class] isEqual: touchesEventClass]) |
|
| 352 | { |
|
| 353 | event = [touchesEventClass alloc]; |
|
| 354 | } |
|
| 355 | |
|
| 356 | [event _initWithEvent: gse touches: touches]; |
|
| 357 | |
|
| 358 | assert(event); |
|
| 359 | |
|
| 360 | [[UIApplication sharedApplication] sendEvent: event]; |
|
| 361 | |
|
| 362 | |
|
| 363 | break; |
|
| 364 | } |
|
| 365 | 49 | */ |
| 366 | 46 | case kTapRecordedEvent: |
| 367 | 49 | { |
| 368 | 46 | NSDictionary* eventDict = [tapDict objectForKey: TapRecordedEventKey]; |
| 369 | |
|
| 370 | 49 | if (!queuedEvents) |
| 371 | queuedEvents = [[NSMutableArray alloc] init]; |
|
| 372 | 46 | |
| 373 | 49 | [queuedEvents addObject: eventDict]; |
| 374 | |
|
| 375 | if (!eventPlaybackBusy) |
|
| 376 | { |
|
| 377 | [self doEventPlayback]; |
|
| 378 | } |
|
| 379 | 46 | break; |
| 380 | } |
|
| 381 | 48 | case kTapAcceleration: |
| 382 | 49 | { |
| 383 | 48 | NSArray* accel = [tapDict objectForKey: TapAccelerationKey]; |
| 384 | |
|
| 385 | UIAccelerometer* accm = [UIAccelerometer sharedAccelerometer]; |
|
| 386 | float x = [[accel objectAtIndex: 0] floatValue]; |
|
| 387 | float y = [[accel objectAtIndex: 1] floatValue]; |
|
| 388 | float z = [[accel objectAtIndex: 2] floatValue]; |
|
| 389 | [accm _acceleratedInX: x y: y z: z timestamp: [[accel objectAtIndex: 3] doubleValue]]; |
|
| 390 | |
|
| 391 | break; |
|
| 392 | } |
|
| 393 | 49 | case kTapLocation: |
| 394 | { |
|
| 395 | static CLLocation* oldLoc = nil; |
|
| 396 | CLLocation* loc = [NSKeyedUnarchiver unarchiveObjectWithData: [tapDict objectForKey: TapLocationKey]]; |
|
| 397 | if ([locationDelegate respondsToSelector: @selector(locationManager:didUpdateToLocation:fromLocation:)]) |
|
| 398 | { |
|
| 399 | [locationDelegate locationManager: (id)self didUpdateToLocation: loc fromLocation: oldLoc]; |
|
| 400 | } |
|
| 401 | oldLoc = loc; |
|
| 402 | break; |
|
| 403 | } |
|
| 404 | case kTapCompass: |
|
| 405 | { |
|
| 406 | CLHeading* hdg = [NSKeyedUnarchiver unarchiveObjectWithData: [tapDict objectForKey: TapCompassKey]]; |
|
| 407 | if ([headingDelegate respondsToSelector: @selector(locationManager:didUpdateHeading:)]) |
|
| 408 | { |
|
| 409 | [headingDelegate locationManager: (id)self didUpdateHeading: hdg]; |
|
| 410 | } |
|
| 411 | break; |
|
| 412 | } |
|
| 413 | 46 | } |
| 414 | |
|
| 415 | 47 | [tapData release]; |
| 416 | |
|
| 417 | } |
|
| 418 | ||
| 419 | - (UIImage *) imageFromView: (UIView *) theView |
|
| 420 | { |
|
| 421 | UIGraphicsBeginImageContext(theView.frame.size); |
|
| 422 | CGContextRef context = UIGraphicsGetCurrentContext(); |
|
| 423 | // [theView.layer drawInContext:context]; |
|
| 424 | [theView.layer renderInContext:context]; |
|
| 425 | UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext(); |
|
| 426 | UIGraphicsEndImageContext(); |
|
| 427 | return theImage; |
|
| 428 | } |
|
| 429 | ||
| 430 | - (void) convertAndSendImage: (UIImage*) img |
|
| 431 | { |
|
| 432 | [capturedImagesCondition lock]; |
|
| 433 | ||
| 434 | if (!capturedImagesQueue) |
|
| 435 | capturedImagesQueue = [[NSMutableArray alloc] init]; |
|
| 436 | ||
| 437 | [capturedImagesQueue addObject: img]; |
|
| 438 | |
|
| 439 | [capturedImagesCondition signal]; |
|
| 440 | [capturedImagesCondition unlock]; |
|
| 441 | |
|
| 442 | } |
|
| 443 | ||
| 444 | - (void) captureScreen |
|
| 445 | { |
|
| 446 | 48 | NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; |
| 447 | |
|
| 448 | if (now < lastGlUpdate + 2.0*desiredFrameInterval) |
|
| 449 | return; |
|
| 450 | |
|
| 451 | // return if any gl has been updated at all |
|
| 452 | if (lastGlUpdate) |
|
| 453 | return; |
|
| 454 | |
|
| 455 | 47 | [self performSelectorOnMainThread: @selector(getWindowToCapture) withObject: nil waitUntilDone: NO]; |
| 456 | ||
| 457 | UIWindow* window = windowToCapture; |
|
| 458 | |
|
| 459 | UIImage* img = [self imageFromView: window]; |
|
| 460 | |
|
| 461 | 48 | lastQuartzUpdate = now; |
| 462 | |
|
| 463 | 47 | if (img) |
| 464 | [self convertAndSendImage: img]; |
|
| 465 | } |
|
| 466 | ||
| 467 | - (void) imageConversionThread: (id) info |
|
| 468 | { |
|
| 469 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 470 | ||
| 471 | @synchronized(self) |
|
| 472 | { |
|
| 473 | [self retain]; |
|
| 474 | } |
|
| 475 | ||
| 476 | while ([self threadActive: info]) |
|
| 477 | { |
|
| 478 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 479 | ||
| 480 | [capturedImagesCondition lock]; |
|
| 481 | while (![capturedImagesQueue count]) |
|
| 482 | [capturedImagesCondition wait]; |
|
| 483 | |
|
| 484 | //NSLog(@"sending"); |
|
| 485 | |
|
| 486 | UIImage* img = [[[capturedImagesQueue objectAtIndex: 0] retain] autorelease]; |
|
| 487 | [capturedImagesQueue removeObjectAtIndex: 0]; |
|
| 488 | |
|
| 489 | [capturedImagesCondition unlock]; |
|
| 490 | ||
| 491 | NSData* imgData = UIImagePNGRepresentation(img); |
|
| 492 | |
|
| 493 | if (imgData) |
|
| 494 | [self sendData: imgData]; |
|
| 495 | ||
| 496 | |
|
| 497 | [pool drain]; |
|
| 498 | ||
| 499 | } |
|
| 500 | |
|
| 501 | ||
| 502 | [self threadWillExit: info]; |
|
| 503 | ||
| 504 | @synchronized(self) |
|
| 505 | { |
|
| 506 | [self release]; |
|
| 507 | } |
|
| 508 | ||
| 509 | [info release]; |
|
| 510 | [pool drain]; |
|
| 511 | } |
|
| 512 | ||
| 513 | ||
| 514 | ||
| 515 | ||
| 516 | - (void) getWindowToCapture |
|
| 517 | { |
|
| 518 | @synchronized(self) |
|
| 519 | { |
|
| 520 | [windowToCapture release]; |
|
| 521 | windowToCapture = [[[UIApplication sharedApplication] keyWindow] retain]; |
|
| 522 | } |
|
| 523 | } |
|
| 524 | - (void) screenieThread: (id) info |
|
| 525 | { |
|
| 526 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 527 | ||
| 528 | @synchronized(self) |
|
| 529 | { |
|
| 530 | [self retain]; |
|
| 531 | } |
|
| 532 | ||
| 533 | ||
| 534 | while ([self threadActive: info]) |
|
| 535 | { |
|
| 536 | [self captureScreen]; |
|
| 537 | usleep(200); |
|
| 538 | } |
|
| 539 | ||
| 540 | 48 | [self threadWillExit: info]; |
| 541 | ||
| 542 | 47 | @synchronized(self) |
| 543 | { |
|
| 544 | [self release]; |
|
| 545 | } |
|
| 546 | ||
| 547 | [info release]; |
|
| 548 | [pool drain]; |
|
| 549 | } |
|
| 550 | ||
| 551 | 46 | |
| 552 | ||
| 553 | /* |
|
| 554 | - (void)performTouches: (NSSet*) touches inView: (UIView *)view |
|
| 555 | { |
|
| 556 | UITouch *touch = [[UITouch alloc] initInView:view]; |
|
| 557 | UIEvent *eventDown = [[UIEvent alloc] initWithTouch:touch]; |
|
| 558 | |
|
| 559 | [touch.view touchesBegan:[eventDown allTouches] withEvent:eventDown]; |
|
| 560 | |
|
| 561 | [touch setPhase:UITouchPhaseEnded]; |
|
| 562 | UIEvent *eventUp = [[UIEvent alloc] initWithTouch:touch]; |
|
| 563 | |
|
| 564 | [touch.view touchesEnded:[eventUp allTouches] withEvent:eventUp]; |
|
| 565 | |
|
| 566 | [eventDown release]; |
|
| 567 | [eventUp release]; |
|
| 568 | [touch release]; |
|
| 569 | } |
|
| 570 | */ |
|
| 571 | ||
| 572 | 48 | static UIImage* imageFromGLData(NSData* data) |
| 573 | { |
|
| 574 | // make data provider with data. |
|
| 575 | CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [data bytes], [data length], NULL); |
|
| 576 | |
|
| 577 | // prep the ingredients |
|
| 578 | int bitsPerComponent = 8; |
|
| 579 | int bitsPerPixel = 32; |
|
| 580 | int bytesPerRow = 4 * 320; |
|
| 581 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); |
|
| 582 | CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; |
|
| 583 | CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; |
|
| 584 | |
|
| 585 | // make the cgimage |
|
| 586 | CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); |
|
| 587 | |
|
| 588 | // then make the uiimage from that |
|
| 589 | UIImage *myImage = [UIImage imageWithCGImage:imageRef]; |
|
| 590 | return myImage; |
|
| 591 | } |
|
| 592 | ||
| 593 | static NSData* dataFromGL(void) |
|
| 594 | { |
|
| 595 | NSInteger myDataLength = 320 * 480 * 4; |
|
| 596 | |
|
| 597 | NSMutableData* data = [NSMutableData dataWithLength: myDataLength]; |
|
| 598 | |
|
| 599 | // allocate array and read pixels into it. |
|
| 600 | void* buffer = [data mutableBytes]; |
|
| 601 | glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
|
| 602 | |
|
| 603 | size_t bpr = 4*320; |
|
| 604 | void* rowbuf = calloc(1, bpr); |
|
| 605 | |
|
| 606 | // swap image in Y |
|
| 607 | for (size_t i = 0; i < 480/2; ++i) |
|
| 608 | { |
|
| 609 | memcpy(rowbuf, buffer + i*bpr, bpr); |
|
| 610 | memcpy(buffer + i*bpr, buffer + (480-i-1)*bpr, bpr); |
|
| 611 | memcpy(buffer + (480-i-1)*bpr, rowbuf, bpr); |
|
| 612 | } |
|
| 613 | |
|
| 614 | free(rowbuf); |
|
| 615 | |
|
| 616 | return data; |
|
| 617 | } |
|
| 618 | ||
| 619 | - (void) glGrabPoint |
|
| 620 | { |
|
| 621 | if (!commsSocket) |
|
| 622 | return; |
|
| 623 | ||
| 624 | NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; |
|
| 625 | if (lastGlUpdate + desiredFrameInterval < now) |
|
| 626 | { |
|
| 627 | NSData* rawData = dataFromGL(); |
|
| 628 | UIImage* image = imageFromGLData(rawData); |
|
| 629 | |
|
| 630 | NSData* data = UIImagePNGRepresentation(image); |
|
| 631 | |
|
| 632 | [self sendData: data]; |
|
| 633 | |
|
| 634 | lastGlUpdate = now; |
|
| 635 | } |
|
| 636 | } |
|
| 637 | ||
| 638 | + (void) glGrabPoint |
|
| 639 | { |
|
| 640 | [[self sharedServer] glGrabPoint]; |
|
| 641 | } |
|
| 642 | ||
| 643 | + (id) sharedServer |
|
| 644 | { |
|
| 645 | static id sharedInstance = nil; |
|
| 646 | if (!sharedInstance) |
|
| 647 | sharedInstance = [[TappityServer alloc] init]; |
|
| 648 | |
|
| 649 | |
|
| 650 | return sharedInstance; |
|
| 651 | } |
|
| 652 | ||
| 653 | 46 | - (void) dealloc |
| 654 | { |
|
| 655 | 48 | [serviceName release]; |
| 656 | [captureTimer invalidate]; |
|
| 657 | [capturedImagesCondition release]; |
|
| 658 | [capturedImagesQueue release]; |
|
| 659 | ||
| 660 | 46 | [netService stop]; |
| 661 | [netService release]; |
|
| 662 | ||
| 663 | [activeThreads release]; |
|
| 664 | ||
| 665 | [super dealloc]; |
|
| 666 | } |
|
| 667 | ||
| 668 | 49 | @synthesize locationDelegate, headingDelegate; |
| 669 | ||
| 670 | 46 | @end |
Loggerhead 1.17 is a web-based interface for Bazaar branches