Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
16KB

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