annotate c/plosc.c @ 2:6c1ba6bf9f96

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