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.

1443 lines
43KB

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