1 2 /*************************************************************************/ 3 /* Copyright (C) 2012 Jonathan Moore Liles */ 4 /* */ 5 /* Permission to use, copy, modify, and/or distribute this software for */ 6 /* any purpose with or without fee is hereby granted, provided that the */ 7 /* above copyright notice and this permission notice appear in all */ 8 /* copies. */ 9 /* */ 10 /* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL */ 11 /* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED */ 12 /* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE */ 13 /* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL */ 14 /* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR */ 15 /* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER */ 16 /* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR */ 17 /* PERFORMANCE OF THIS SOFTWARE. */ 18 /*************************************************************************/ 19 20 21 /*************************************************************/ 22 /* A simple, callback based C API for NSM clients. */ 23 /* */ 24 /* Simplified Example: */ 25 /* */ 26 /* #include "nsm.h" */ 27 /* */ 28 /* int */ 29 /* cb_nsm_open ( const char *name, */ 30 /* const char *display_name, */ 31 /* const char *client_id, */ 32 /* char **out_msg, */ 33 /* void *userdata ) */ 34 /* { */ 35 /* do_open_stuff(); */ 36 /* return ERR_OK; */ 37 /* } */ 38 /* */ 39 /* int */ 40 /* cb_nsm_save ( char **out_msg, */ 41 /* void *userdata ) */ 42 /* { */ 43 /* do_save_stuff(); */ 44 /* return ERR_OK; */ 45 /* } */ 46 /* */ 47 /* static nsm_client_t *nsm = 0 */ 48 /* */ 49 /* int main( int argc, char **argv ) */ 50 /* { */ 51 /* const char *nsm_url = getenv( "NSM_URL" ); */ 52 /* */ 53 /* if ( nsm_url ) */ 54 /* { */ 55 /* nsm = nsm_new(); */ 56 /* */ 57 /* nsm_set_open_callback( nsm, cb_nsm_open, 0 ); */ 58 /* nsm_set_save_callback( nsm, cb_nsm_save, 0 ); */ 59 /* */ 60 /* if ( 0 == nsm_init( nsm, nsm_url ) ) */ 61 /* { */ 62 /* nsm_send_announce( nsm, "FOO", "", argv[0] ); */ 63 /* } */ 64 /* else */ 65 /* { */ 66 /* nsm_free( nsm ); */ 67 /* nsm = 0; */ 68 /* } */ 69 /* } */ 70 /* } */ 71 /*************************************************************/ 72 73 #ifndef _NSM_H 74 #define _NSM_H 75 76 #define NSM_API_VERSION_MAJOR 1 77 #define NSM_API_VERSION_MINOR 0 78 79 #include <lo/lo.h> 80 #include <string.h> 81 #include <sys/types.h> 82 #include <unistd.h> 83 #include <stdlib.h> 84 #include <stdio.h> 85 86 typedef void * nsm_client_t; 87 typedef int (nsm_open_callback)( const char *name, const char *display_name, const char *client_id, char **out_msg, void *userdata ); 88 typedef int (nsm_save_callback)( char **out_msg, void *userdata ); 89 typedef void (nsm_active_callback)( int b, void *userdata ); 90 typedef void (nsm_session_is_loaded_callback)( void *userdata ); 91 typedef int (nsm_broadcast_callback)( const char *, lo_message m, void *userdata ); 92 93 #define _NSM() ((struct _nsm_client_t*)nsm) 94 95 #define NSM_EXPORT __attribute__((unused)) static 96 97 /* private parts */ 98 struct _nsm_client_t 99 { 100 const char *nsm_url; 101 102 lo_server _server; 103 lo_server_thread _st; 104 lo_address nsm_addr; 105 106 int nsm_is_active; 107 char *nsm_client_id; 108 char *_session_manager_name; 109 110 nsm_open_callback *open; 111 void *open_userdata; 112 113 nsm_save_callback *save; 114 void *save_userdata; 115 116 nsm_active_callback *active; 117 void *active_userdata; 118 119 nsm_session_is_loaded_callback *session_is_loaded; 120 void *session_is_loaded_userdata; 121 122 nsm_broadcast_callback *broadcast; 123 void *broadcast_userdata; 124 }; 125 126 enum 127 { 128 ERR_OK = 0, 129 ERR_GENERAL = -1, 130 ERR_INCOMPATIBLE_API = -2, 131 ERR_BLACKLISTED = -3, 132 ERR_LAUNCH_FAILED = -4, 133 ERR_NO_SUCH_FILE = -5, 134 ERR_NO_SESSION_OPEN = -6, 135 ERR_UNSAVED_CHANGES = -7, 136 ERR_NOT_NOW = -8 137 }; 138 139 NSM_EXPORT 140 int 141 nsm_is_active ( nsm_client_t *nsm ) 142 { 143 return _NSM()->nsm_is_active; 144 } 145 146 NSM_EXPORT 147 const char * 148 nsm_get_session_manager_name ( nsm_client_t *nsm ) 149 { 150 return _NSM()->_session_manager_name; 151 } 152 153 NSM_EXPORT 154 nsm_client_t * 155 nsm_new ( void ) 156 { 157 struct _nsm_client_t *nsm = (struct _nsm_client_t*)malloc( sizeof( struct _nsm_client_t ) ); 158 159 nsm->nsm_url = 0; 160 161 nsm->nsm_is_active = 0; 162 nsm->nsm_client_id = 0; 163 164 nsm->_server = 0; 165 nsm->_st = 0; 166 nsm->nsm_addr = 0; 167 nsm->_session_manager_name = 0; 168 169 nsm->open = 0; 170 nsm->save = 0; 171 nsm->active = 0; 172 nsm->session_is_loaded = 0; 173 nsm->broadcast = 0; 174 175 return (nsm_client_t *)nsm; 176 } 177 178 /*******************************************/ 179 /* CLIENT TO SERVER INFORMATIONAL MESSAGES */ 180 /*******************************************/ 181 182 NSM_EXPORT 183 void 184 nsm_send_is_dirty ( nsm_client_t *nsm ) 185 { 186 if ( _NSM()->nsm_is_active ) 187 lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_dirty", "" ); 188 } 189 190 NSM_EXPORT 191 void 192 nsm_send_is_clean ( nsm_client_t *nsm ) 193 { 194 if ( _NSM()->nsm_is_active ) 195 lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_clean", "" ); 196 } 197 198 NSM_EXPORT 199 void 200 nsm_send_progress ( nsm_client_t *nsm, float p ) 201 { 202 if ( _NSM()->nsm_is_active ) 203 lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/progress", "f", p ); 204 } 205 206 NSM_EXPORT 207 void 208 nsm_send_message ( nsm_client_t *nsm, int priority, const char *msg ) 209 { 210 if ( _NSM()->nsm_is_active ) 211 lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/message", "is", priority, msg ); 212 } 213 214 NSM_EXPORT void 215 nsm_send_announce ( nsm_client_t *nsm, const char *app_name, const char *capabilities, const char *process_name ) 216 { 217 lo_address to = lo_address_new_from_url( _NSM()->nsm_url ); 218 219 if ( ! to ) 220 { 221 fprintf( stderr, "NSM: Bad address!" ); 222 return; 223 } 224 225 int pid = (int)getpid(); 226 227 lo_send_from( to, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", 228 app_name, 229 capabilities, 230 process_name, 231 NSM_API_VERSION_MAJOR, 232 NSM_API_VERSION_MINOR, 233 pid ); 234 235 lo_address_free( to ); 236 } 237 238 NSM_EXPORT void 239 nsm_send_broadcast ( nsm_client_t *nsm, lo_message msg ) 240 { 241 if ( _NSM()->nsm_is_active ) 242 lo_send_message_from( _NSM()->nsm_addr, _NSM()->_server, "/nsm/server/broadcast", msg ); 243 } 244 245 246 247 NSM_EXPORT 248 void 249 nsm_check_wait ( nsm_client_t *nsm, int timeout ) 250 { 251 if ( lo_server_wait( _NSM()->_server, timeout ) ) 252 while ( lo_server_recv_noblock( _NSM()->_server, 0 ) ) {} 253 } 254 255 NSM_EXPORT 256 void 257 nsm_check_nowait (nsm_client_t *nsm ) 258 { 259 nsm_check_wait( nsm, 0 ); 260 } 261 262 263 NSM_EXPORT 264 void 265 nsm_thread_start ( nsm_client_t *nsm ) 266 { 267 lo_server_thread_start( _NSM()->_st ); 268 } 269 270 271 NSM_EXPORT 272 void 273 nsm_thread_stop ( nsm_client_t *nsm ) 274 { 275 lo_server_thread_stop( _NSM()->_st ); 276 } 277 278 279 280 NSM_EXPORT void 281 nsm_free ( nsm_client_t *nsm ) 282 { 283 if ( _NSM()->_st ) 284 nsm_thread_stop( nsm ); 285 286 if ( _NSM()->_st ) 287 lo_server_thread_free( _NSM()->_st ); 288 else 289 lo_server_free( _NSM()->_server ); 290 291 free( _NSM() ); 292 } 293 294 /*****************/ 295 /* SET CALLBACKS */ 296 /*****************/ 297 298 NSM_EXPORT 299 void 300 nsm_set_open_callback( nsm_client_t *nsm, nsm_open_callback *open_callback, void *userdata ) 301 { 302 _NSM()->open = open_callback; 303 _NSM()->open_userdata = userdata; 304 } 305 306 NSM_EXPORT 307 void 308 nsm_set_save_callback( nsm_client_t *nsm, nsm_save_callback *save_callback, void *userdata ) 309 { 310 _NSM()->save = save_callback; 311 _NSM()->save_userdata = userdata; 312 313 } 314 315 NSM_EXPORT 316 void 317 nsm_set_active_callback( nsm_client_t *nsm, nsm_active_callback *active_callback, void *userdata ) 318 { 319 _NSM()->active = active_callback; 320 _NSM()->active_userdata = userdata; 321 } 322 323 NSM_EXPORT 324 void 325 nsm_set_session_is_loaded_callback( nsm_client_t *nsm, nsm_session_is_loaded_callback *session_is_loaded_callback, void *userdata ) 326 { 327 _NSM()->session_is_loaded = session_is_loaded_callback; 328 _NSM()->session_is_loaded_userdata = userdata; 329 } 330 331 332 NSM_EXPORT 333 void 334 nsm_set_broadcast_callback( nsm_client_t *nsm, nsm_broadcast_callback *broadcast_callback, void *userdata ) 335 { 336 _NSM()->broadcast = broadcast_callback; 337 _NSM()->broadcast_userdata = userdata; 338 } 339 340 341 342 /****************/ 343 /* OSC HANDLERS */ 344 /****************/ 345 346 #undef OSC_REPLY 347 #undef OSC_REPLY_ERR 348 349 #define OSC_REPLY( value ) lo_send_from( ((struct _nsm_client_t*)user_data)->nsm_addr, ((struct _nsm_client_t*)user_data)->_server, LO_TT_IMMEDIATE, "/reply", "ss", path, value ) 350 351 #define OSC_REPLY_ERR( errcode, value ) lo_send_from( ((struct _nsm_client_t*)user_data)->nsm_addr, ((struct _nsm_client_t*)user_data)->_server, LO_TT_IMMEDIATE, "/error", "sis", path, errcode, value ) 352 353 354 NSM_EXPORT int _nsm_osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 355 { 356 (void) types; 357 (void) argc; 358 (void) msg; 359 360 char *out_msg = NULL; 361 362 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 363 364 nsm->nsm_client_id = strdup( &argv[2]->s ); 365 366 if ( ! nsm->open ) 367 return 0; 368 369 int r = nsm->open( &argv[0]->s, &argv[1]->s, &argv[2]->s, &out_msg, nsm->open_userdata ); 370 371 if ( r ) 372 OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") ); 373 else 374 OSC_REPLY( "OK" ); 375 376 if ( out_msg ) 377 free( out_msg ); 378 379 return 0; 380 } 381 382 NSM_EXPORT int _nsm_osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 383 { 384 (void) types; 385 (void) argv; 386 (void) argc; 387 (void) msg; 388 389 char *out_msg = NULL; 390 391 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 392 393 if ( ! nsm->save ) 394 return 0; 395 396 int r = nsm->save(&out_msg, nsm->save_userdata ); 397 398 if ( r ) 399 OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") ); 400 else 401 OSC_REPLY( "OK" ); 402 403 if ( out_msg ) 404 free( out_msg ); 405 406 return 0; 407 } 408 409 NSM_EXPORT int _nsm_osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 410 { 411 (void) path; 412 (void) types; 413 (void) argc; 414 415 if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) ) 416 return -1; 417 418 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 419 420 fprintf( stderr, "NSM: Successfully registered. NSM says: %s", &argv[1]->s ); 421 422 nsm->nsm_is_active = 1; 423 nsm->_session_manager_name = strdup( &argv[2]->s ); 424 nsm->nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) )); 425 426 if ( nsm->active ) 427 nsm->active( nsm->nsm_is_active, nsm->active_userdata ); 428 429 return 0; 430 } 431 432 NSM_EXPORT int _nsm_osc_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 433 { 434 (void) path; 435 (void) types; 436 (void) argc; 437 (void) msg; 438 439 if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) ) 440 return -1; 441 442 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 443 444 fprintf( stderr, "NSM: Failed to register with NSM server: %s", &argv[2]->s ); 445 446 nsm->nsm_is_active = 0; 447 448 if ( nsm->active ) 449 nsm->active( nsm->nsm_is_active, nsm->active_userdata ); 450 451 return 0; 452 } 453 454 NSM_EXPORT int _nsm_osc_session_is_loaded ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 455 { 456 (void) path; 457 (void) types; 458 (void) argv; 459 (void) argc; 460 (void) msg; 461 462 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 463 464 if ( ! nsm->session_is_loaded ) 465 return 0; 466 467 nsm->session_is_loaded( nsm->session_is_loaded_userdata ); 468 469 return 0; 470 } 471 472 NSM_EXPORT int _nsm_osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) 473 { 474 (void) types; 475 (void) argv; 476 (void) argc; 477 478 struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; 479 480 if ( ! nsm->broadcast ) 481 return 0; 482 483 return nsm->broadcast( path, msg, nsm->broadcast_userdata ); 484 } 485 486 487 488 NSM_EXPORT 489 int 490 nsm_init ( nsm_client_t *nsm, const char *nsm_url ) 491 { 492 _NSM()->nsm_url = nsm_url; 493 494 lo_address addr = lo_address_new_from_url( nsm_url ); 495 int proto = lo_address_get_protocol( addr ); 496 lo_address_free( addr ); 497 498 _NSM()->_server = lo_server_new_with_proto( NULL, proto, NULL ); 499 500 if ( ! _NSM()->_server ) 501 return -1; 502 503 lo_server_add_method( _NSM()->_server, "/error", "sis", _nsm_osc_error, _NSM() ); 504 lo_server_add_method( _NSM()->_server, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() ); 505 lo_server_add_method( _NSM()->_server, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() ); 506 lo_server_add_method( _NSM()->_server, "/nsm/client/save", "", _nsm_osc_save, _NSM() ); 507 lo_server_add_method( _NSM()->_server, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() ); 508 lo_server_add_method( _NSM()->_server, NULL, NULL, _nsm_osc_broadcast, _NSM() ); 509 510 return 0; 511 } 512 513 514 NSM_EXPORT 515 int 516 nsm_init_thread ( nsm_client_t *nsm, const char *nsm_url ) 517 { 518 _NSM()->nsm_url = nsm_url; 519 520 lo_address addr = lo_address_new_from_url( nsm_url ); 521 int proto = lo_address_get_protocol( addr ); 522 lo_address_free( addr ); 523 524 _NSM()->_st = lo_server_thread_new_with_proto( NULL, proto, NULL ); 525 _NSM()->_server = lo_server_thread_get_server( _NSM()->_st ); 526 527 if ( ! _NSM()->_server ) 528 return -1; 529 530 lo_server_thread_add_method( _NSM()->_st, "/error", "sis", _nsm_osc_error, _NSM() ); 531 lo_server_thread_add_method( _NSM()->_st, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() ); 532 lo_server_thread_add_method( _NSM()->_st, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() ); 533 lo_server_thread_add_method( _NSM()->_st, "/nsm/client/save", "", _nsm_osc_save, _NSM() ); 534 lo_server_thread_add_method( _NSM()->_st, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() ); 535 lo_server_thread_add_method( _NSM()->_st, NULL, NULL, _nsm_osc_broadcast, _NSM() ); 536 537 return 0; 538 } 539 540 #endif /* NSM_H */ 541