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.

999 lines
29KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2012 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. #include "OSC/Endpoint.H"
  19. #include <FL/Fl_Window.H>
  20. #include <FL/Fl_Double_Window.H>
  21. #include <FL/Fl_Widget.H>
  22. #include <FL/Fl.H>
  23. #include <FL/Fl_File_Chooser.H>
  24. #include <FL/Fl_Box.H>
  25. #include <FL/Fl_Pack.H>
  26. #include <FL/Fl_File_Chooser.H>
  27. #include <FL/Fl_Progress.H>
  28. #include "debug.h"
  29. #include <FL/Fl_Browser.H>
  30. #include <FL/Fl_Select_Browser.H>
  31. #include <FL/Fl_Tile.H>
  32. #include "FL/Fl_Packscroller.H"
  33. #include <unistd.h>
  34. #include <errno.h>
  35. #include <time.h>
  36. #include <getopt.h>
  37. #define APP_NAME "Non Session Manager"
  38. // static lo_address nsm_addr = NULL;
  39. static time_t last_ping_response;
  40. static OSC::Endpoint *osc;
  41. struct Daemon
  42. {
  43. const char *url;
  44. lo_address addr;
  45. bool is_child;
  46. Daemon ( )
  47. {
  48. url = NULL;
  49. addr = NULL;
  50. is_child = false;
  51. }
  52. };
  53. static std::list<Daemon*> daemon_list; /* list of all connected daemons */
  54. #define foreach_daemon( _it ) for ( std::list<Daemon*>::iterator _it = daemon_list.begin(); _it != daemon_list.end(); ++ _it )
  55. class NSM_Client : public Fl_Group
  56. {
  57. char *_client_id;
  58. // Fl_Box *client_name;
  59. Fl_Progress *_progress;
  60. Fl_Light_Button *_dirty;
  61. Fl_Button *_remove_button;
  62. Fl_Button *_restart_button;
  63. public:
  64. void
  65. name ( const char *v )
  66. {
  67. label( strdup( v ) );
  68. }
  69. void
  70. client_id ( const char *v )
  71. {
  72. if ( _client_id )
  73. free( _client_id );
  74. _client_id = strdup( v );
  75. }
  76. void
  77. progress ( float f )
  78. {
  79. _progress->value( f );
  80. _progress->redraw();
  81. }
  82. void
  83. dirty ( bool b )
  84. {
  85. _dirty->value( b );
  86. _dirty->redraw();
  87. }
  88. void
  89. stopped ( bool b )
  90. {
  91. if ( b )
  92. {
  93. _remove_button->show();
  94. _restart_button->show();
  95. color( FL_RED );
  96. redraw();
  97. }
  98. else
  99. {
  100. _restart_button->hide();
  101. _remove_button->hide();
  102. }
  103. /* _restart_button->redraw(); */
  104. /* _remove_button->redraw(); */
  105. }
  106. void
  107. pending_command ( const char *command )
  108. {
  109. char *cmd = strdup( command );
  110. free( (void*)_progress->label() );
  111. _progress->label( cmd );
  112. stopped( 0 );
  113. if ( ! strcmp( command, "ready" ) )
  114. {
  115. color( FL_GREEN );
  116. // _progress->value( 0.0f );
  117. }
  118. else if ( ! strcmp( command, "quit" ) ||
  119. ! strcmp( command, "kill" ) ||
  120. ! strcmp( command, "error" ) )
  121. {
  122. color( FL_RED );
  123. }
  124. else if ( ! strcmp( command, "stopped" ) )
  125. {
  126. stopped( 1 );
  127. }
  128. else
  129. {
  130. color( FL_YELLOW );
  131. }
  132. redraw();
  133. }
  134. static void
  135. cb_button ( Fl_Widget *o, void * v )
  136. {
  137. ((NSM_Client*)v)->cb_button( o );
  138. }
  139. void
  140. cb_button ( Fl_Widget *o )
  141. {
  142. if ( o == _dirty )
  143. {
  144. MESSAGE( "Sending save.");
  145. foreach_daemon ( d )
  146. {
  147. osc->send( (*d)->addr, "/nsm/gui/client/save", _client_id );
  148. }
  149. }
  150. if ( o == _remove_button )
  151. {
  152. MESSAGE( "Sending remove.");
  153. foreach_daemon ( d )
  154. {
  155. osc->send( (*d)->addr, "/nsm/gui/client/remove", _client_id );
  156. }
  157. }
  158. else if ( o == _restart_button )
  159. {
  160. MESSAGE( "Sending resume" );
  161. foreach_daemon ( d )
  162. {
  163. osc->send( (*d)->addr, "/nsm/gui/client/resume", _client_id );
  164. }
  165. }
  166. }
  167. const char *
  168. client_id ( void )
  169. { return _client_id; }
  170. NSM_Client ( int X, int Y, int W, int H, const char *L ) :
  171. Fl_Group( X, Y, W, H, L )
  172. {
  173. _client_id = NULL;
  174. align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE );
  175. color( FL_RED );
  176. box( FL_UP_BOX );
  177. { Fl_Progress *o = _progress = new Fl_Progress( ( X + W ) - ( W / 4) - 20, Y + 5, ( W / 4 ), H - 10, NULL );
  178. o->label( strdup( "launch" ) );
  179. o->minimum( 0.0f );
  180. o->maximum( 1.0f );
  181. }
  182. { Fl_Light_Button *o = _dirty = new Fl_Light_Button( _progress->x() - 30, Y + 7, 25, 25 );
  183. o->box( FL_UP_BOX );
  184. o->type(0);
  185. o->color();
  186. o->selection_color( FL_YELLOW );
  187. o->value( 0 );
  188. o->callback( cb_button, this );
  189. }
  190. { Fl_Button *o = _remove_button = new Fl_Button( _progress->x() - 60, Y + 7, 25, 25 );
  191. o->box( FL_UP_BOX );
  192. o->type(0);
  193. o->color( FL_RED );
  194. o->value( 0 );
  195. o->label( "X" );
  196. o->tooltip( "Remove" );
  197. o->hide();
  198. o->callback( cb_button, this );
  199. }
  200. { Fl_Button *o = _restart_button = new Fl_Button( _progress->x() - 90, Y + 7, 25, 25 );
  201. o->box( FL_UP_BOX );
  202. o->type(0);
  203. o->color( FL_GREEN );
  204. o->value( 0 );
  205. o->label( "@>" );
  206. o->tooltip( "Resume" );
  207. o->hide();
  208. o->callback( cb_button, this );
  209. }
  210. end();
  211. }
  212. };
  213. void
  214. browser_callback ( Fl_Widget *w, void *v )
  215. {
  216. w->window()->hide();
  217. }
  218. class NSM_Controller : public Fl_Group
  219. {
  220. public:
  221. Fl_Pack *clients_pack;
  222. Fl_Pack *buttons_pack;
  223. Fl_Button *close_button;
  224. Fl_Button *abort_button;
  225. Fl_Button *save_button;
  226. Fl_Button *open_button;
  227. Fl_Button *new_button;
  228. Fl_Button *add_button;
  229. Fl_Button *duplicate_button;
  230. Fl_Select_Browser *session_browser;
  231. static void cb_handle ( Fl_Widget *w, void *v )
  232. {
  233. ((NSM_Controller*)v)->cb_handle( w );
  234. }
  235. void
  236. cb_handle ( Fl_Widget *w )
  237. {
  238. if ( w == abort_button )
  239. {
  240. if ( 0 == fl_choice( "Are you sure you want to abort this session? Unsaved changes will be lost.", "Abort", "Cancel", NULL ) )
  241. {
  242. MESSAGE( "Sending abort." );
  243. foreach_daemon ( d )
  244. {
  245. osc->send( (*d)->addr, "/nsm/server/abort" );
  246. }
  247. }
  248. }
  249. if ( w == close_button )
  250. {
  251. MESSAGE( "Sending close." );
  252. foreach_daemon ( d )
  253. {
  254. osc->send( (*d)->addr, "/nsm/server/close" );
  255. }
  256. }
  257. else if ( w == save_button )
  258. {
  259. MESSAGE( "Sending save." );
  260. foreach_daemon ( d )
  261. {
  262. osc->send( (*d)->addr, "/nsm/server/save" );
  263. }
  264. }
  265. else if ( w == open_button )
  266. {
  267. const char *name = fl_input( "Open Session", NULL );
  268. if ( ! name )
  269. return;
  270. MESSAGE( "Sending open for: %s", name );
  271. foreach_daemon ( d )
  272. {
  273. osc->send( (*d)->addr, "/nsm/server/open", name );
  274. }
  275. }
  276. else if ( w == duplicate_button )
  277. {
  278. const char *name = fl_input( "New Session", NULL );
  279. if ( ! name )
  280. return;
  281. MESSAGE( "Sending duplicate for: %s", name );
  282. foreach_daemon ( d )
  283. {
  284. osc->send( (*d)->addr, "/nsm/server/duplicate", name );
  285. }
  286. }
  287. else if ( w == session_browser )
  288. {
  289. const char *name = session_browser->text( session_browser->value());
  290. /* strip out formatting codes */
  291. foreach_daemon ( d )
  292. {
  293. osc->send( (*d)->addr, "/nsm/server/open", index( name, ' ' ) + 1 );
  294. }
  295. }
  296. else if ( w == new_button )
  297. {
  298. const char *name = fl_input( "New Session", NULL );
  299. if ( !name )
  300. return;
  301. MESSAGE( "Sending new for: %s", name );
  302. foreach_daemon ( d )
  303. {
  304. osc->send( (*d)->addr, "/nsm/server/new", name );
  305. }
  306. }
  307. else if ( w == add_button )
  308. {
  309. Fl_Select_Browser *browser;
  310. if ( daemon_list.size() > 1 )
  311. {
  312. Fl_Window* win = new Fl_Window( window()->x(), window()->y(), 300, 400, "Choose Server" );
  313. {
  314. {
  315. Fl_Box *o = new Fl_Box( 0,0, 300, 100 );
  316. o->label( "Connected to multiple NSM servers, please select which one to add a client to." );
  317. o->align( FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_WRAP );
  318. }
  319. {
  320. Fl_Select_Browser *o = browser = new Fl_Select_Browser( 0, 100, 300, 300 );
  321. o->box( FL_ROUNDED_BOX );
  322. o->color( FL_BLACK );
  323. o->callback( browser_callback, win );
  324. foreach_daemon( d )
  325. {
  326. o->add( (*d)->url );
  327. }
  328. }
  329. }
  330. win->end();
  331. win->show();
  332. while ( win->visible() )
  333. {
  334. Fl::wait();
  335. }
  336. if ( ! browser->value() )
  337. return;
  338. const char *name = fl_input( "Add Client" );
  339. if ( !name )
  340. return;
  341. lo_address nsm_addr = lo_address_new_from_url( browser->text( browser->value() ) );
  342. osc->send( nsm_addr, "/nsm/server/add", name );
  343. delete win;
  344. }
  345. else
  346. {
  347. const char *name = fl_input( "Add Client" );
  348. if ( !name )
  349. return;
  350. MESSAGE( "Sending add for: %s", name );
  351. /* FIXME: user should get to choose which system to do the add on */
  352. foreach_daemon ( d )
  353. {
  354. osc->send( (*d)->addr, "/nsm/server/add", name );
  355. }
  356. }
  357. }
  358. }
  359. void
  360. ForwardSort( Fl_Browser *b ) {
  361. for ( int t=1; t<=b->size(); t++ ) {
  362. for ( int r=t+1; r<=b->size(); r++ ) {
  363. if ( strcmp(b->text(t), b->text(r)) > 0 ) {
  364. b->swap(t,r);
  365. }
  366. }
  367. }
  368. }
  369. void
  370. sort_sessions ( void )
  371. {
  372. ForwardSort( session_browser );
  373. }
  374. NSM_Client *
  375. client_by_id ( const char *id )
  376. {
  377. for ( int i = clients_pack->children(); i--; )
  378. {
  379. NSM_Client *c = (NSM_Client*)clients_pack->child( i );
  380. if ( ! strcmp( c->client_id(), id ) )
  381. {
  382. return c;
  383. }
  384. }
  385. return NULL;
  386. }
  387. void
  388. session_name ( const char *name )
  389. {
  390. if ( clients_pack->label() )
  391. free( (char*)clients_pack->label() );
  392. clients_pack->parent()->label( strdup( name ) );
  393. redraw();
  394. }
  395. void
  396. client_stopped ( const char *client_id )
  397. {
  398. NSM_Client *c = client_by_id( client_id );
  399. if ( c )
  400. {
  401. c->stopped( 1 );
  402. }
  403. }
  404. void
  405. client_quit ( const char *client_id )
  406. {
  407. NSM_Client *c = client_by_id( client_id );
  408. if ( c )
  409. {
  410. clients_pack->remove( c );
  411. delete c;
  412. }
  413. if ( clients_pack->children() == 0 )
  414. {
  415. ((Fl_Packscroller*)clients_pack->parent())->yposition( 0 );
  416. }
  417. parent()->redraw();
  418. }
  419. void
  420. client_new ( const char *client_id, const char *client_name )
  421. {
  422. NSM_Client *c;
  423. c = client_by_id( client_id );
  424. if ( c )
  425. {
  426. c->name( client_name );
  427. return;
  428. }
  429. c = new NSM_Client( 0, 0, w(), 40, NULL );
  430. c->name( client_name );
  431. c->client_id( client_id );
  432. c->stopped( 0 );
  433. clients_pack->add( c );
  434. redraw();
  435. }
  436. void client_pending_command ( NSM_Client *c, const char *command )
  437. {
  438. if ( c )
  439. {
  440. if ( ! strcmp( command, "removed" ) )
  441. {
  442. clients_pack->remove( c );
  443. delete c;
  444. parent()->redraw();
  445. }
  446. else
  447. c->pending_command( command );
  448. }
  449. }
  450. void add_session_to_list ( const char *name )
  451. {
  452. char *s;
  453. asprintf( &s, "@S18@C3 %s", name );
  454. for ( int i = 1; i <= session_browser->size(); i++ )
  455. {
  456. if ( !strcmp( session_browser->text( i ), s ) )
  457. {
  458. free( s );
  459. return;
  460. }
  461. }
  462. session_browser->add( s );
  463. free(s);
  464. }
  465. NSM_Controller ( int X, int Y, int W, int H, const char *L ) :
  466. Fl_Group( X, Y, W, H, L )
  467. {
  468. align( FL_ALIGN_RIGHT | FL_ALIGN_CENTER | FL_ALIGN_INSIDE );
  469. { Fl_Pack *o = buttons_pack = new Fl_Pack( X, Y, W, 30 );
  470. o->type( Fl_Pack::HORIZONTAL );
  471. o->box( FL_NO_BOX );
  472. { Fl_Button *o = open_button = new Fl_Button( 0, 0, 80, 50, "Open" );
  473. o->box( FL_UP_BOX );
  474. o->callback( cb_handle, (void*)this );
  475. }
  476. { Fl_Button *o = close_button = new Fl_Button( 0, 0, 80, 50, "Close" );
  477. o->box( FL_UP_BOX );
  478. o->callback( cb_handle, (void*)this );
  479. }
  480. { Fl_Button *o = abort_button = new Fl_Button( 0, 0, 80, 50, "Abort" );
  481. o->box( FL_UP_BOX );
  482. o->color( FL_RED );
  483. o->callback( cb_handle, (void*)this );
  484. }
  485. { Fl_Button *o = save_button = new Fl_Button( 0, 0, 80, 50, "Save" );
  486. o->box( FL_UP_BOX );
  487. o->callback( cb_handle, (void*)this );
  488. }
  489. { Fl_Button *o = new_button = new Fl_Button( 0, 0, 80, 50, "New" );
  490. o->box( FL_UP_BOX );
  491. o->callback( cb_handle, (void*)this );
  492. }
  493. { Fl_Button *o = duplicate_button = new Fl_Button( 0, 0, 100, 50, "Duplicate" );
  494. o->box( FL_UP_BOX );
  495. o->callback( cb_handle, (void*)this );
  496. }
  497. { Fl_Button *o = add_button = new Fl_Button( 0, 0, 100, 100, "Add Client" );
  498. o->box( FL_UP_BOX );
  499. o->callback( cb_handle, (void*)this );
  500. }
  501. o->end();
  502. add(o);
  503. }
  504. { Fl_Tile *o = new Fl_Tile( X, Y + 50, W, H - 50 );
  505. {
  506. Fl_Select_Browser *o = session_browser = new Fl_Select_Browser( X, Y + 50, W / 3, H - 50 );
  507. o->callback( cb_handle, (void *)this );
  508. o->color( fl_darker( FL_GRAY ) );
  509. o->box( FL_ROUNDED_BOX );
  510. o->label( "Sessions" );
  511. }
  512. {
  513. Fl_Packscroller *o = new Fl_Packscroller( X + ( W / 3 ), Y + 50, ( W / 3 ) * 2, H - 50 );
  514. o->align( FL_ALIGN_TOP );
  515. o->labeltype( FL_SHADOW_LABEL );
  516. {
  517. Fl_Pack *o = clients_pack = new Fl_Pack( X + ( W / 3 ), Y + 50, ( W / 3 ) * 2, H - 50 );
  518. o->align( FL_ALIGN_TOP );
  519. o->type( Fl_Pack::VERTICAL );
  520. o->end();
  521. Fl_Group::current()->resizable( o );
  522. }
  523. o->end();
  524. }
  525. o->end();
  526. }
  527. Fl_Group::current()->resizable( this );
  528. end();
  529. deactivate();
  530. }
  531. int min_h ( void )
  532. {
  533. return 500;
  534. }
  535. void
  536. ping ( void )
  537. {
  538. if ( daemon_list.size() )
  539. {
  540. foreach_daemon( d )
  541. {
  542. osc->send( (*d)->addr, "/osc/ping" );
  543. }
  544. }
  545. if ( last_ping_response )
  546. {
  547. if ( time(NULL) - last_ping_response > 10 )
  548. {
  549. if ( active() )
  550. {
  551. deactivate();
  552. fl_alert( "Server is not responding..." );
  553. }
  554. }
  555. }
  556. }
  557. int init_osc ( void )
  558. {
  559. osc = new OSC::Endpoint();
  560. if ( int r = osc->init( LO_UDP ) )
  561. return r;
  562. osc->owner = this;
  563. osc->url();
  564. osc->add_method( "/error", "sis", osc_handler, osc, "msg" );
  565. osc->add_method( "/reply", "ss", osc_handler, osc, "msg" );
  566. osc->add_method( "/reply", "s", osc_handler, osc, "" );
  567. osc->add_method( "/nsm/server/broadcast", NULL, osc_broadcast_handler, osc, "msg" );
  568. osc->add_method( "/nsm/gui/server_announce", "s", osc_handler, osc, "msg" );
  569. osc->add_method( "/nsm/gui/gui_announce", "s", osc_handler, osc, "msg" );
  570. osc->add_method( "/nsm/gui/session/session", "s", osc_handler, osc, "path,display_name" );
  571. osc->add_method( "/nsm/gui/session/name", "s", osc_handler, osc, "path,display_name" );
  572. osc->add_method( "/nsm/gui/client/new", "ss", osc_handler, osc, "path,display_name" );
  573. osc->add_method( "/nsm/gui/client/status", "ss", osc_handler, osc, "path,display_name" );
  574. osc->add_method( "/nsm/gui/client/switch", "ss", osc_handler, osc, "path,display_name" );
  575. osc->add_method( "/nsm/gui/client/progress", "sf", osc_handler, osc, "path,display_name" );
  576. osc->add_method( "/nsm/gui/client/dirty", "si", osc_handler, osc, "path,display_name" );
  577. osc->start();
  578. return 0;
  579. }
  580. void announce ( const char *nsm_url )
  581. {
  582. /* Daemon *d = new Daemon; */
  583. /* d->url = nsm_url; */
  584. lo_address nsm_addr = lo_address_new_from_url( nsm_url );
  585. // d->is_child = true;
  586. /* daemon_list.push_back( d ); */
  587. osc->send( nsm_addr, "/nsm/gui/gui_announce" );
  588. }
  589. private:
  590. static int osc_broadcast_handler ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
  591. {
  592. DMESSAGE( "Relaying broadcast" );
  593. foreach_daemon( d )
  594. {
  595. char *u1 = lo_address_get_url( (*d)->addr );
  596. char *u2 = lo_address_get_url( lo_message_get_source( msg ) );
  597. if ( strcmp( u1, u2 ) )
  598. {
  599. osc->send( (*d)->addr, path, msg );
  600. }
  601. free( u1 );
  602. free( u2 );
  603. }
  604. return 0;
  605. }
  606. static int osc_handler ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
  607. {
  608. // OSC_DMSG();
  609. NSM_Controller *controller = (NSM_Controller*)((OSC::Endpoint*)user_data)->owner;
  610. Fl::lock();
  611. if ( !strcmp( path, "/nsm/gui/session/session" ) )
  612. {
  613. controller->add_session_to_list( &argv[0]->s );
  614. controller->sort_sessions();
  615. }
  616. else if ( !strcmp( path, "/nsm/gui/gui_announce" ) )
  617. {
  618. /* pre-existing server is replying to our announce message */
  619. controller->activate();
  620. lo_address nsm_addr = lo_message_get_source( msg );
  621. osc->send( nsm_addr, "/nsm/server/list" );
  622. }
  623. else if ( !strcmp( path, "/nsm/gui/server_announce" ) )
  624. {
  625. /* must be a server we launched */
  626. controller->activate();
  627. Daemon *d = new Daemon;
  628. d->url = lo_address_get_url( lo_message_get_source( msg ) );
  629. d->addr = lo_address_new_from_url( d->url );
  630. d->is_child = true;
  631. daemon_list.push_back( d );
  632. osc->send( d->addr, "/nsm/server/list" );
  633. }
  634. else if ( !strcmp( path, "/nsm/gui/session/name" ))
  635. {
  636. controller->session_name( &argv[0]->s );
  637. }
  638. else if (!strcmp( path, "/error" ))
  639. {
  640. int err = argv[1]->i;
  641. if ( err != 0 )
  642. fl_alert( "ERROR: %s failed with: %s", &argv[0]->s, &argv[2]->s );
  643. }
  644. else if (!strcmp( path, "/reply" ))
  645. {
  646. if ( !strcmp( &argv[0]->s, "/nsm/server/list" ) )
  647. {
  648. controller->add_session_to_list( &argv[1]->s );
  649. controller->sort_sessions();
  650. }
  651. else if ( !strcmp( &argv[0]->s, "/osc/ping" ) )
  652. {
  653. last_ping_response = time( NULL );
  654. }
  655. else
  656. MESSAGE( "%s says %s", &argv[0]->s, &argv[1]->s);
  657. }
  658. if ( !strncmp( path, "/nsm/gui/client/", strlen( "/nsm/gui/client/" ) ) )
  659. {
  660. if ( !strcmp( path, "/nsm/gui/client/new" ))
  661. {
  662. controller->client_new( &argv[0]->s, &argv[1]->s );
  663. }
  664. else
  665. {
  666. NSM_Client *c = controller->client_by_id( &argv[0]->s );
  667. if ( c )
  668. {
  669. if ( !strcmp( path, "/nsm/gui/client/status" ))
  670. {
  671. controller->client_pending_command( c, &argv[1]->s );
  672. }
  673. else if ( !strcmp( path, "/nsm/gui/client/progress" ))
  674. {
  675. c->progress( argv[1]->f );
  676. }
  677. else if ( !strcmp( path, "/nsm/gui/client/dirty" ))
  678. {
  679. c->dirty( argv[1]->i );
  680. }
  681. else if ( !strcmp( path, "/nsm/gui/client/switch" ) )
  682. {
  683. c->client_id( &argv[1]->s );
  684. }
  685. }
  686. else
  687. MESSAGE( "Got message %s from unknown client", path );
  688. }
  689. }
  690. Fl::unlock();
  691. Fl::awake();
  692. return 0;
  693. }
  694. };
  695. static NSM_Controller *controller;
  696. void
  697. ping ( void *v )
  698. {
  699. controller->ping();
  700. Fl::repeat_timeout( 1.0, ping, NULL );
  701. }
  702. int
  703. main (int argc, char **argv )
  704. {
  705. Fl::get_system_colors();
  706. Fl::scheme( "plastic" );
  707. Fl::lock();
  708. Fl_Double_Window *main_window;
  709. {
  710. Fl_Double_Window *o = main_window = new Fl_Double_Window( 600, 800, APP_NAME );
  711. {
  712. main_window->xclass( APP_NAME );
  713. Fl_Widget *o = controller = new NSM_Controller( 0, 0, main_window->w(), main_window->h(), NULL );
  714. Fl_Group::current()->resizable(o);
  715. }
  716. o->end();
  717. o->size_range( main_window->w(), controller->min_h(), 0, 0 );
  718. // o->callback( (Fl_Callback*)cb_main, main_window );
  719. o->show( argc, argv );
  720. // o->show();
  721. }
  722. static struct option long_options[] =
  723. {
  724. { "nsm-url", required_argument, 0, 'n' },
  725. { "help", no_argument, 0, 'h' },
  726. { 0, 0, 0, 0 }
  727. };
  728. int option_index = 0;
  729. int c = 0;
  730. const char *osc_port = NULL;
  731. while ( ( c = getopt_long_only( argc, argv, "", long_options, &option_index ) ) != -1 )
  732. {
  733. switch ( c )
  734. {
  735. case 'n':
  736. {
  737. DMESSAGE( "Adding %s to daemon list", optarg );
  738. Daemon *d = new Daemon;
  739. d->url = optarg;
  740. d->addr = lo_address_new_from_url( optarg );
  741. daemon_list.push_back( d );
  742. break;
  743. }
  744. case 'h':
  745. printf( "Usage: %s [--nsmd-url...] [-- server options ]\n\n", argv[0] );
  746. exit(0);
  747. break;
  748. }
  749. }
  750. const char *nsm_url = getenv( "NSM_URL" );
  751. if ( nsm_url )
  752. {
  753. MESSAGE( "Found NSM URL of \"%s\" in environment, attempting to connect.", nsm_url );
  754. Daemon *d = new Daemon;
  755. d->url = nsm_url;
  756. d->addr = lo_address_new_from_url( nsm_url );
  757. daemon_list.push_back( d );
  758. }
  759. if ( controller->init_osc() )
  760. FATAL( "Could not create OSC server" );
  761. if ( daemon_list.size() )
  762. {
  763. foreach_daemon ( d )
  764. {
  765. controller->announce( (*d)->url );
  766. }
  767. }
  768. else
  769. {
  770. /* start a new daemon... */
  771. MESSAGE( "Starting daemon..." );
  772. char *url = osc->url();
  773. if ( ! fork() )
  774. {
  775. /* pass non-option arguments on to daemon */
  776. option_index += 2;
  777. char **args = (char **)malloc( 4 + argc - option_index );
  778. int i = 0;
  779. args[i++] = "nsmd";
  780. args[i++] = "--gui-url";
  781. args[i++] = url;
  782. for ( ; option_index < argc; i++, option_index++ )
  783. {
  784. DMESSAGE( "Passing argument: %s", argv[option_index] );
  785. args[i] = argv[option_index];
  786. }
  787. args[i] = 0;
  788. if ( -1 == execvp( "nsmd", args ) )
  789. {
  790. FATAL( "Error starting process: %s", strerror( errno ) );
  791. }
  792. }
  793. }
  794. Fl::add_timeout( 1.0, ping, NULL );
  795. Fl::run();
  796. foreach_daemon ( d )
  797. {
  798. if ( (*d)->is_child )
  799. {
  800. MESSAGE( "Telling server to quit" );
  801. osc->send( (*d)->addr, "/nsm/server/quit" );
  802. }
  803. }
  804. return 0;
  805. }