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.

662 lines
21KB

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