HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / Common / Control / NetAddr.sc
blob3d6bd00e2dd6c5ef8e6230385b09b89183eec2a4
1 NetAddr {
2         var <addr=0, <>port=0, <hostname, <socket;
3         classvar connections;
5         *initClass {
6                 connections = IdentityDictionary.new;
7         }
9         *new { arg hostname, port=0;
10                 var addr;
11                 addr = if (hostname.notNil,{ hostname.gethostbyname },{ 0 });
12                 ^super.newCopyArgs(addr, port, hostname);
13         }
14         *fromIP { arg addr, port=0;
15                 ^super.newCopyArgs(addr, port, addr.asIPString)
16         }
18         *langPort {
19                 _GetLangPort
20         }
21         *localAddr {
22                 ^this.new("127.0.0.1", this.langPort)
23         }
24         *useDoubles_ { arg flag = false;
25                 _NetAddr_UseDoubles
26                 ^this.primitiveFailed;
27         }
28         *broadcastFlag {
29                 _NetAddr_GetBroadcastFlag
30                 ^this.primitiveFailed
31         }
32         *broadcastFlag_ { arg flag = true;
33                 _NetAddr_SetBroadcastFlag
34                 ^this.primitiveFailed
35         }
37         *disconnectAll {
38                 connections.keys.do({ | netAddr |
39                         netAddr.disconnect;
40                 });
41         }
43         hostname_ { arg inHostname;
44                 hostname = inHostname;
45                 addr = inHostname.gethostbyname;
46         }
49         sendRaw { arg rawArray;
50                 _NetAddr_SendRaw
51                 ^this.primitiveFailed;
52         }
53         sendMsg { arg ... args;
54                 _NetAddr_SendMsg
55                 ^this.primitiveFailed;
56         }
57         // warning: this primitive will fail to send if the bundle size is too large
58         // but it will not throw an error.  this needs to be fixed
59         sendBundle { arg time ... args;
60                 _NetAddr_SendBundle
61                 ^this.primitiveFailed;
62         }
63         sendStatusMsg {
64                 this.sendMsg("/status");
65         }
66         sendClumpedBundles { arg time ... args;
67                 if(args.bundleSize > 65535) {// udp max size.
68                                 args.clumpBundles.do { |item|
69                                         if(time.notNil) { time = time + 1e-9 }; // make it a nanosecond later
70                                         this.sendBundle(time, *item)
71                                 };
72                         } {
73                                 this.sendBundle(time, *args)
74                 }
75         }
77         sync { arg condition, bundles, latency; // array of bundles that cause async action
78                 var resp, id;
79                 if (condition.isNil) { condition = Condition.new };
80                 if(bundles.isNil) {
81                         id = this.makeSyncResponder(condition);
82                         this.sendBundle(latency, ["/sync", id]);
83                         condition.wait;
84                 } {
85                         // not sure what the exact size is, but its about 20000
86                         // this relates to what _NetAddr_SendBundle can send
87                         if(bundles.bundleSize > 20000/*65515*/) { // 65515 = 65535 - 16 - 4 (sync msg size)
88                                 bundles.clumpBundles.do { |item|
89                                         id = this.makeSyncResponder(condition);
90                                         this.sendBundle(latency, *(item ++ [["/sync", id]]));
91                                         if(latency.notNil) { latency = latency + 1e-9 };
92                                         condition.wait;
93                                 }
94                         } {
95                                 id = this.makeSyncResponder(condition);
96                                 this.sendBundle(latency, *(bundles ++ [["/sync", id]]));
97                                 condition.wait;
98                         }
99                 };
100                 // maybe needed: a timeout
101         }
103         makeSyncResponder { arg condition;
104                         var id = UniqueID.next;
105                         var resp;
107                         resp = OSCFunc({|msg|
108                                 if (msg[1] == id) {
109                                         resp.free;
110                                         condition.test = true;
111                                         condition.signal;
112                                 };
113                         }, '/synced', this);
114                         condition.test = false;
115                         ^id
116         }
118         isConnected {
119                 ^socket.notNil
120         }
121         connect { | disconnectHandler |
122                 if (this.isConnected.not) {
123                         this.prConnect;
124                         connections.put(this, disconnectHandler);
125                 };
126         }
127         disconnect {
128                 if (this.isConnected) {
129                         this.prDisconnect;
130                         this.prConnectionClosed;
131                 };
132         }
134         == { arg that;
135                 ^that respondsTo: #[\port, \addr]
136                         and: { this.port == that.port and: { this.addr == that.addr} }
137         }
138         hash { arg that;
139                 ^addr.hash bitXor: port.hash
140         }
142         // Asymmetric: "that" may be nil or have nil port (wildcards)
143         matches { arg that;
144                 ^this==that or:{
145                         that.isNil or: {
146                                 this.addr == that.addr and: { that.port.isNil }
147                         }
148                 }
149         }
151         ip {
152                 ^addr.asIPString
153         }
155         hasBundle { ^false }
157         printOn { | stream |
158                 super.printOn(stream);
159                 stream << $( << this.ip << ", " << port << $)
160         }
161         storeOn { | stream |
162                 super.storeOn(stream);
163                 stream << $( << "\"" << this.ip << "\", " << port << $)
164         }
166         // PRIVATE
167         prConnect {
168                 _NetAddr_Connect
169                 ^this.primitiveFailed;
170         }
171         prDisconnect {
172                 _NetAddr_Disconnect
173                 ^this.primitiveFailed;
174         }
175         prConnectionClosed {
176                 // called when connection is closed either by sclang or by peer
177                 socket = nil;
178                 connections.removeAt(this).value(this);
179         }
180         recover { ^this }
183 BundleNetAddr : NetAddr {
184         var <saveAddr, <>bundle;
185         var <async = false;
187         *copyFrom { arg addr, bundle;
188                 ^super.newCopyArgs(addr.addr, addr.port, addr.hostname, addr.socket, addr, bundle ? []);
189         }
191         sendRaw { arg rawArray;
192                 bundle = bundle.add( rawArray );
193         }
194         sendMsg { arg ... args;
195                 bundle = bundle.add( args );
196         }
197         sendBundle { arg time ... args;
198                 bundle = bundle.addAll( args );
199         }
200         sendClumpedBundles { arg time ... args;
201                 bundle = bundle.addAll( args );
202         }
203         sendStatusMsg {} // ignore status messages
205         recover {
206                 ^saveAddr.recover
207         }
208         hasBundle { ^true }
211         sync { arg condition, bundles, latency;
212                 bundle = bundle.add([\syncFlag, bundles, latency]);
213                         // not sure about condition. here we ignore it.
214                 async = true;
215         }
217         closeBundle { arg time;
218                 var bundleList, lastBundles;
219                 if(time != false) {
220                         if(async.not) {
221                                 saveAddr.sendClumpedBundles(time, *bundle);
222                                 ^bundle;
223                         };
225                         forkIfNeeded {
226                                         bundleList = this.splitBundles(time);
227                                         lastBundles = bundleList.pop;
228                                         bundleList.do { |bundles|
229                                                 var t = bundles.removeAt(0);
230                                                 saveAddr.sync(nil, bundles, t); // make an independent condition.
231                                         };
232                                         saveAddr.sendClumpedBundles(*lastBundles);  // time ... args
233                         }
234                 };
235                 ^bundle
236         }
238         splitBundles { arg time;
239                 var res, curr;
240                 curr = [time]; // first element in the array is always the time
241                 bundle.do { |item|
242                         if(item[0] === \syncFlag) {
243                                 curr = curr.addAll(item[1]); // then comes the messages
244                                 res = res.add(curr);
245                                 curr = [item[2]]; // time
246                         } {
247                                 curr = curr.add(item)
248                         }
249                 };
250                 res = res.add(curr);
251                 ^res
252         }