comparison c/plosc.c @ 0:bbd2b1abfb32

Initial check in.
author samer
date Wed, 11 Jan 2012 15:30:21 +0000
parents
children 6c1ba6bf9f96
comparison
equal deleted inserted replaced
-1:000000000000 0:bbd2b1abfb32
1 /*
2 * Copyright (C) 2009 Samer Abdallah
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16 #include <SWI-Stream.h>
17 #include <SWI-Prolog.h>
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <math.h>
22 #include <lo/lo.h>
23
24 // ---------------------------------------------------------------------------
25
26 // Reimplementation of lo_server_thread to all calls to
27 // Prolog from the server thread.
28
29 typedef struct _my_server_thread {
30 lo_server s;
31 pthread_t thread;
32 volatile int active;
33 volatile int done;
34 } *my_server_thread;
35
36 int my_server_thread_start(my_server_thread st);
37 int my_server_thread_stop(my_server_thread st);
38 int my_server_thread_run(my_server_thread st, int timeout);
39 void my_server_thread_free(my_server_thread st);
40 my_server_thread my_server_thread_new(const char *port, lo_err_handler err_h);
41
42 // ---------------------------------------------------------------------------
43
44 // BLOB to hold a lo_address
45 static PL_blob_t addr_blob;
46
47 // BLOB to hold server thread
48 static PL_blob_t server_blob;
49
50 static predicate_t call3, call5;
51 static atom_t osc_immed;
52 static functor_t osc_ts_2;
53 static functor_t int_1, float_1, double_1, string_1;
54
55 install_t install();
56
57 foreign_t mk_address( term_t host, term_t port, term_t addr);
58 foreign_t is_address( term_t addr);
59 foreign_t send_osc_now( term_t addr, term_t msg, term_t args);
60 foreign_t send_osc_at( term_t addr, term_t msg, term_t args, term_t time);
61 foreign_t send_osc_from_at( term_t serv, term_t addr, term_t msg, term_t args, term_t time);
62 foreign_t send_timestamped( term_t addr, term_t msg, term_t args, term_t sec, term_t frac);
63 foreign_t now( term_t sec, term_t frac);
64 foreign_t time_to_ts( term_t time, term_t sec, term_t frac);
65 foreign_t time_from_ts( term_t time, term_t sec, term_t frac);
66
67 // OSC server predicates
68 foreign_t mk_server( term_t port, term_t server);
69 foreign_t start_server( term_t server);
70 foreign_t stop_server( term_t server);
71 foreign_t del_handler( term_t server, term_t msg, term_t types);
72 foreign_t add_handler( term_t server, term_t msg, term_t types, term_t handler);
73 foreign_t add_handler_x( term_t server, term_t msg, term_t types, term_t handler);
74 foreign_t run_server( term_t server);
75
76
77 // BLOB functions
78 int addr_release(atom_t a) {
79 PL_blob_t *type;
80 size_t len;
81 void *p=PL_blob_data(a,&len,&type);
82 if (p) lo_address_free(*(lo_address *)p);
83 return TRUE;
84 }
85
86 int addr_write(IOSTREAM *s, atom_t a, int flags) {
87 PL_blob_t *type;
88 size_t len;
89 lo_address *p=(lo_address *)PL_blob_data(a,&len,&type);
90 if (p) {
91 const char *host = lo_address_get_hostname(*p);
92 const char *port = lo_address_get_port(*p);
93 if (host!=NULL && port!=NULL) {
94 Sfprintf(s,"osc_address<%s:%s>",host,port);
95 } else {
96 Sfprintf(s,"osc_address<invalid>");
97 }
98 }
99 return TRUE;
100 }
101
102 int server_release(atom_t a) {
103 PL_blob_t *type;
104 size_t len;
105 void *p=PL_blob_data(a,&len,&type);
106 if (p) my_server_thread_free(*(my_server_thread *)p);
107 return TRUE;
108 }
109
110 int server_write(IOSTREAM *s, atom_t a, int flags) {
111 PL_blob_t *type;
112 size_t len;
113 my_server_thread *p=(my_server_thread *)PL_blob_data(a,&len,&type);
114 if (p) {
115 char *url=lo_server_get_url((*p)->s);
116 Sfprintf(s,"osc_server<%s>",url);
117 free(url);
118 }
119 return TRUE;
120 }
121
122 install_t install() {
123 PL_register_foreign("osc_now", 2, (void *)now, 0);
124 PL_register_foreign("time_to_ts", 3, (void *)time_to_ts, 0);
125 PL_register_foreign("time_from_ts", 3, (void *)time_from_ts, 0);
126 PL_register_foreign("osc_mk_address", 3, (void *)mk_address, 0);
127 PL_register_foreign("osc_is_address", 1, (void *)is_address, 0);
128 PL_register_foreign("osc_send_now", 3, (void *)send_osc_now, 0);
129 PL_register_foreign("osc_send_at", 4, (void *)send_osc_at, 0);
130 PL_register_foreign("osc_send_from_at", 5, (void *)send_osc_from_at, 0);
131 PL_register_foreign("osc_mk_server", 2, (void *)mk_server, 0);
132 PL_register_foreign("osc_start_server", 1, (void *)start_server, 0);
133 PL_register_foreign("osc_stop_server", 1, (void *)stop_server, 0);
134 PL_register_foreign("osc_run_server", 1, (void *)run_server, 0);
135 PL_register_foreign("osc_del_method", 3, (void *)del_handler, 0);
136 PL_register_foreign("osc_add_method", 4, (void *)add_handler, 0);
137 PL_register_foreign("osc_add_method_x", 4, (void *)add_handler_x, 0);
138
139 addr_blob.magic = PL_BLOB_MAGIC;
140 addr_blob.flags = PL_BLOB_UNIQUE;
141 addr_blob.name = "osc_address";
142 addr_blob.acquire = 0;
143 addr_blob.release = addr_release;
144 addr_blob.write = addr_write;
145 addr_blob.compare = 0;
146
147 server_blob.magic = PL_BLOB_MAGIC;
148 server_blob.flags = PL_BLOB_UNIQUE;
149 server_blob.name = "osc_server";
150 server_blob.acquire = 0;
151 server_blob.release = server_release;
152 server_blob.write = server_write;
153 server_blob.compare = 0;
154
155 call3 = PL_predicate("call",3,"user");
156 call5 = PL_predicate("call",5,"user");
157 osc_immed = PL_new_atom("osc_immed");
158 osc_ts_2 = PL_new_functor(PL_new_atom("osc_ts"),2);
159 int_1 = PL_new_functor(PL_new_atom("int"),1);
160 float_1 = PL_new_functor(PL_new_atom("float"),1);
161 double_1 = PL_new_functor(PL_new_atom("double"),1);
162 string_1 = PL_new_functor(PL_new_atom("string"),1);
163 }
164
165 // throws a Prolog exception to signal type error
166 static int type_error(term_t actual, const char *expected)
167 {
168 term_t ex = PL_new_term_ref();
169 int rc;
170
171 rc = PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 2,
172 PL_FUNCTOR_CHARS, "type_error", 2,
173 PL_CHARS, expected,
174 PL_TERM, actual,
175 PL_VARIABLE);
176
177 return PL_raise_exception(ex);
178 }
179
180 static int osc_error(int errno, const char *errmsg, const char *msg)
181 {
182 term_t ex = PL_new_term_ref();
183 int rc;
184
185 rc=PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 1,
186 PL_FUNCTOR_CHARS, "osc_error", 3,
187 PL_INTEGER, errno,
188 PL_CHARS, errmsg,
189 PL_CHARS, msg==NULL ? "none" : msg);
190
191 return PL_raise_exception(ex);
192 }
193
194 static int arg_error(const char *type, term_t arg)
195 {
196 term_t ex = PL_new_term_ref();
197 int rc;
198
199 rc=PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 1,
200 PL_FUNCTOR_CHARS, "arg_error", 2,
201 PL_CHARS, type,
202 PL_TERM, arg);
203
204 return PL_raise_exception(ex);
205 }
206
207 // put lo_address into a Prolog BLOB
208 static int unify_addr(term_t addr,lo_address a) {
209 return PL_unify_blob(addr, &a, sizeof(lo_address), &addr_blob);
210 }
211
212 // get lo_address from BLOB
213 static int get_addr(term_t addr, lo_address *a)
214 {
215 PL_blob_t *type;
216 size_t len;
217 lo_address *a1;
218
219 PL_get_blob(addr, (void **)&a1, &len, &type);
220 if (type != &addr_blob) {
221 return type_error(addr, "osc_address");
222 } else {
223 *a=*a1;
224 return TRUE;
225 }
226 }
227
228 // put lo_address into a Prolog BLOB
229 static int unify_server(term_t server,my_server_thread s) {
230 return PL_unify_blob(server, &s, sizeof(my_server_thread), &server_blob);
231 }
232
233 // get my_server_thread from BLOB
234 static int get_server(term_t server, my_server_thread *a)
235 {
236 PL_blob_t *type;
237 size_t len;
238 my_server_thread *a1;
239
240 PL_get_blob(server, (void **)&a1, &len, &type);
241 if (type != &server_blob) {
242 return type_error(server, "osc_server");
243 } else {
244 *a=*a1;
245 return TRUE;
246 }
247 }
248
249 // get Prolog (Unix) time value and convert to OSC timestamp
250 static int get_prolog_time(term_t time, lo_timetag *ts) {
251 double t, ft;
252 int ok = PL_get_float(time, &t);
253
254 ft=floor(t);
255 ts->sec = ((uint32_t)ft)+2208988800u;
256 ts->frac = (uint32_t)(4294967296.0*(t-ft));
257 return ok;
258 }
259
260 static int get_timetag(term_t sec, term_t frac, lo_timetag *ts) {
261 int64_t s, f;
262 int ok = PL_get_int64(sec, &s) && PL_get_int64(frac, &f);
263 ts->sec = s;
264 ts->frac = f;
265 return ok;
266 }
267
268
269 static int get_msg(term_t msg, char **m) {
270 int rc=PL_get_chars(msg, m, CVT_ATOM | CVT_STRING);
271 if (rc && strcmp(*m,"any")==0) *m=NULL;
272 return rc;
273 }
274
275 // parse a list of Prolog terms and add arguments to an OSC message
276 static int add_msg_args(lo_message msg, term_t list)
277 {
278 term_t head=PL_new_term_ref();
279
280 // copy term ref so as not to modify original
281 list=PL_copy_term_ref(list);
282
283 while (PL_get_list(list,head,list)) {
284 atom_t name;
285 int rc, arity;
286 const char *type;
287
288 if (!PL_get_name_arity(head,&name,&arity)) return type_error(head,"term");
289 type=PL_atom_chars(name);
290 switch (arity) {
291 case 1: {
292 term_t a1=PL_new_term_ref();
293 rc=PL_get_arg(1,head,a1); // !!!! check return value
294
295 if (!strcmp(type,"int")) {
296 int x;
297 if (!PL_get_integer(a1,&x)) return type_error(a1,"integer");
298 lo_message_add_int32(msg,x);
299 } else if (!strcmp(type,"double")) {
300 double x;
301 if (!PL_get_float(a1,&x)) return type_error(a1,"float");
302 lo_message_add_double(msg,x);
303 } else if (!strcmp(type,"string")) {
304 char *x;
305 if (!PL_get_chars(a1,&x,CVT_ATOM|CVT_STRING)) return type_error(a1,"string");
306 lo_message_add_string(msg,x);
307 } else if (!strcmp(type,"symbol")) {
308 char *x;
309 if (!PL_get_chars(a1,&x,CVT_ATOM)) return type_error(a1,"atom");
310 lo_message_add_symbol(msg,x);
311 } else if (!strcmp(type,"float")) {
312 double x;
313 if (!PL_get_float(a1,&x)) return type_error(a1,"float");
314 lo_message_add_float(msg,(float)x);
315 } else {
316 return arg_error(type,head);
317 }
318
319 break;
320 }
321 case 0: {
322 if (!strcmp(type,"true")) lo_message_add_true(msg);
323 else if (!strcmp(type,"false")) lo_message_add_false(msg);
324 else if (!strcmp(type,"nil")) lo_message_add_nil(msg);
325 else if (!strcmp(type,"inf")) lo_message_add_infinitum(msg);
326 break;
327 }
328 }
329 }
330 if (!PL_get_nil(list)) return type_error(list,"nil");
331 return TRUE;
332 }
333
334 static int send_msg_timestamped(lo_address a, lo_timetag *ts, char *path, term_t args)
335 {
336 lo_message msg=lo_message_new();
337 lo_bundle bun=lo_bundle_new(*ts);
338
339 if (add_msg_args(msg,args)) {
340 int ret;
341
342 lo_bundle_add_message(bun,path,msg);
343 ret = lo_send_bundle(a,bun);
344 lo_message_free(msg);
345 lo_bundle_free(bun);
346 if (ret==-1) {
347 return osc_error(lo_address_errno(a),lo_address_errstr(a),path);
348 } else {
349 return TRUE;
350 }
351 } else return FALSE;
352 }
353
354 static int send_msg_timestamped_from(lo_address a, lo_server s, lo_timetag *ts, char *path, term_t args)
355 {
356 lo_message msg=lo_message_new();
357 lo_bundle bun=lo_bundle_new(*ts);
358
359 if (add_msg_args(msg,args)) {
360 int ret;
361
362 lo_bundle_add_message(bun,path,msg);
363 ret = lo_send_bundle_from(a,s,bun);
364 lo_message_free(msg);
365 lo_bundle_free(bun);
366 if (ret==-1) {
367 return osc_error(lo_address_errno(a),lo_address_errstr(a),path);
368 } else {
369 return TRUE;
370 }
371 } else return FALSE;
372 }
373
374 static int send_msg(lo_address a, char *path, term_t args)
375 {
376 lo_message msg=lo_message_new();
377
378 if (add_msg_args(msg,args)) {
379 if (lo_send_message(a,path,msg)==-1) {
380 lo_message_free(msg);
381 return osc_error(lo_address_errno(a),lo_address_errstr(a),path);
382 } else {
383 lo_message_free(msg);
384 return TRUE;
385 }
386 } else return FALSE;
387 }
388
389 foreign_t mk_address(term_t host, term_t port, term_t addr) {
390 char *h, *p;
391
392 if (PL_get_chars(host, &h, CVT_ATOM | CVT_STRING)) {
393 if (PL_get_chars(port, &p, CVT_INTEGER)) {
394 lo_address a = lo_address_new(h,p);
395 return unify_addr(addr,a);
396 } else {
397 return type_error(port,"integer");
398 }
399 } else {
400 return type_error(host,"atom");
401 }
402 }
403
404 foreign_t now(term_t sec, term_t frac) {
405 lo_timetag ts;
406 int64_t s, f;
407
408 lo_timetag_now(&ts);
409 s=ts.sec; f=ts.frac;
410 return PL_unify_int64(sec,s) && PL_unify_int64(frac,f);
411 }
412
413 foreign_t time_to_ts(term_t time, term_t sec, term_t frac) {
414 lo_timetag ts;
415
416 return get_prolog_time(time,&ts) &&
417 PL_unify_int64(sec,ts.sec) &&
418 PL_unify_int64(frac,ts.frac);
419 }
420
421 foreign_t time_from_ts(term_t time, term_t sec, term_t frac) {
422 lo_timetag ts;
423
424 return get_timetag(sec,frac,&ts) &&
425 PL_unify_float(time, (double)(ts.sec-2208988800u) + ts.frac/4294967296.0);
426 }
427
428
429
430 // set current random state structure to values in Prolog term
431 foreign_t is_address(term_t addr) {
432 PL_blob_t *type;
433 return PL_is_blob(addr,&type) && type==&addr_blob;
434 }
435
436 foreign_t send_osc_from_at(term_t serv, term_t addr, term_t msg, term_t args, term_t time) {
437 my_server_thread s;
438 lo_address a;
439 lo_timetag ts;
440 char *m;
441
442 return get_addr(addr,&a) &&
443 get_server(serv,&s) &&
444 get_prolog_time(time,&ts) &&
445 get_msg(msg, &m) &&
446 send_msg_timestamped_from(a,s->s,&ts,m,args);
447 }
448
449 foreign_t send_osc_at(term_t addr, term_t msg, term_t args, term_t time) {
450 lo_address a;
451 lo_timetag ts;
452 char *m;
453
454 return get_addr(addr,&a) &&
455 get_prolog_time(time,&ts) &&
456 get_msg(msg, &m) &&
457 send_msg_timestamped(a,&ts,m,args);
458 }
459
460 foreign_t send_timestamped(term_t addr, term_t msg, term_t args, term_t secs, term_t frac) {
461 lo_address a;
462 lo_timetag ts;
463 char *m;
464
465 return get_addr(addr,&a) &&
466 get_timetag(secs,frac,&ts) &&
467 get_msg(msg, &m) &&
468 send_msg_timestamped(a,&ts,m,args);
469 }
470
471
472
473 foreign_t send_osc_now(term_t addr, term_t msg, term_t args) {
474 lo_address a;
475 char *m;
476
477 return get_addr(addr,&a) &&
478 get_msg(msg, &m) &&
479 send_msg(a,m,args);
480 }
481
482
483
484 /*
485 * Server Bits
486 */
487
488 static void prolog_thread_func(void *data);
489
490 // parse a list of type terms and encode as a NULL terminated
491 // string where each character encodes the type of one argument.
492 static int get_types_list(term_t list, char *typespec, int len)
493 {
494 term_t head=PL_new_term_ref();
495 int count=0;
496
497 // copy term ref so as not to modify original
498 list=PL_copy_term_ref(list);
499
500 while (PL_get_list(list,head,list) && count<len) {
501 atom_t name;
502 int arity;
503 const char *type;
504
505 if (!PL_get_name_arity(head,&name,&arity)) return type_error(head,"term");
506 type=PL_atom_chars(name);
507 switch (arity) {
508 case 1: {
509 if (!strcmp(type,"int")) {
510 typespec[count++]='i';
511 } else if (!strcmp(type,"double")) {
512 typespec[count++]='d';
513 } else if (!strcmp(type,"string")) {
514 typespec[count++]='s';
515 } else if (!strcmp(type,"symbol")) {
516 typespec[count++]='S';
517 } else if (!strcmp(type,"float")) {
518 typespec[count++]='f';
519 }
520 break;
521 }
522 case 0: {
523 if (!strcmp(type,"true")) typespec[count++]='T';
524 else if (!strcmp(type,"false")) typespec[count++]='F';
525 else if (!strcmp(type,"nil")) typespec[count++]='N';
526 else if (!strcmp(type,"inf")) typespec[count++]='I';
527 break;
528 }
529 }
530 }
531 typespec[count]=0;
532 if (!PL_get_nil(list)) return type_error(list,"nil");
533 return TRUE;
534 }
535
536 // parse a term representing argument types - types can be a list
537 // as accepted by get_types_list() above or the atom 'any'
538 static int get_types(term_t types, char *buffer, int len, char **typespec)
539 {
540 if (PL_is_list(types)) {
541 *typespec=buffer;
542 return get_types_list(types,buffer,len);
543 } else if (PL_is_atom(types)) {
544 char *a;
545 if (PL_get_atom_chars(types,&a) && strcmp(a,"any")==0) {
546 *typespec=NULL; return TRUE;
547 } else return type_error(types,"list or 'any'");
548 } else return type_error(types,"list or 'any'");
549 }
550
551 // handler server error
552 static void server_error(int num, const char *msg, const char *path) {
553 osc_error(num,msg,path);
554 }
555
556 // handle the /plosc/stop message for the synchronous server loop
557 // in run_stoppable_server() and hence osc_run_server/1
558 static int stop_handler(const char *path, const char *types, lo_arg **argv,
559 int argc, lo_message msg, void *user_data)
560 {
561 my_server_thread s=(my_server_thread)user_data;
562 s->active=0;
563 return 1;
564 }
565
566 // get message arguments and unify given term with list of arg terms
567 static int unify_msg_args(term_t list, const char *types, lo_arg **argv, int argc)
568 {
569 int i, rc=0;
570 for (i=0; i<argc; i++) {
571 term_t head=PL_new_term_ref();
572 term_t tail=PL_new_term_ref();
573 if (!PL_unify_list(list,head,tail)) PL_fail;
574 switch (types[i]) {
575 case 'i': rc=PL_unify_term(head,PL_FUNCTOR, int_1, PL_INT,argv[i]->i); break;
576 case 'f': rc=PL_unify_term(head,PL_FUNCTOR, float_1, PL_FLOAT,(double)argv[i]->f); break;
577 case 'd': rc=PL_unify_term(head,PL_FUNCTOR, double_1, PL_DOUBLE,argv[i]->d); break;
578 case 's': rc=PL_unify_term(head,PL_FUNCTOR, string_1, PL_CHARS,&argv[i]->s); break;
579 case 'h': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"int64",1,PL_INT64,argv[i]->h); break;
580 case 'c': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"char",1,PL_INT,(int)argv[i]->c); break;
581 case 'S': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"symbol",1,PL_CHARS,&argv[i]->S); break;
582 case 'T': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"true",0); break;
583 case 'F': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"false",0); break;
584 case 'N': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"nil",0); break;
585 case 'I': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"inf",0); break;
586 case 'b': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"blob",0); break;
587 case 't': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"timetag",2,
588 PL_INT64,(int64_t)argv[i]->t.sec,
589 PL_INT64,(int64_t)argv[i]->t.frac);
590 break;
591 case 'm': rc=PL_unify_term(head,PL_FUNCTOR_CHARS,"midi",4,
592 PL_INT,(int)argv[i]->m[0], PL_INT,(int)argv[i]->m[1],
593 PL_INT,(int)argv[i]->m[2], PL_INT,(int)argv[i]->m[3]);
594 break;
595 }
596 if (!rc) PL_fail;
597 list=tail;
598 }
599 return PL_unify_nil(list);
600 }
601
602 // handle OSC message by calling the associated Prolog goal
603 static int prolog_handler(const char *path, const char *types, lo_arg **argv,
604 int argc, lo_message msg, void *user_data)
605 {
606 term_t goal = PL_new_term_ref();
607 term_t term0 = PL_new_term_refs(3);
608
609
610 PL_recorded((record_t)user_data,goal); // retrieve the goal term
611 PL_put_term(term0,goal); // term_t goal encoded in user_data
612 PL_put_atom_chars(term0+1,path);
613
614 return !( unify_msg_args(PL_copy_term_ref(term0+2),types,argv,argc)
615 && PL_call_predicate(NULL,PL_Q_NORMAL,call3,term0));
616 }
617
618 static int prolog_handler_x(const char *path, const char *types, lo_arg **argv,
619 int argc, lo_message msg, void *user_data)
620 {
621 term_t goal = PL_new_term_ref();
622 term_t term0 = PL_new_term_refs(5);
623 int rc=1;
624
625 lo_timetag ts = lo_message_get_timestamp(msg);
626 lo_address sender = lo_message_get_source(msg);
627 // printf("osc tt: %u s + %u micros\n",ts.sec,ts.frac);
628
629 PL_recorded((record_t)user_data,goal); // retrieve the goal term
630 PL_put_term(term0,goal); // term_t goal encoded in user_data
631 PL_put_atom_chars(term0+3,path);
632
633 if (ts.sec==0u) PL_put_atom(term0+2,osc_immed);
634 else {
635 rc=PL_unify_term( term0+2, PL_FUNCTOR, osc_ts_2,
636 PL_INT64, (int64_t)ts.sec,
637 PL_INT64, (int64_t)ts.frac);
638 }
639 // PL_put_float(term0+2, (double)(ts.sec-2208988800u) + ts.frac/4294967296.0);
640
641 return !( rc
642 && unify_addr(term0+1,sender)
643 && unify_msg_args(PL_copy_term_ref(term0+4),types,argv,argc)
644 && PL_call_predicate(NULL,PL_Q_NORMAL,call5,term0));
645 }
646
647 /*
648 static int generic_handler(const char *path, const char *types, lo_arg **argv,
649 int argc, lo_message msg, void *user_data)
650 {
651 int i;
652
653 printf("path: <%s>\n", path);
654 for (i=0; i<argc; i++) {
655 printf("arg %d '%c' ", i, types[i]);
656 lo_arg_pp(types[i], argv[i]);
657 printf("\n");
658 }
659 printf("\n");
660 fflush(stdout);
661 return 1;
662 }
663
664 static int verbose_prolog_handler(const char *path, const char *types, lo_arg **argv,
665 int argc, lo_message msg, void *user_data)
666 {
667 generic_handler(path,types,argv,argc,msg,user_data);
668 prolog_handler(path,types,argv,argc,msg,user_data);
669 return 1;
670 }
671 */
672
673 // run OSC server in this thread but with an extra message handler
674 // to allow the /plosc/stop message to terminate the loop.
675 static int run_stoppable_server(my_server_thread s, int timeout)
676 {
677 lo_server_add_method(s->s, "/plosc/stop", NULL, stop_handler, (void *)s);
678 my_server_thread_run(s,timeout);
679 lo_server_del_method(s->s,"/plosc/stop",NULL);
680 return TRUE;
681 }
682
683 foreign_t mk_server(term_t port, term_t server)
684 {
685 char *p;
686
687 if (PL_get_chars(port, &p, CVT_INTEGER)) {
688 my_server_thread s = my_server_thread_new(p, server_error);
689 if (s) return unify_server(server,s);
690 else return FALSE;
691 } else {
692 return type_error(port,"integer");
693 }
694 }
695
696 foreign_t add_handler_x(term_t server, term_t msg, term_t types, term_t goal)
697 {
698 my_server_thread s;
699 lo_method method;
700 char *pattern, *typespec;
701 char buffer[256]; // !! space for up to 255 arguments
702 int rc;
703
704 rc = get_server(server,&s)
705 && get_msg(msg,&pattern)
706 && get_types(types,buffer,256,&typespec);
707
708 if (rc) {
709 record_t goal_record=PL_record(goal);
710 method = lo_server_add_method(s->s, pattern, typespec, prolog_handler_x, (void *)goal_record);
711 }
712 return rc;
713 }
714
715 foreign_t add_handler(term_t server, term_t msg, term_t types, term_t goal)
716 {
717 my_server_thread s;
718 lo_method method;
719 char *pattern, *typespec;
720 char buffer[256]; // !! space for up to 255 arguments
721 int rc;
722
723 rc = get_server(server,&s)
724 && get_msg(msg,&pattern)
725 && get_types(types,buffer,256,&typespec);
726
727 if (rc) {
728 record_t goal_record=PL_record(goal);
729 method = lo_server_add_method(s->s, pattern, typespec, prolog_handler, (void *)goal_record);
730 }
731 return rc;
732 }
733
734 foreign_t del_handler(term_t server, term_t msg, term_t types)
735 {
736 my_server_thread s;
737 char *pattern, *typespec;
738 char buffer[256]; // !! space for up to 255 arguments
739 int rc;
740
741 rc = get_server(server,&s)
742 && get_msg(msg,&pattern)
743 && get_types(types,buffer,256,&typespec);
744
745 if (rc) lo_server_del_method(s->s,pattern,typespec);
746 return rc;
747 }
748
749 foreign_t start_server( term_t server)
750 {
751 my_server_thread s;
752 return get_server(server,&s) && (my_server_thread_start(s)==0);
753 }
754
755 foreign_t stop_server( term_t server)
756 {
757 my_server_thread s;
758 return get_server(server,&s) && (my_server_thread_stop(s)==0);
759 }
760
761 foreign_t run_server( term_t server)
762 {
763 my_server_thread s;
764 printf("running OSC server synchronously...\n");
765 return get_server(server,&s) && run_stoppable_server(s,10);
766 }
767
768
769 // -------------------------------------------------------------------------
770 // my_server_thread implementation
771
772 my_server_thread my_server_thread_new(const char *port, lo_err_handler err_h)
773 {
774 my_server_thread st = malloc(sizeof(struct _my_server_thread));
775 st->s = lo_server_new(port, err_h);
776 st->active = 0;
777 st->done = 0;
778
779 if (!st->s) {
780 free(st);
781 return NULL;
782 }
783 return st;
784 }
785
786 void my_server_thread_free(my_server_thread st)
787 {
788 if (st) {
789 if (st->active) {
790 my_server_thread_stop(st);
791 }
792 lo_server_free(st->s);
793 }
794 free(st);
795 }
796
797 int my_server_thread_stop(my_server_thread st)
798 {
799 int result;
800
801 if (st->active) {
802 st->active = 0; // Signal thread to stop
803
804 result = pthread_join( st->thread, NULL );
805 if (result) {
806 fprintf(stderr,"Failed to stop thread: pthread_join(), %s",strerror(result));
807 return -result;
808 }
809 }
810
811 return 0;
812 }
813
814
815 int my_server_thread_start(my_server_thread st)
816 {
817 int result;
818
819 if (!st->active) {
820 st->active = 1;
821 st->done = 0;
822
823 // Create the server thread
824 result = pthread_create(&(st->thread), NULL, (void *)&prolog_thread_func, st);
825 if (result) {
826 fprintf(stderr, "Failed to create thread: pthread_create(), %s",
827 strerror(result));
828 return -result;
829 }
830
831 }
832 return 0;
833 }
834
835 int my_server_thread_run(my_server_thread st, int timeout)
836 {
837 st->active = 1;
838 st->done = 0;
839 while (st->active) {
840 lo_server_recv_noblock(st->s, timeout);
841 }
842 st->done = 1;
843 return 0;
844 }
845
846 // code for the asynchronous server loop
847 // we must create and attach a new Prolog engine to enable
848 // calls to Prolog from this thread.
849 static void prolog_thread_func(void *data)
850 {
851 my_server_thread st = (my_server_thread)data;
852
853 printf("OSC server started.\n");
854 PL_thread_attach_engine(NULL);
855 my_server_thread_run(st,50);
856 PL_thread_destroy_engine();
857 printf("OSC server stopped.\n");
858 pthread_exit(NULL);
859 }
860