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.

1441 lines
43KB

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