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.

1274 lines
37KB

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