Permalink
Browse files

Added example applications

  • Loading branch information...
1 parent 1337626 commit e3eac02e35c42d8f0f497dd4ecbd2714664991a4 @rsms committed Mar 31, 2012
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
Binary file not shown.
@@ -0,0 +1,13 @@
+#import "PTChannel.h"
+
+static const NSTimeInterval PTAppReconnectDelay = 1.0;
+
+@interface PTAppDelegate : NSObject <NSApplicationDelegate, PTChannelDelegate>
+
+@property (assign) IBOutlet NSWindow *window;
+@property (assign) IBOutlet NSTextField *inputTextField;
+@property (assign) IBOutlet NSTextView *outputTextView;
+
+- (IBAction)sendMessage:(id)sender;
+
+@end
@@ -0,0 +1,293 @@
+#import "PTAppDelegate.h"
+#import "PTUSBHub.h"
+#import "PTExampleProtocol.h"
+#import <QuartzCore/QuartzCore.h>
+
+@interface PTAppDelegate () {
+ // If the remote connection is over USB transport...
+ NSNumber *connectingToDeviceID_;
+ NSNumber *connectedDeviceID_;
+ NSDictionary *connectedDeviceProperties_;
+ NSDictionary *remoteDeviceInfo_;
+ dispatch_queue_t notConnectedQueue_;
+ BOOL notConnectedQueueSuspended_;
+ PTChannel *connectedChannel_;
+ NSDictionary *consoleTextAttributes_;
+ NSDictionary *consoleStatusTextAttributes_;
+}
+
+@property (readonly) NSNumber *connectedDeviceID;
+@property PTChannel *connectedChannel;
+
+- (void)presentMessage:(NSString*)message isStatus:(BOOL)isStatus;
+- (void)startListeningForDevices;
+- (void)didDisconnectFromDevice:(NSNumber*)deviceID;
+- (void)disconnectFromCurrentChannel;
+- (void)enqueueConnectToLocalIPv4Port;
+- (void)connectToLocalIPv4Port;
+- (void)connectToUSBDevice;
+
+@end
+
+
+@implementation PTAppDelegate
+
+@synthesize window = window_;
+@synthesize inputTextField = inputTextField_;
+@synthesize outputTextView = outputTextView_;
+@synthesize connectedDeviceID = connectedDeviceID_;
+
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // We use a serial queue that we toggle depending on if we are connected or
+ // not. When we are not connected to a peer, the queue is running to handle
+ // "connect" tries. When we are connected to a peer, the queue is suspended
+ // thus no longer trying to connect.
+ notConnectedQueue_ = dispatch_queue_create("PTExample.notConnectedQueue", DISPATCH_QUEUE_SERIAL);
+
+ // Configure the output NSTextView we use for UI feedback
+ outputTextView_.textContainerInset = NSMakeSize(15.0, 10.0);
+ consoleTextAttributes_ = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSFont fontWithName:@"helvetica" size:16.0], NSFontAttributeName,
+ [NSColor lightGrayColor], NSForegroundColorAttributeName,
+ nil];
+ consoleStatusTextAttributes_ = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSFont fontWithName:@"menlo" size:11.0], NSFontAttributeName,
+ [NSColor darkGrayColor], NSForegroundColorAttributeName,
+ nil];
+
+ // Configure the input NSTextField we use for UI input
+ [inputTextField_ setFont:[NSFont fontWithDescriptor:[[consoleTextAttributes_ objectForKey:NSFontAttributeName] fontDescriptor] size:14.0]];
+ [self.window makeFirstResponder:inputTextField_];
+
+ // Start listening for device attached/detached notifications
+ [self startListeningForDevices];
+
+ // Start trying to connect to local IPv4 port (defined in PTExampleProtocol.h)
+ [self enqueueConnectToLocalIPv4Port];
+
+ // Put a little message in the UI
+ [self presentMessage:@"Ready for action — connecting at will." isStatus:YES];
+}
+
+
+- (IBAction)sendMessage:(id)sender {
+ if (connectedChannel_) {
+ NSString *message = self.inputTextField.stringValue;
+ dispatch_data_t payload = [[message dataUsingEncoding:NSUTF8StringEncoding] createReferencingDispatchData];
+ [connectedChannel_ sendFrameOfType:PTExampleFrameTypeTextMessage tag:PTFrameNoTag withPayload:payload callback:^(NSError *error) {
+ if (error) {
+ NSLog(@"Failed to send message: %@", error);
+ }
+ }];
+ [self presentMessage:[NSString stringWithFormat:@"[you]: %@", message] isStatus:NO];
+ self.inputTextField.stringValue = @"";
+ }
+}
+
+
+- (void)presentMessage:(NSString*)message isStatus:(BOOL)isStatus {
+ NSLog(@">> %@", message);
+ [self.outputTextView.textStorage beginEditing];
+ if (self.outputTextView.textStorage.length > 0) {
+ message = [@"\n" stringByAppendingString:message];
+ }
+ [self.outputTextView.textStorage appendAttributedString:[[NSAttributedString alloc] initWithString:message attributes:isStatus ? consoleStatusTextAttributes_ : consoleTextAttributes_]];
+ [self.outputTextView.textStorage endEditing];
+
+ [NSAnimationContext beginGrouping];
+ [NSAnimationContext currentContext].duration = 0.15;
+ [NSAnimationContext currentContext].timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ NSClipView* clipView = [[self.outputTextView enclosingScrollView] contentView];
+ NSPoint newOrigin = clipView.bounds.origin;
+ newOrigin.y += 5.0; // hack A 1/2
+ [clipView setBoundsOrigin:newOrigin]; // hack A 2/2
+ newOrigin.y += 1000.0;
+ newOrigin = [clipView constrainScrollPoint:newOrigin];
+ [clipView.animator setBoundsOrigin:newOrigin];
+ [NSAnimationContext endGrouping];
+
+ // Scrolling w/o animation:
+ //[self.outputTextView scrollToEndOfDocument:self];
+}
+
+
+- (PTChannel*)connectedChannel {
+ return connectedChannel_;
+}
+
+- (void)setConnectedChannel:(PTChannel*)connectedChannel {
+ connectedChannel_ = connectedChannel;
+
+ // Toggle the notConnectedQueue_ depending on if we are connected or not
+ if (!connectedChannel_ && notConnectedQueueSuspended_) {
+ dispatch_resume(notConnectedQueue_);
+ notConnectedQueueSuspended_ = NO;
+ } else if (connectedChannel_ && !notConnectedQueueSuspended_) {
+ dispatch_suspend(notConnectedQueue_);
+ notConnectedQueueSuspended_ = YES;
+ }
+
+ if (!connectedChannel_ && connectingToDeviceID_) {
+ [self enqueueConnectToUSBDevice];
+ }
+}
+
+
+#pragma mark - PTChannelDelegate
+
+
+- (BOOL)ioFrameChannel:(PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {
+ if ( type != PTExampleFrameTypeDeviceInfo
+ && type != PTExampleFrameTypeTextMessage
+ && type != PTFrameTypeEndOfStream) {
+ NSLog(@"Unexpected frame of type %u", type);
+ [channel close];
+ return NO;
+ } else {
+ return YES;
+ }
+}
+
+- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(PTData*)payload {
+ //NSLog(@"received %@, %u, %u, %@", channel, type, tag, payload);
+ if (type == PTExampleFrameTypeDeviceInfo) {
+ NSDictionary *deviceInfo = [NSDictionary dictionaryWithContentsOfDispatchData:payload.dispatchData];
+ [self presentMessage:[NSString stringWithFormat:@"Connected to %@", deviceInfo.description] isStatus:YES];
+ } else if (type == PTExampleFrameTypeTextMessage) {
+ NSString *message = [[NSString alloc] initWithBytes:payload.data length:payload.length encoding:NSUTF8StringEncoding];
+ [self presentMessage:[NSString stringWithFormat:@"[%@]: %@", channel.userInfo, message] isStatus:NO];
+ }
+}
+
+- (void)ioFrameChannel:(PTChannel*)channel didEndWithError:(NSError*)error {
+ if (connectedDeviceID_ && [connectedDeviceID_ isEqualToNumber:channel.userInfo]) {
+ [self didDisconnectFromDevice:connectedDeviceID_];
+ }
+
+ if (connectedChannel_ == channel) {
+ [self presentMessage:[NSString stringWithFormat:@"Disconnected from %@", channel.userInfo] isStatus:YES];
+ self.connectedChannel = nil;
+ }
+}
+
+
+#pragma mark - Wired device connections
+
+
+- (void)startListeningForDevices {
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ [nc addObserverForName:PTUSBDeviceDidAttachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) {
+ NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"];
+ //NSLog(@"PTUSBDeviceDidAttachNotification: %@", note.userInfo);
+ NSLog(@"PTUSBDeviceDidAttachNotification: %@", deviceID);
+
+ dispatch_async(notConnectedQueue_, ^{
+ if (!connectingToDeviceID_ || ![deviceID isEqualToNumber:connectingToDeviceID_]) {
+ [self disconnectFromCurrentChannel];
+ connectingToDeviceID_ = deviceID;
+ connectedDeviceProperties_ = [note.userInfo objectForKey:@"Properties"];
+ [self enqueueConnectToUSBDevice];
+ }
+ });
+ }];
+
+ [nc addObserverForName:PTUSBDeviceDidDetachNotification object:PTUSBHub.sharedHub queue:nil usingBlock:^(NSNotification *note) {
+ NSNumber *deviceID = [note.userInfo objectForKey:@"DeviceID"];
+ //NSLog(@"PTUSBDeviceDidDetachNotification: %@", note.userInfo);
+ NSLog(@"PTUSBDeviceDidDetachNotification: %@", deviceID);
+
+ if ([connectingToDeviceID_ isEqualToNumber:deviceID]) {
+ connectedDeviceProperties_ = nil;
+ connectingToDeviceID_ = nil;
+ if (connectedChannel_) {
+ [connectedChannel_ close];
+ }
+ }
+ }];
+}
+
+
+- (void)didDisconnectFromDevice:(NSNumber*)deviceID {
+ NSLog(@"Disconnected from device");
+ if ([connectedDeviceID_ isEqualToNumber:deviceID]) {
+ [self willChangeValueForKey:@"connectedDeviceID"];
+ connectedDeviceID_ = nil;
+ [self didChangeValueForKey:@"connectedDeviceID"];
+ }
+}
+
+
+- (void)disconnectFromCurrentChannel {
+ if (connectedDeviceID_ && connectedChannel_) {
+ [connectedChannel_ close];
+ self.connectedChannel = nil;
+ }
+}
+
+
+- (void)enqueueConnectToLocalIPv4Port {
+ dispatch_async(notConnectedQueue_, ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self connectToLocalIPv4Port];
+ });
+ });
+}
+
+
+- (void)connectToLocalIPv4Port {
+ PTChannel *channel = [PTChannel channelWithDelegate:self];
+ channel.userInfo = [NSString stringWithFormat:@"127.0.0.1:%d", PTExampleProtocolIPv4PortNumber];
+ [channel connectToPort:PTExampleProtocolIPv4PortNumber IPv4Address:INADDR_LOOPBACK callback:^(NSError *error, PTAddress *address) {
+ if (error) {
+ if (error.domain == NSPOSIXErrorDomain && (error.code == ECONNREFUSED || error.code == ETIMEDOUT)) {
+ // this is an expected state
+ } else {
+ NSLog(@"Failed to connect to 127.0.0.1:%d: %@", PTExampleProtocolIPv4PortNumber, error);
+ }
+ } else {
+ [self disconnectFromCurrentChannel];
+ self.connectedChannel = channel;
+ channel.userInfo = address;
+ NSLog(@"Connected to %@", address);
+ }
+ [self performSelector:@selector(enqueueConnectToLocalIPv4Port) withObject:nil afterDelay:PTAppReconnectDelay];
+ }];
+}
+
+
+- (void)enqueueConnectToUSBDevice {
+ dispatch_async(notConnectedQueue_, ^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self connectToUSBDevice];
+ });
+ });
+}
+
+
+- (void)connectToUSBDevice {
+ PTChannel *channel = [PTChannel channelWithDelegate:self];
+ channel.userInfo = connectingToDeviceID_;
+ channel.delegate = self;
+
+ [channel connectToPort:PTExampleProtocolIPv4PortNumber overUSBHub:PTUSBHub.sharedHub deviceID:connectingToDeviceID_ callback:^(NSError *error) {
+ if (error) {
+ if (error.domain == PTUSBHubErrorDomain && error.code == PTUSBHubErrorConnectionRefused) {
+ NSLog(@"Failed to connect to device #%@: %@", channel.userInfo, error);
+ } else {
+ NSLog(@"Failed to connect to device #%@: %@", channel.userInfo, error);
+ }
+ if (channel.userInfo == connectingToDeviceID_) {
+ [self performSelector:@selector(enqueueConnectToUSBDevice) withObject:nil afterDelay:PTAppReconnectDelay];
+ }
+ } else {
+ connectedDeviceID_ = connectingToDeviceID_;
+ self.connectedChannel = channel;
+ //NSLog(@"Connected to device #%@\n%@", connectingToDeviceID_, connectedDeviceProperties_);
+ //infoTextField_.stringValue = [NSString stringWithFormat:@"Connected to device #%@\n%@", deviceID, connectedDeviceProperties_];
+ }
+ }];
+}
+
+@end
@@ -0,0 +1,19 @@
+#ifndef peertalk_PTExampleProtocol_h
+#define peertalk_PTExampleProtocol_h
+
+#import <Foundation/Foundation.h>
+#include <stdint.h>
+
+static const int PTExampleProtocolIPv4PortNumber = 2345;
+
+enum {
+ PTExampleFrameTypeDeviceInfo = 100,
+ PTExampleFrameTypeTextMessage = 101,
+};
+
+typedef struct _PTExampleTextFrame {
+ const uint8_t *utf8text;
+} PTExampleTextFrame;
+
+
+#endif
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>Icon</string>
+ <key>CFBundleIdentifier</key>
+ <string>me.rsms.peertalk.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2012 __MyCompanyName__. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'Peertalk Example' target in the 'Peertalk Example' project
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
Oops, something went wrong.

0 comments on commit e3eac02

Please sign in to comment.