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.

1080 lines
31KB

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