class library: Spawner - don't access PriorityQueue-array
[supercollider.git] / editors / scapp / RendezvousClient.M
blobe821843048ab78e10533424122b8836f2da33af3
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];
117 // 10 elements is a reasonable starting point
118 oscServices = [[NSMutableArray alloc] initWithCapacity: 10];
120 return self;
123 - (void)findOSCServices
125 for (int serviceType = 0; serviceType < kNumOSCServiceTypes; serviceType++) {
126 [browsers[serviceType] searchForServicesOfType: kOSCServiceTypeStrings[serviceType] inDomain: @""];
130 - (unsigned)numberOfOSCServices
132 return [oscServices count];
135 - (OSCService*)oscServiceAtIndex:(unsigned)index
137 if ((index >= [oscServices count]))
138 return nil;
139 return [oscServices objectAtIndex: index];
142 - (void)resolveAllServices
144 // See the comment on re-entrancy in resolveNextService
146 // begin with the first service, and let them each hand off to the next
147 // (see the implementation of netServiceDidResolveAddress:)
148 if ([oscServices count] < 1)
149 return;
151 indexOfServiceBeingResolved = 0;
152 [self resolveNextService];
155 - (void)resolveNextService
157 // NOTE ON RE-ENTRANCY OF THIS CODE
158 // Theoretically, there should be locks between resolveAllServices, resolveNextService, and
159 // netServiceDidResolveAddress, but in practice it is not necessary because all these run on the
160 // same thread, so it is not possible, e.g., for resolveNextService to have simultaneous, interleaved
161 // calls. Thus, there is no need for locking.
163 // find something to resolve
164 for (; indexOfServiceBeingResolved < [oscServices count]; indexOfServiceBeingResolved++) {
165 serviceBeingResolved = [oscServices objectAtIndex: indexOfServiceBeingResolved];
166 if (!serviceBeingResolved->isResolved)
167 break;
170 // if we didn't find anything, we are done
171 if (!(indexOfServiceBeingResolved < [oscServices count])) {
172 // we're done
173 serviceBeingResolved = nil;
174 indexOfServiceBeingResolved = 0;
175 return;
178 // resolve it!
179 [serviceBeingResolved->netService setDelegate: self];
180 [serviceBeingResolved->netService resolveWithTimeout:5]; // Deprecated; deprecator auto uses use resolveWithTimeout 5, should probably shorten
183 // Rendezvous implementation methods
184 - (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)aNetServiceBrowser
186 // do nothing
189 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
191 if (nil == aNetService)
193 // I don't think this should happen...
194 return;
197 // see if it is a duplicate
198 unsigned arrayCount = [oscServices count];
199 unsigned i;
200 OSCService* potentialDuplicate;
201 for(i = 0; i < arrayCount; i++)
203 potentialDuplicate = [oscServices objectAtIndex: i];
204 if ([aNetService isEqual: potentialDuplicate->netService])
206 (potentialDuplicate->refCount)++;
207 return;
211 // allocate an OSCService object for this NSService
212 // (autorelease it because we are going to put it into an array)
213 OSCService* service = [[[OSCService alloc] init] autorelease];
214 service->netService = aNetService;
215 [service->netService retain];
216 service->refCount++;
217 service->isResolved = NO;
219 // add this to the list of known services
220 [oscServices addObject: service];
222 // if there's no more coming, then it's time to resolve all the services
223 if (!moreComing) {
224 [self resolveAllServices];
228 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict
230 post("Can't search for OSC Services\n");
233 - (void)netBrowserDidStopSearch:(NSNetServiceBrowser *)aNetServiceBrowser
235 // do nothing
238 - (void)netServiceWillResolve:(NSNetService *)sender
240 // do nothing
243 - (void)netServiceDidResolveAddress:(NSNetService *)sender
245 if (!serviceBeingResolved) {
246 // this is odd... I got a resolution I didn't ask for
247 post("Resolved OSC Service %s without requesting it.\n", [[sender name] cString]);
248 [sender stop];
249 return;
252 // get the addresses
253 NSArray* addresses = [sender addresses];
254 if (nil == addresses)
255 return; // I don't think this should happen
257 // just pick one of them
258 NSData* address = [addresses lastObject];
259 if (nil == address)
260 return; // I don't think this should happen
262 const struct sockaddr_in* sockaddr = (const struct sockaddr_in*) [address bytes];
263 serviceBeingResolved->netService = sender;
264 serviceBeingResolved->sockaddr = sockaddr;
265 serviceBeingResolved->hostAddress = sockaddr->sin_addr.s_addr;
266 serviceBeingResolved->port = sockaddr->sin_port;
268 struct hostent* hostent = gethostbyaddr((char*) &(sockaddr->sin_addr), 4, AF_INET);
270 if (hostent)
271 serviceBeingResolved->hostName = [[NSString alloc] initWithCString: hostent->h_name];
272 else {
273 // Couldn't find a host name for the address.
274 // Convert the address to a string for the hostname
275 char* addrOctets = inet_ntoa(sockaddr->sin_addr);
276 serviceBeingResolved->hostName = [[NSString alloc] initWithCString: addrOctets];
278 serviceBeingResolved->isResolved = YES;
279 [sender stop];
281 [self resolveNextService];
284 - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
286 post("Could not resolve the address for a discovered OSC Service\n");
289 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing
291 // Ignore this -- I don't think it happens, as I never triger a removal of a domain
294 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
296 OSCService* service;
298 // remove this from the list of services
299 unsigned arrayCount = [oscServices count];
300 unsigned i;
301 for(i = 0; i < arrayCount; i++)
303 service = [oscServices objectAtIndex: i];
304 if ([aNetService isEqual: service->netService])
306 service->refCount--;
307 if (service->refCount < 1) {
308 [service->hostName release];
309 // removing it from the array will release the service object
310 // too
311 [oscServices removeObjectAtIndex: i];
313 return;
318 @end
320 @implementation OSCService
322 @end
324 // SuperCollider glue
325 int prNumOSCServices(struct VMGlobals *g, int numArgsPushed);
326 int prNumOSCServices(struct VMGlobals *g, int numArgsPushed)
328 unsigned numServices = [[RendezvousClient sharedClient] numberOfOSCServices];
329 // set the result of the call to the numServices
330 SetInt(g->sp, numServices);
331 return errNone;
334 int prInitOSCService(struct VMGlobals *g, int numArgsPushed);
335 int prInitOSCService(struct VMGlobals *g, int numArgsPushed)
337 PyrSlot *serverSlot = g->sp -1;
338 PyrSlot *indexSlot = g->sp;
340 int index;
341 slotIntVal(indexSlot, &index);
343 OSCService* service = [[RendezvousClient sharedClient] oscServiceAtIndex: index];
344 if (nil == service)
345 return errNone;
347 // set the server name
348 NSString* name = [service->netService name];
349 PyrString *serverNameString = newPyrString(g->gc, [name cString] , 0, true);
350 PyrObject *serverObject = slotRawObject(serverSlot);
351 SetObject(&serverObject->slots[0], serverNameString);
353 if (!service->isResolved) {
354 PyrString *hostNameString = newPyrString(g->gc, "" , 0, true);
355 SetObject(&serverObject->slots[1], hostNameString);
357 // port
358 int port = 0;
359 SetInt(&serverObject->slots[2], port);
360 return errNone;
363 // hostName
364 NSString* hostName = service->hostName;
365 PyrString *hostNameString = newPyrString(g->gc, [hostName cString] , 0, true);
366 SetObject(&serverObject->slots[1], hostNameString);
368 // port
369 int port = (int) service->port;
370 SetInt(&serverObject->slots[2], port);
372 // protocol
373 NSString* type = [service->netService type];
374 PyrSlot* protoSlot = &serverObject->slots[3];
375 if ([type isEqualToString: kOSCServiceTypeStrings[kOSCServiceUDP]]) {
376 SetSymbol(protoSlot, getsym("udp"));
377 } else if ([type isEqualToString: kOSCServiceTypeStrings[kOSCServiceTCP]]) {
378 SetSymbol(protoSlot, getsym("tcp"));
379 } else {
380 SetNil(protoSlot);
384 // if OSCService is changed to hold onto a netAddr, use this to set the netAddr fields
385 PyrSlot *netAddrSlot = &serverObject->slots[1];
386 PyrObject *netAddrObject = slotRawObject(netAddrSlot);
388 // addr
389 int addr = (int) service->hostAddress;
390 SetInt(&netAddrObject->slots[0], addr);
392 // port
393 int port = (int) service->port;
394 SetInt(&netAddrObject->slots[1], port);
396 // hostName
397 NSString* hostName = service->hostName;
398 PyrString *hostNameString = newPyrString(g->gc, [hostName cString] , 0, true);
399 SetObject(&serverObject->slots[2], hostNameString);
402 return errNone;
405 void initRendezvousPrimitives()
407 int base, index;
408 base = nextPrimitiveIndex();
409 index = 0;
410 definePrimitive(base, index++, "_NumOSCServices", prNumOSCServices, 1, 0);
411 definePrimitive(base, index++, "_InitOSCService", prInitOSCService, 3, 0);