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 | ||
| 12 | ||
| 13 | static void _setStandardSocketOpts(int socket) |
|
| 14 | { |
|
| 15 | int yes = 1; |
|
| 16 | setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); |
|
| 17 | int timeout = 2000; |
|
| 18 | setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); |
|
| 19 | setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); |
|
| 20 | } |
|
| 21 | ||
| 22 | @interface UIApplication (TappityPrivate) |
|
| 23 | - (void)_playbackEvents:(id)arg1 atPlaybackRate:(float)arg2 messageWhenDone:(id)arg3 withSelector:(SEL)arg4; |
|
| 24 | @end |
|
| 25 | ||
| 26 | @implementation TappityServer |
|
| 27 | ||
| 28 | - (BOOL) threadActive: (id) key |
|
| 29 | { |
|
| 30 | return [[activeThreads objectForKey: key] boolValue]; |
|
| 31 | } |
|
| 32 | ||
| 33 | - (void) receivingThread: (id) info |
|
| 34 | { |
|
| 35 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 36 | ||
| 37 | @synchronized(self) |
|
| 38 | { |
|
| 39 | [self retain]; |
|
| 40 | } |
|
| 41 | ||
| 42 | while ([self threadActive: info]) |
|
| 43 | { |
|
| 44 | struct timeval tv; |
|
| 45 | fd_set readfds; |
|
| 46 | fd_set writefds; |
|
| 47 | fd_set errorfds; |
|
| 48 | int maxSocket = -1; |
|
| 49 | ||
| 50 | tv.tv_sec = 1; |
|
| 51 | tv.tv_usec = 0; |
|
| 52 | ||
| 53 | FD_ZERO(&readfds); |
|
| 54 | FD_ZERO(&writefds); |
|
| 55 | FD_ZERO(&errorfds); |
|
| 56 | ||
| 57 | FD_SET(commsSocket, &readfds); |
|
| 58 | FD_SET(commsSocket, &errorfds); |
|
| 59 | maxSocket = MAX(maxSocket, commsSocket); |
|
| 60 | |
|
| 61 | if (maxSocket < 0) |
|
| 62 | { |
|
| 63 | //printf("No sockets, sleeping for a bit...\n"); |
|
| 64 | usleep(1000000); |
|
| 65 | continue; |
|
| 66 | } |
|
| 67 | ||
| 68 | if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0) |
|
| 69 | { |
|
| 70 | perror("select"); |
|
| 71 | break; |
|
| 72 | } |
|
| 73 | ||
| 74 | if (FD_ISSET(commsSocket, &readfds)) |
|
| 75 | { |
|
| 76 | NSLog(@"receiving..."); |
|
| 77 | if (!expectedMessageSize) |
|
| 78 | { |
|
| 79 | uint32_t buf = 0; |
|
| 80 | int actuallyRead = 0; |
|
| 81 | |
|
| 82 | actuallyRead = recv(commsSocket, &buf, sizeof(uint32_t), MSG_PEEK); |
|
| 83 | |
|
| 84 | if (actuallyRead == sizeof(uint32_t)) |
|
| 85 | { |
|
| 86 | actuallyRead = recv(commsSocket, &buf, sizeof(uint32_t), 0); |
|
| 87 | expectedMessageSize = ntohl(buf); |
|
| 88 | currentData = [[NSMutableData alloc] initWithLength: expectedMessageSize]; |
|
| 89 | } |
|
| 90 | else if (!actuallyRead) |
|
| 91 | { |
|
| 92 | // close this socket as nothing read means connection closed |
|
| 93 | close(commsSocket); |
|
| 94 | self->commsSocket = 0; |
|
| 95 | printf("Connection dropped.\n"); |
|
| 96 | break; |
|
| 97 | } |
|
| 98 | if (actuallyRead == -1) |
|
| 99 | { |
|
| 100 | close(commsSocket); |
|
| 101 | self->commsSocket = 0; |
|
| 102 | printf("Connection dropped with error.\n"); |
|
| 103 | break; |
|
| 104 | } |
|
| 105 | } |
|
| 106 | else |
|
| 107 | { |
|
| 108 | size_t readAmount = expectedMessageSize - currentlyRead; |
|
| 109 | int actuallyRead = 0; |
|
| 110 | |
|
| 111 | actuallyRead = recv(commsSocket, [currentData mutableBytes] + currentlyRead, readAmount, 0); |
|
| 112 | if (actuallyRead == -1) |
|
| 113 | { |
|
| 114 | close(commsSocket); |
|
| 115 | self->commsSocket = 0; |
|
| 116 | printf("Connection dropped with error.\n"); |
|
| 117 | break; |
|
| 118 | } |
|
| 119 | currentlyRead += actuallyRead; |
|
| 120 | |
|
| 121 | if (currentlyRead == expectedMessageSize) |
|
| 122 | { |
|
| 123 | @synchronized(self) |
|
| 124 | { |
|
| 125 | expectedMessageSize = 0; |
|
| 126 | /* |
|
| 127 | if (!receivedPackets) |
|
| 128 | receivedPackets = [[NSMutableArray alloc] init]; |
|
| 129 | [receivedPackets addObject: currentData]; |
|
| 130 | [currentData release]; |
|
| 131 | */ |
|
| 132 | |
|
| 133 | } |
|
| 134 | [self performSelectorOnMainThread: @selector(tapReceived:) withObject: currentData waitUntilDone: NO]; |
|
| 135 | currentData = nil; |
|
| 136 | currentlyRead = 0; |
|
| 137 | } |
|
| 138 | } |
|
| 139 | ||
| 140 | } |
|
| 141 | } |
|
| 142 | ||
| 143 | @synchronized(self) |
|
| 144 | { |
|
| 145 | [self release]; |
|
| 146 | } |
|
| 147 | ||
| 148 | [info release]; |
|
| 149 | [pool drain]; |
|
| 150 | } |
|
| 151 | ||
| 152 | ||
| 153 | - (void) runThreadWithSelector: (SEL) selector |
|
| 154 | { |
|
| 155 | @synchronized(self) |
|
| 156 | { |
|
| 157 | if (!activeThreads) |
|
| 158 | activeThreads = [[NSMutableDictionary alloc] init]; |
|
| 159 | id number = [NSNumber numberWithInt: threadIds++]; |
|
| 160 | ||
| 161 | [activeThreads setObject: [NSNumber numberWithBool: YES] forKey: number]; |
|
| 162 | [NSThread detachNewThreadSelector: selector toTarget: self withObject: [number retain]]; |
|
| 163 | } |
|
| 164 | } |
|
| 165 | ||
| 166 | - (void) acceptThread: (id) info |
|
| 167 | { |
|
| 168 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
|
| 169 | ||
| 170 | @synchronized(self) |
|
| 171 | { |
|
| 172 | [self retain]; |
|
| 173 | } |
|
| 174 | ||
| 175 | while ([self threadActive: info]) |
|
| 176 | { |
|
| 177 | ||
| 178 | struct timeval tv; |
|
| 179 | fd_set readfds; |
|
| 180 | fd_set writefds; |
|
| 181 | fd_set errorfds; |
|
| 182 | int maxSocket = -1; |
|
| 183 | ||
| 184 | tv.tv_sec = 1; |
|
| 185 | tv.tv_usec = 0; |
|
| 186 | ||
| 187 | FD_ZERO(&readfds); |
|
| 188 | FD_ZERO(&writefds); |
|
| 189 | FD_ZERO(&errorfds); |
|
| 190 | ||
| 191 | FD_SET(listenSocket, &readfds); |
|
| 192 | FD_SET(listenSocket, &errorfds); |
|
| 193 | maxSocket = MAX(maxSocket, listenSocket); |
|
| 194 | |
|
| 195 | if (maxSocket < 0) |
|
| 196 | { |
|
| 197 | printf("No sockets, sleeping for a bit...\n"); |
|
| 198 | usleep(1000000); |
|
| 199 | continue; |
|
| 200 | } |
|
| 201 | |
|
| 202 | //NSLog(@"listening for connection..."); |
|
| 203 | ||
| 204 | if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0) |
|
| 205 | { |
|
| 206 | perror("select"); |
|
| 207 | break; |
|
| 208 | } |
|
| 209 | ||
| 210 | if (FD_ISSET(listenSocket, &readfds)) |
|
| 211 | { |
|
| 212 | NSLog(@"Accepting connection..."); |
|
| 213 | // accept |
|
| 214 | socklen_t sinSize = sizeof(struct sockaddr_in); |
|
| 215 | struct sockaddr_in peerAddress; |
|
| 216 | int newSocket = accept(listenSocket, (struct sockaddr *)&peerAddress, &sinSize); |
|
| 217 | |
|
| 218 | _setStandardSocketOpts(newSocket); |
|
| 219 | ||
| 220 | |
|
| 221 | if (newSocket == -1) |
|
| 222 | { |
|
| 223 | if (errno == EWOULDBLOCK) |
|
| 224 | { |
|
| 225 | } |
|
| 226 | else |
|
| 227 | { |
|
| 228 | printf("Error accepting connection.\n"); |
|
| 229 | break; |
|
| 230 | } |
|
| 231 | } |
|
| 232 | |
|
| 233 | if (commsSocket) |
|
| 234 | close(commsSocket); |
|
| 235 | ||
| 236 | // add socket to sockets list |
|
| 237 | commsSocket = newSocket; |
|
| 238 | |
|
| 239 | |
|
| 240 | [self runThreadWithSelector: @selector(receivingThread:)]; |
|
| 241 | |
|
| 242 | NSLog(@"Accepted connection."); |
|
| 243 | } |
|
| 244 | ||
| 245 | } |
|
| 246 | ||
| 247 | @synchronized(self) |
|
| 248 | { |
|
| 249 | [self release]; |
|
| 250 | } |
|
| 251 | ||
| 252 | [info release]; |
|
| 253 | [pool drain]; |
|
| 254 | } |
|
| 255 | ||
| 256 | - (BOOL) enableBonjourWithDomain:(NSString*)domain applicationProtocol:(NSString*)protocol name:(NSString*)name |
|
| 257 | { |
|
| 258 | if(![domain length]) |
|
| 259 | domain = @""; //Will use default Bonjour registration doamins, typically just ".local" |
|
| 260 | if(![name length]) |
|
| 261 | name = @""; //Will use default Bonjour name, e.g. the name assigned to the device in iTunes |
|
| 262 | |
|
| 263 | assert([protocol length] && listenSocket); |
|
| 264 | |
|
| 265 | NSLog(@"tappity port: %d", ntohs(myAddress.sin_port)); |
|
| 266 | ||
| 267 | netService = [[NSNetService alloc] initWithDomain: domain type: protocol name: name port: ntohs(myAddress.sin_port)]; |
|
| 268 | if(netService == nil) |
|
| 269 | return NO; |
|
| 270 | |
|
| 271 | [netService setDelegate:self]; |
|
| 272 | // [netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; |
|
| 273 | [netService publish]; |
|
| 274 | |
|
| 275 | return YES; |
|
| 276 | } |
|
| 277 | ||
| 278 | - (void) start |
|
| 279 | { |
|
| 280 | NSLog(@"starting tappity server"); |
|
| 281 | myAddress.sin_family = AF_INET; // host byte order |
|
| 282 | myAddress.sin_port = htons(1234); // short, network byte order, any port |
|
| 283 | myAddress.sin_addr.s_addr = htonl(INADDR_ANY); // auto-fill with my IP |
|
| 284 | ||
| 285 | listenSocket = socket(PF_INET, SOCK_STREAM, 0); |
|
| 286 | assert(listenSocket != -1); |
|
| 287 | ||
| 288 | _setStandardSocketOpts(listenSocket); |
|
| 289 | ||
| 290 | int err = bind(self->listenSocket, (struct sockaddr *)&myAddress, sizeof(myAddress)); |
|
| 291 | assert(-1 != err); |
|
| 292 | |
|
| 293 | err = listen(self->listenSocket, 1); |
|
| 294 | assert(-1 != err); |
|
| 295 | ||
| 296 | [self runThreadWithSelector: @selector(acceptThread:)]; |
|
| 297 | |
|
| 298 | if([self enableBonjourWithDomain: @"" applicationProtocol: @"_tappity._tcp" name: @"tappity"]) |
|
| 299 | NSLog(@"tappity bounjour advertisments up and running"); |
|
| 300 | ||
| 301 | } |
|
| 302 | ||
| 303 | static size_t _read_i8(const void* buf, uint8_t* p) |
|
| 304 | { |
|
| 305 | uint8_t v = 0; |
|
| 306 | memcpy(&v, buf, 1); |
|
| 307 | *p = v; |
|
| 308 | return 1; |
|
| 309 | } |
|
| 310 | static size_t _read_i16(const void* buf, uint16_t* p) |
|
| 311 | { |
|
| 312 | uint16_t v = 0; |
|
| 313 | memcpy(&v, buf, 2); |
|
| 314 | *p = CFSwapInt16BigToHost(v); |
|
| 315 | return 2; |
|
| 316 | } |
|
| 317 | static size_t _read_i32(const void* buf, uint32_t* p) |
|
| 318 | { |
|
| 319 | uint32_t v = 0; |
|
| 320 | memcpy(&v, buf, 4); |
|
| 321 | *p = CFSwapInt32BigToHost(v); |
|
| 322 | return 4; |
|
| 323 | } |
|
| 324 | static size_t _read_i64(const void* buf, uint64_t* p) |
|
| 325 | { |
|
| 326 | uint64_t v = 0; |
|
| 327 | memcpy(&v, buf, 8); |
|
| 328 | *p = CFSwapInt64BigToHost(v); |
|
| 329 | return 8; |
|
| 330 | } |
|
| 331 | static size_t _read_float(const void* buf, float* p) |
|
| 332 | { |
|
| 333 | CFSwappedFloat32 v; |
|
| 334 | memcpy(&v, buf, 4); |
|
| 335 | *p = CFConvertFloat32SwappedToHost(v); |
|
| 336 | return 4; |
|
| 337 | } |
|
| 338 | ||
| 339 | static size_t _read_gspoint(const void* buf, GSTouchPointRef p) |
|
| 340 | { |
|
| 341 | size_t bc = 0; |
|
| 342 | ||
| 343 | bc += _read_i32(buf+bc, &p->unk0); |
|
| 344 | bc += _read_float(buf+bc, &p->unk1); |
|
| 345 | bc += _read_float(buf+bc, &p->touchSize); |
|
| 346 | bc += _read_float(buf+bc, &p->x); |
|
| 347 | bc += _read_float(buf+bc, &p->y); |
|
| 348 | bc += _read_i32(buf+bc, (uint32_t*)&p->window); |
|
| 349 | |
|
| 350 | return bc; |
|
| 351 | } |
|
| 352 | ||
| 353 | - (GSEvent*) gsEventWithData: (NSData*) data |
|
| 354 | { |
|
| 355 | GSEvent* gse = [[[GSEvent alloc] init] autorelease]; |
|
| 356 | |
|
| 357 | size_t bc = 0; |
|
| 358 | const void* buf = [data bytes]; |
|
| 359 | |
|
| 360 | bc += _read_i32(buf+bc, &gse->type0); |
|
| 361 | bc += _read_i32(buf+bc, &gse->type1); |
|
| 362 | bc += _read_i32(buf+bc, &gse->r3); |
|
| 363 | bc += _read_float(buf+bc, &gse->avgX0); |
|
| 364 | bc += _read_float(buf+bc, &gse->avgY0); |
|
| 365 | bc += _read_float(buf+bc, &gse->avgX1); |
|
| 366 | bc += _read_float(buf+bc, &gse->avgY1); |
|
| 367 | bc += _read_i32(buf+bc, &gse->processId); |
|
| 368 | bc += _read_i64(buf+bc, &gse->timestamp); |
|
| 369 | bc += _read_i32(buf+bc, (uint32_t*)&gse->window); |
|
| 370 | bc += _read_i32(buf+bc, &gse->r11); |
|
| 371 | bc += _read_i32(buf+bc, &gse->type12); |
|
| 372 | bc += _read_i32(buf+bc, &gse->gesture13); |
|
| 373 | bc += _read_i32(buf+bc, &gse->gesture14); |
|
| 374 | bc += _read_i16(buf+bc, &gse->numInitialTouches); |
|
| 375 | bc += _read_i16(buf+bc, &gse->numCurrentTouches); |
|
| 376 | bc += _read_i32(buf+bc, &gse->r16); |
|
| 377 | bc += _read_i32(buf+bc, &gse->r17); |
|
| 378 | bc += _read_i32(buf+bc, &gse->r18); |
|
| 379 | bc += _read_i32(buf+bc, &gse->r19); |
|
| 380 | bc += _read_i32(buf+bc, &gse->r20); |
|
| 381 | bc += _read_i32(buf+bc, &gse->r21); |
|
| 382 | bc += _read_i8(buf+bc, &gse->r22_0); |
|
| 383 | bc += _read_i8(buf+bc, &gse->numPoints); |
|
| 384 | bc += _read_i16(buf+bc, &gse->r22_2); |
|
| 385 | for (size_t i = 0; i < gse->numPoints; ++i) |
|
| 386 | bc += _read_gspoint(buf+bc, gse->points + i); |
|
| 387 | ||
| 388 | return gse; |
|
| 389 | } |
|
| 390 | ||
| 391 | - (void) tapReceived: (NSData*) tapData |
|
| 392 | { |
|
| 393 | assert([tapData length] > 4); |
|
| 394 | |
|
| 395 | |
|
| 396 | NSDictionary* tapDict = [NSPropertyListSerialization propertyListFromData: tapData mutabilityOption:NSPropertyListImmutable format: NULL errorDescription: NULL]; |
|
| 397 | assert(tapDict); |
|
| 398 | switch([[tapDict objectForKey: TapIdKey] intValue]) |
|
| 399 | { |
|
| 400 | case kTapTouchEvent: |
|
| 401 | { |
|
| 402 | NSLog(@"TOUCH EVENT"); |
|
| 403 | |
|
| 404 | GSEvent* gse = [self gsEventWithData: [tapDict objectForKey: TapGSEventKey]]; |
|
| 405 | UIWindow* window = [[UIApplication sharedApplication] keyWindow]; |
|
| 406 | gse->window = window; |
|
| 407 | for (size_t i = 0; i < gse->numPoints; ++i) |
|
| 408 | gse->points[i].window = window; |
|
| 409 | |
|
| 410 | int phase = [[tapDict objectForKey: TapTouchPhaseKey] intValue]; |
|
| 411 | NSSet* touches = [UITouch _createTouchesWithGSEvent: gse phase: phase view: [[UIApplication sharedApplication] keyWindow]]; |
|
| 412 | |
|
| 413 | assert([[UIApplication sharedApplication] keyWindow]); |
|
| 414 | |
|
| 415 | NSLog(@"%@", touches); |
|
| 416 | ||
| 417 | UIEvent* event = [UIEvent alloc]; |
|
| 418 | |
|
| 419 | Class touchesEventClass = objc_getClass("UITouchesEvent"); |
|
| 420 | if (touchesEventClass && ![[event class] isEqual: touchesEventClass]) |
|
| 421 | { |
|
| 422 | event = [touchesEventClass alloc]; |
|
| 423 | } |
|
| 424 | |
|
| 425 | [event _initWithEvent: gse touches: touches]; |
|
| 426 | |
|
| 427 | assert(event); |
|
| 428 | |
|
| 429 | [[UIApplication sharedApplication] sendEvent: event]; |
|
| 430 | |
|
| 431 | |
|
| 432 | break; |
|
| 433 | } |
|
| 434 | case kTapRecordedEvent: |
|
| 435 | { |
|
| 436 | NSLog(@"RECORDED EVENT"); |
|
| 437 | |
|
| 438 | NSDictionary* eventDict = [tapDict objectForKey: TapRecordedEventKey]; |
|
| 439 | |
|
| 440 | [[UIApplication sharedApplication] _playbackEvents:[NSArray arrayWithObject: eventDict] atPlaybackRate: 1.0f messageWhenDone: nil withSelector: nil]; |
|
| 441 | ||
| 442 | break; |
|
| 443 | } |
|
| 444 | } |
|
| 445 | |
|
| 446 | } |
|
| 447 | ||
| 448 | ||
| 449 | /* |
|
| 450 | - (void)performTouches: (NSSet*) touches inView: (UIView *)view |
|
| 451 | { |
|
| 452 | UITouch *touch = [[UITouch alloc] initInView:view]; |
|
| 453 | UIEvent *eventDown = [[UIEvent alloc] initWithTouch:touch]; |
|
| 454 | |
|
| 455 | [touch.view touchesBegan:[eventDown allTouches] withEvent:eventDown]; |
|
| 456 | |
|
| 457 | [touch setPhase:UITouchPhaseEnded]; |
|
| 458 | UIEvent *eventUp = [[UIEvent alloc] initWithTouch:touch]; |
|
| 459 | |
|
| 460 | [touch.view touchesEnded:[eventUp allTouches] withEvent:eventUp]; |
|
| 461 | |
|
| 462 | [eventDown release]; |
|
| 463 | [eventUp release]; |
|
| 464 | [touch release]; |
|
| 465 | } |
|
| 466 | */ |
|
| 467 | ||
| 468 | - (void) dealloc |
|
| 469 | { |
|
| 470 | |
|
| 471 | [netService stop]; |
|
| 472 | [netService release]; |
|
| 473 | ||
| 474 | [activeThreads release]; |
|
| 475 | ||
| 476 | [super dealloc]; |
|
| 477 | } |
|
| 478 | ||
| 479 | @end |
Loggerhead 1.17 is a web-based interface for Bazaar branches