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.

542 lines
16KB

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