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.

1378 lines
41KB

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