Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / editors / scapp / RendezvousClient.M
blob143e1415e6eaf28e2bf79e5e39f64328501b7af0
1 //
2 // RendezvousClient.m
3 // SC3lang
4 //
5 // Created by C. Ramakrishnan on Mon Feb 24 2003.
6 // Copyright (c) 2003 __MyCompanyName__. All rights reserved.
7 //
9 /*
10 SuperCollider real time audio synthesis system
11 Copyright (c) 2002 James McCartney. All rights reserved.
12 http://www.audiosynth.com
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #import "RendezvousClient.h"
30 #import "SCBase.h"
32 // for the locking mechanism
33 #import <Foundation/NSLock.h>
35 // SC headers for primitives
36 #include "PyrPrimitive.h"
37 #include "PyrObject.h"
38 #include "PyrKernel.h"
39 #include "VMGlobals.h"
40 #import "GC.h"
42 // Networking headers
43 // struct sockaddr_in
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
51 // for gethostbyaddr
52 #include <netdb.h>
54 // DEBUG
55 #include <pthread.h>
57 @interface RendezvousClient (RendezvousClientNetServiceBrowserCallbacks)
58 // methods for callbacks from the NetServiceBrowser API
60 - (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)aNetServiceBrowser;
61 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing;
62 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict;
63 - (void)netBrowserDidStopSearch:(NSNetServiceBrowser *)aNetServiceBrowser;
65 - (void)netServiceWillResolve:(NSNetService *)sender;
66 - (void)netServiceDidResolveAddress:(NSNetService *)sender;
67 - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict;
69 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing;
71 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing;
73 @end
76 @interface RendezvousClient (RendezvousClientPrivate)
77 // private methods for internal use
78 - (void)resolveAllServices;
79 - (void)resolveNextService;
81 @end
84 void initRendezvousPrimitives();
86 static NSString* kOSCServiceTypeStrings[kNumOSCServiceTypes] = {
87 @"_osc._udp.",
88 @"_osc._tcp."
91 static RendezvousClient* sharedRendezvousClient = nil; // shared instance of the rendezvous client
92 static OSCService* serviceBeingResolved = nil; // placeholder for what I'm currently resolving
93 static unsigned indexOfServiceBeingResolved = 0; // which service am I currently resolving?
96 @implementation RendezvousClient
98 + (RendezvousClient*)sharedClient
100 if (nil == sharedRendezvousClient)
102 sharedRendezvousClient = [[RendezvousClient alloc] init];
105 return sharedRendezvousClient;
108 - (id)init
110 [super init];
112 for (int serviceType = 0; serviceType < kNumOSCServiceTypes; serviceType++) {
113 browsers[serviceType] = [[NSNetServiceBrowser alloc] init];
114 [browsers[serviceType] setDelegate: self];
116 // 10 elements is a reasonable starting point
117 oscServices = [[NSMutableArray alloc] initWithCapacity: 10];
119 return self;
122 - (void)findOSCServices
124 for (int serviceType = 0; serviceType < kNumOSCServiceTypes; serviceType++) {
125 [browsers[serviceType] searchForServicesOfType: kOSCServiceTypeStrings[serviceType] inDomain: @""];
129 - (unsigned)numberOfOSCServices
131 return [oscServices count];
134 - (OSCService*)oscServiceAtIndex:(unsigned)index
136 if ((index >= [oscServices count]))
137 return nil;
138 return [oscServices objectAtIndex: index];
141 - (void)resolveAllServices
143 // See the comment on re-entrancy in resolveNextService
145 // begin with the first service, and let them each hand off to the next
146 // (see the implementation of netServiceDidResolveAddress:)
147 if ([oscServices count] < 1)
148 return;
150 indexOfServiceBeingResolved = 0;
151 [self resolveNextService];
154 - (void)resolveNextService
156 // NOTE ON RE-ENTRANCY OF THIS CODE
157 // Theoretically, there should be locks between resolveAllServices, resolveNextService, and
158 // netServiceDidResolveAddress, but in practice it is not necessary because all these run on the
159 // same thread, so it is not possible, e.g., for resolveNextService to have simultaneous, interleaved
160 // calls. Thus, there is no need for locking.
162 // find something to resolve
163 for (; indexOfServiceBeingResolved < [oscServices count]; indexOfServiceBeingResolved++) {
164 serviceBeingResolved = [oscServices objectAtIndex: indexOfServiceBeingResolved];
165 if (!serviceBeingResolved->isResolved)
166 break;
169 // if we didn't find anything, we are done
170 if (!(indexOfServiceBeingResolved < [oscServices count])) {
171 // we're done
172 serviceBeingResolved = nil;
173 indexOfServiceBeingResolved = 0;
174 return;
177 // resolve it!
178 [serviceBeingResolved->netService setDelegate: self];
179 [serviceBeingResolved->netService resolveWithTimeout:5]; // Deprecated; deprecator auto uses use resolveWithTimeout 5, should probably shorten
182 // Rendezvous implementation methods
183 - (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)aNetServiceBrowser
185 // do nothing
188 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
190 if (nil == aNetService)
192 // I don't think this should happen...
193 return;
196 // see if it is a duplicate
197 unsigned arrayCount = [oscServices count];
198 unsigned i;
199 OSCService* potentialDuplicate;
200 for(i = 0; i < arrayCount; i++)
202 potentialDuplicate = [oscServices objectAtIndex: i];
203 if ([aNetService isEqual: potentialDuplicate->netService])
205 (potentialDuplicate->refCount)++;
206 return;
210 // allocate an OSCService object for this NSService
211 // (autorelease it because we are going to put it into an array)
212 OSCService* service = [[[OSCService alloc] init] autorelease];
213 service->netService = aNetService;
214 [service->netService retain];
215 service->refCount++;
216 service->isResolved = NO;
218 // add this to the list of known services
219 [oscServices addObject: service];
221 // if there's no more coming, then it's time to resolve all the services
222 if (!moreComing) {
223 [self resolveAllServices];
227 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict
229 post("Can't search for OSC Services\n");
232 - (void)netBrowserDidStopSearch:(NSNetServiceBrowser *)aNetServiceBrowser
234 // do nothing
237 - (void)netServiceWillResolve:(NSNetService *)sender
239 // do nothing
242 - (void)netServiceDidResolveAddress:(NSNetService *)sender
244 if (!serviceBeingResolved) {
245 // this is odd... I got a resolution I didn't ask for
246 post("Resolved OSC Service %s without requesting it.\n", [[sender name] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
247 [sender stop];
248 return;
251 // get the addresses
252 NSArray* addresses = [sender addresses];
253 if (nil == addresses)
254 return; // I don't think this should happen
256 // just pick one of them
257 NSData* address = [addresses lastObject];
258 if (nil == address)
259 return; // I don't think this should happen
261 const struct sockaddr_in* sockaddr = (const struct sockaddr_in*) [address bytes];
262 serviceBeingResolved->netService = sender;
263 serviceBeingResolved->sockaddr = sockaddr;
264 serviceBeingResolved->hostAddress = sockaddr->sin_addr.s_addr;
265 serviceBeingResolved->port = sockaddr->sin_port;
267 struct hostent* hostent = gethostbyaddr((char*) &(sockaddr->sin_addr), 4, AF_INET);
269 if (hostent)
270 serviceBeingResolved->hostName = [[NSString alloc] initWithCString: hostent->h_name encoding:NSASCIIStringEncoding];
271 else {
272 // Couldn't find a host name for the address.
273 // Convert the address to a string for the hostname
274 char* addrOctets = inet_ntoa(sockaddr->sin_addr);
275 serviceBeingResolved->hostName = [[NSString alloc] initWithCString: addrOctets encoding:NSASCIIStringEncoding];
277 serviceBeingResolved->isResolved = YES;
278 [sender stop];
280 [self resolveNextService];
283 - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
285 post("Could not resolve the address for a discovered OSC Service\n");
288 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing
290 // Ignore this -- I don't think it happens, as I never triger a removal of a domain
293 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
295 OSCService* service;
297 // remove this from the list of services
298 unsigned arrayCount = [oscServices count];
299 unsigned i;
300 for(i = 0; i < arrayCount; i++)
302 service = [oscServices objectAtIndex: i];
303 if ([aNetService isEqual: service->netService])
305 service->refCount--;
306 if (service->refCount < 1) {
307 [service->hostName release];
308 // removing it from the array will release the service object
309 // too
310 [oscServices removeObjectAtIndex: i];
312 return;
317 @end
319 @implementation OSCService
321 @end
323 // SuperCollider glue
324 int prNumOSCServices(struct VMGlobals *g, int numArgsPushed);
325 int prNumOSCServices(struct VMGlobals *g, int numArgsPushed)
327 unsigned numServices = [[RendezvousClient sharedClient] numberOfOSCServices];
328 // set the result of the call to the numServices
329 SetInt(g->sp, numServices);
330 return errNone;
333 int prInitOSCService(struct VMGlobals *g, int numArgsPushed);
334 int prInitOSCService(struct VMGlobals *g, int numArgsPushed)
336 PyrSlot *serverSlot = g->sp -1;
337 PyrSlot *indexSlot = g->sp;
339 int index;
340 slotIntVal(indexSlot, &index);
342 OSCService* service = [[RendezvousClient sharedClient] oscServiceAtIndex: index];
343 if (nil == service)
344 return errNone;
346 // set the server name
347 NSString* name = [service->netService name];
348 PyrString *serverNameString = newPyrString(g->gc, [name cStringUsingEncoding:[NSString defaultCStringEncoding]] , 0, true);
349 PyrObject *serverObject = slotRawObject(serverSlot);
350 SetObject(&serverObject->slots[0], serverNameString);
352 if (!service->isResolved) {
353 PyrString *hostNameString = newPyrString(g->gc, "" , 0, true);
354 SetObject(&serverObject->slots[1], hostNameString);
356 // port
357 int port = 0;
358 SetInt(&serverObject->slots[2], port);
359 return errNone;
362 // hostName
363 NSString* hostName = service->hostName;
364 PyrString *hostNameString = newPyrString(g->gc, [hostName cStringUsingEncoding:[NSString defaultCStringEncoding]] , 0, true);
365 SetObject(&serverObject->slots[1], hostNameString);
367 // port
368 int port = (int) service->port;
369 SetInt(&serverObject->slots[2], port);
371 // protocol
372 NSString* type = [service->netService type];
373 PyrSlot* protoSlot = &serverObject->slots[3];
374 if ([type isEqualToString: kOSCServiceTypeStrings[kOSCServiceUDP]]) {
375 SetSymbol(protoSlot, getsym("udp"));
376 } else if ([type isEqualToString: kOSCServiceTypeStrings[kOSCServiceTCP]]) {
377 SetSymbol(protoSlot, getsym("tcp"));
378 } else {
379 SetNil(protoSlot);
383 // if OSCService is changed to hold onto a netAddr, use this to set the netAddr fields
384 PyrSlot *netAddrSlot = &serverObject->slots[1];
385 PyrObject *netAddrObject = slotRawObject(netAddrSlot);
387 // addr
388 int addr = (int) service->hostAddress;
389 SetInt(&netAddrObject->slots[0], addr);
391 // port
392 int port = (int) service->port;
393 SetInt(&netAddrObject->slots[1], port);
395 // hostName
396 NSString* hostName = service->hostName;
397 PyrString *hostNameString = newPyrString(g->gc, [hostName cString] , 0, true);
398 SetObject(&serverObject->slots[2], hostNameString);
401 return errNone;
404 void initRendezvousPrimitives()
406 int base, index;
407 base = nextPrimitiveIndex();
408 index = 0;
409 definePrimitive(base, index++, "_NumOSCServices", prNumOSCServices, 1, 0);
410 definePrimitive(base, index++, "_InitOSCService", prInitOSCService, 3, 0);