diff --git a/session-manager/configure b/session-manager/configure index 6e6a268..1ec1288 100755 --- a/session-manager/configure +++ b/session-manager/configure @@ -17,6 +17,7 @@ begin_tests require_command ar ar require_command makedepend makedepend require_FLTK 1.3.0 images +require_command FLUID lib/fltk/fluid/fluid suggest_package JACK 0.103.0 jack require_package liblo 0.23 liblo require_package sigcpp 2.0.0 sigc++-2.0 diff --git a/session-manager/makefile.inc b/session-manager/makefile.inc index 0c9330d..ac05471 100644 --- a/session-manager/makefile.inc +++ b/session-manager/makefile.inc @@ -24,23 +24,35 @@ src/session-manager: src/session-manager.o nonlib/libnonlib.a $(FLTK_LIBS) @ echo -n Linking session handler. @ $(CXX) $(CXXFLAGS) $(FLTK_LDFLAGS) $(XPM_LIBS) $(LIBS) $^ -o $@ -LFL -lfl_widgets -Lnonlib -lnonlib && echo $(DONE) +src/nsm-proxy: src/nsm-proxy.o nonlib/libnonlib.a + @ echo -n Linking NSM Proxy. + @ $(CXX) $(CXXFLAGS) $(LIBS) $^ -o $@ -Lnonlib -lnonlib && echo $(DONE) + +# src/NSM_Proxy_UI.o: +# @ cd src && $(FLUID) -c NSM_Proxy_UI.fl + +src/nsm-proxy-gui: src/NSM_Proxy_UI.o src/nsm-proxy-gui.o nonlib/libnonlib.a $(FLTK_LIBS) + @ echo -n Linking NSM Proxy. + @ $(CXX) $(CXXFLAGS) $(FLTK_LDFLAGS) $(XPM_LIBS) $(LIBS) $^ -o $@ -LFL -lfl_widgets -Lnonlib -lnonlib && echo $(DONE) + src/send_osc: src/send_osc.o nonlib/libnonlib.a @ $(CXX) $(CXXFLAGS) $(LIBS) $^ -o $@ -Lnonlib -lnonlib && echo $(DONE) src/jackpatch: src/jackpatch.o @ $(CXX) $(CXXFLAGS) $(JACK_LIBS) $(LIBLO_LIBS) $^ -o $@ && echo $(DONE) -Session: src/send_osc src/nsmd src/session-manager src/jackpatch + +Session: src/send_osc src/nsmd src/session-manager src/jackpatch src/nsm-proxy src/nsm-proxy-gui clean: - rm -f $(OBJS) src/nsmd src/session-manager src/jackpatch .deps - - + rm -f $(OBJS) src/nsmd src/session-manager src/jackpatch src/nsm-proxy src/nsm-proxy-gui .deps install: all @ echo -n "Installing..." @ install -Dm755 src/nsmd "$(DESTDIR)$(prefix)"/bin/nsmd @ install -Dm755 src/session-manager "$(DESTDIR)$(prefix)"/bin/non-session-manager + @ install -Dm755 src/nsm-proxy "$(DESTDIR)$(prefix)"/bin/nsm-proxy + @ install -Dm755 src/nsm-proxy-gui "$(DESTDIR)$(prefix)"/bin/nsm-proxy-gui @ install -Dm755 src/jackpatch "$(DESTDIR)$(prefix)"/bin/jackpatch @ install -d "$(DESTDIR)$(SYSTEM_PATH)"/non-session-manager @ $(MAKE) -s -C doc install diff --git a/session-manager/src/NSM_Proxy_UI.fl b/session-manager/src/NSM_Proxy_UI.fl new file mode 100644 index 0000000..91cde92 --- /dev/null +++ b/session-manager/src/NSM_Proxy_UI.fl @@ -0,0 +1,64 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0300 +header_name {.H} +code_name {.C} +class NSM_Proxy_UI {open +} { + Function {make_window()} {open + } { + Fl_Window {} { + label {NSM Proxy} open selected + xywh {106 75 485 475} type Double visible + } { + Fl_Box {} { + label {Command-line options are incompatible with robust session management, so the NSM protocol does not support them. This proxy exists to allow programs which require command-line options to be included in an NSM session. Be warned that referring to files outside of the session directory will impair your ability to reliably archive and transport sessions. Patching the program to use NSM natively will result in a better experience. + +The program will be started with its current directory being a uniquely named directory under the current session directory. It is recommended that you only refer to files in the current directory. + +The environment variables $NSM_CLIENT_ID and $NSM_SESSION_NAME will contain the unique client ID (suitable for use as e.g. a JACK client name) and the display name for the session, respectively.} + xywh {15 14 455 286} box ROUNDED_BOX color 64 labelcolor 55 align 128 + } + Fl_File_Input executable_input { + label {Executable: } + xywh {115 316 350 33} + } + Fl_Input arguments_input { + label {Arguments:} + xywh {115 358 350 28} + } + Fl_Input label_input { + label {Label:} + xywh {115 396 350 28} + } + Fl_Return_Button start_button { + label Start + xywh {375 435 88 25} + } + Fl_Button kill_button { + label Kill + xywh {290 435 80 25} color 72 + } + Fl_Choice save_signal_choice { + label {Save Signal:} open + tooltip {Some programs may respond to a certain Unix signal by saving their state to the file specified on the command-line. Usually if this is the case, the signal will be SIGUSR1. However, if you pick the wrong one, the client will likely respond as if to KILL.} xywh {115 435 170 25} down_box BORDER_BOX + } { + MenuItem {} { + label None + xywh {0 0 40 24} + } + MenuItem {} { + label SIGUSR1 + xywh {10 10 40 24} + } + MenuItem {} { + label SIGUSR2 + xywh {20 20 40 24} + } + MenuItem {} { + label SIGINT + xywh {30 30 40 24} + } + } + } + } +} diff --git a/session-manager/src/nsm-proxy-gui.C b/session-manager/src/nsm-proxy-gui.C new file mode 100644 index 0000000..e514c76 --- /dev/null +++ b/session-manager/src/nsm-proxy-gui.C @@ -0,0 +1,186 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#define _GNU_SOURCE +#pragma GCC diagnostic ignored "-Wunused-parameter" + + +#define _MODULE_ "nsm-proxy-gui" + +#define APP_NAME "NSM Proxy" +#define APP_TITLE "NSM Proxy" + +#include "NSM_Proxy_UI.H" +#include +#include +#include +#include +#include + +lo_server losrv; +lo_address nsmp_addr; + +static NSM_Proxy_UI *ui; + +int +osc_update ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + printf( "Got update for %s\n", path ); + + Fl::lock(); + + if (!strcmp( path, "/nsm/proxy/label" )) + ui->label_input->value( &argv[0]->s ); + else if (!strcmp( path, "/nsm/proxy/arguments" )) + ui->arguments_input->value( &argv[0]->s ); + else if (!strcmp( path, "/nsm/proxy/executable" )) + ui->executable_input->value( &argv[0]->s ); + else if (!strcmp( path, "/nsm/proxy/save_signal" )) + { + if ( argv[0]->i == SIGUSR1 ) + ui->save_signal_choice->value( 1 ); + else if ( argv[0]->i == SIGUSR2 ) + ui->save_signal_choice->value( 2 ); + else if ( argv[0]->i == SIGINT ) + ui->save_signal_choice->value( 3 ); + else + ui->save_signal_choice->value( 0 ); + } + + Fl::unlock(); + + return 0; +} + + +void +init_osc ( const char *osc_port ) +{ + + lo_server_thread loth = lo_server_thread_new( osc_port, NULL ); + losrv = lo_server_thread_get_server( loth ); + +//error_handler ); + + char *url = lo_server_get_url(losrv); + printf("OSC: %s\n",url); + free(url); + + /* GUI */ + + lo_server_thread_add_method( loth, "/nsm/proxy/executable", "s", osc_update, NULL ); + lo_server_thread_add_method( loth, "/nsm/proxy/arguments", "s", osc_update, NULL ); + lo_server_thread_add_method( loth, "/nsm/proxy/label", "s", osc_update, NULL ); + lo_server_thread_add_method( loth, "/nsm/proxy/save_signal", "i", osc_update, NULL ); + + lo_server_thread_start( loth ); +} + +/*****************/ +/* GUI Callbacks */ +/*****************/ + +void +handle_kill ( Fl_Widget *o, void *v ) +{ + lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/kill", "" ); +} + +void +handle_start ( Fl_Widget *o, void *v ) +{ + lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/start", "ss", + ui->executable_input->value(), + ui->arguments_input->value() ); +} + +void +handle_label ( Fl_Widget *o, void *v ) +{ + lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/label", "s", + ui->label_input->value() ); +} + +void +handle_executable ( Fl_Widget *o, void *v ) +{ + ui->label_input->value( ui->executable_input->value() ); +} + +void +handle_save_signal ( Fl_Widget *o, void *v ) +{ + int sig = 0; + + const char* picked = ui->save_signal_choice->mvalue()->label(); + + if ( !strcmp( picked, "SIGUSR1" ) ) + sig = SIGUSR1; + else if ( !strcmp( picked, "SIGUSR2" ) ) + sig = SIGUSR2; + else if ( !strcmp( picked, "SIGINT" ) ) + sig = SIGINT; + + lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE,"/nsm/proxy/save_signal", "i", + sig ); +} + +void +connect_ui ( void ) +{ + ui->executable_input->callback( handle_executable, NULL ); + ui->kill_button->callback( handle_kill, NULL ); + ui->start_button->callback( handle_start, NULL ); + ui->save_signal_choice->callback( handle_save_signal, NULL ); + ui->label_input->callback( handle_label, NULL ); +} + + + +int +main ( int argc, char **argv ) +{ + if ( argc != 3 ) + { + fprintf( stderr, "Usage: %s --connect-to url\n", argv[0] ); + return 1; + } + + init_osc( NULL ); + + nsmp_addr = lo_address_new_from_url( argv[2] ); + + printf( "Connecting to nsm-proxy at: %s\n", argv[2] ); + + ui = new NSM_Proxy_UI; + + Fl_Double_Window *w = ui->make_window(); + + connect_ui(); + + lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/update", "" ); + + w->show(); + + Fl::lock(); + + Fl::run(); + + return 0; +} diff --git a/session-manager/src/nsm-proxy.C b/session-manager/src/nsm-proxy.C new file mode 100644 index 0000000..3acbec6 --- /dev/null +++ b/session-manager/src/nsm-proxy.C @@ -0,0 +1,636 @@ + +/*******************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */ +/* more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with This program; see the file COPYING. If not,write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*******************************************************************************/ + +#define _GNU_SOURCE +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#define _MODULE_ "nsm-proxy" +#define APP_NAME "NSM Proxy" +#define APP_TITLE "NSM Proxy" + +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static lo_server losrv; +static lo_address nsm_addr; +static lo_address gui_addr; +static int nsm_is_active; +static char *project_file; +static int die_now = 0; +static int signal_fd; + +static char *nsm_client_id; +static char *nsm_display_name; + +#define CONFIG_FILE_NAME "nsm-proxy.config" + +class NSM_Proxy { + + char *_label; + char *_executable; + char *_arguments; + int _save_signal; + int _pid; + +public: + + NSM_Proxy ( ) + { + _label = _executable = _arguments = 0; + _save_signal = 0; + _pid = 0; + } + + ~NSM_Proxy ( ) + { + } + + void kill ( void ) + { + if ( _pid ) + ::kill( _pid, SIGTERM ); + } + + void start ( const char *executable, const char *arguments ) + { + if ( _executable ) + free( _executable ); + if ( _arguments ) + free( _arguments ); + + _executable = strdup( executable ); + _arguments = strdup( arguments ); + + start(); + } + + void start ( void ) + { + int pid; + if ( ! (pid = fork()) ) + { + MESSAGE( "Launching %s\n", _executable ); + +// char *args[] = { strdup( executable ), NULL }; + + char *cmd; + asprintf( &cmd, "exec %s %s", _executable, _arguments ); + + char *args[] = { _executable, "-c", cmd, NULL }; + + setenv( "NSM_CLIENT_ID", nsm_client_id, 1 ); + setenv( "NSM_SESSION_NAME", nsm_display_name, 1 ); + + if ( -1 == execvp( "/bin/sh", args ) ) + { + WARNING( "Error starting process: %s", strerror( errno ) ); + + exit(-1); + } + } + + _pid = pid; + + } + + void save_signal ( int s ) + { + _save_signal = s; + } + + void label ( const char *s ) + { + if ( _label ) + free( _label ); + + _label = strdup( s ); + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/label", "s", _label ); + } + + void save ( void ) + { + DMESSAGE( "Sending process save signal" ); + if ( _pid ) + ::kill( _pid, _save_signal ); + } + + + bool dump ( const char *path ) + { + FILE *fp = fopen( path, "w" ); + + if ( !fp ) + { + WARNING( "Error opening file for saving: %s", strerror( errno ) ); + return false; + } + + if ( _executable && strlen(_executable) ) + fprintf( fp, "executable\n\t%s\n", _executable ); + + if ( _arguments && strlen(_arguments) ) + fprintf( fp, "arguments\n\t%s\n", _arguments ); + + fprintf( fp, "save signal\n\t%i\n", _save_signal ); + + if ( _label && strlen(_label) ) + fprintf( fp, "label\n\t%s\n", _label ); + + fclose( fp ); + + return true; + } + + bool restore ( const char *path ) + { + FILE *fp = fopen( path, "r" ); + + if ( ! fp ) + { + WARNING( "Error opening file for restore: %s", strerror( errno ) ); + return false; + } + + char *name; + char *value; + + MESSAGE( "Loading file config \"%s\"", path ); + + while ( 2 == fscanf( fp, "%a[^\n]\n\t%a[^\n]\n", &name, &value ) ) + { + + DMESSAGE( "%s=%s", name, value ); + + if ( !strcmp( name, "executable" ) ) + _executable = value; + else if (!strcmp( name, "arguments" ) ) + _arguments = value; + else if ( !strcmp( name, "save signal" ) ) + { + _save_signal = atoi( value ); + free( value ); + } + else if ( !strcmp( name, "label" ) ) + { + label( value ); + free( value ); + } + else + { + WARNING( "Unknown option \"%s\" in config file", name ); + } + + free( name ); + } + + fclose( fp ); + + start(); + + return true; + } + + void update ( lo_address to ) + { + DMESSAGE( "Sending update" ); + + lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/save_signal", "i", _save_signal ); + lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/label", "s", _label ? _label : "" ); + lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/executable", "s", _executable ? _executable : "" ); + lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/arguments", "s", _arguments ? _arguments : "" ); + } +}; + +NSM_Proxy *nsm_proxy; + +void +announce ( const char *nsm_url, const char *client_name, const char *process_name ) +{ + printf( "Announcing to NSM\n" ); + + lo_address to = lo_address_new_from_url( nsm_url ); + + int pid = (int)getpid(); + + lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", + client_name, + ":optional-gui:", + process_name, + 1, /* api_major_version */ + 0, /* api_minor_version */ + pid ); + + lo_address_free( to ); +} + +bool +snapshot ( const char *file ) +{ + /* mkdir( file, 0777 ); */ + + char *path; + asprintf( &path, "%s/%s", file, CONFIG_FILE_NAME ); + + bool r = nsm_proxy->dump( path ); + + free( path ); + + return r; +} + +bool +open ( const char *file ) +{ + char *path; + asprintf( &path, "%s/%s", file, CONFIG_FILE_NAME ); + + bool r = nsm_proxy->restore( path ); + + free( path ); + + return r; +} + +/****************/ +/* OSC HANDLERS */ +/****************/ + +/* NSM */ + +int +osc_announce_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + if ( strcmp( types, "sis" ) ) + return -1; + + if ( strcmp( "/nsm/server/announce", &argv[0]->s ) ) + return -1; + + printf( "Failed to register with NSM: %s\n", &argv[2]->s ); + nsm_is_active = 0; + + return 0; +} + + +int +osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + if ( strcmp( "/nsm/server/announce", &argv[0]->s ) ) + return -1; + + printf( "Successfully registered. NSM says: %s", &argv[1]->s ); + + nsm_is_active = 1; + nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ) ); + + return 0; +} + +int +osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + bool r = snapshot( project_file ); + + nsm_proxy->save(); + + if ( r ) + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" ); + else + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/error", "sis", path, -1, "Error saving project file" ); + + return 0; +} + +static int gui_pid; + +void +show_gui ( void ) +{ + + int pid; + if ( ! (pid = fork()) ) + { + char executable[] = "nsm-proxy-gui"; + + MESSAGE( "Launching %s\n", executable ); + + char *url = lo_server_get_url( losrv ); + + char *args[] = { executable, "--connect-to", url, NULL }; + + if ( -1 == execvp( executable, args ) ) + { + WARNING( "Error starting process: %s", strerror( errno ) ); + + exit(-1); + } + } + + gui_pid = pid; + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "" ); +} + +int +osc_show_gui ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + show_gui(); + + /* FIXME: detect errors */ + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" ); + + return 0; +} + +int +osc_hide_gui ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + if ( gui_pid ) + kill( gui_pid, SIGTERM ); + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" ); + + /* FIXME: detect errors */ + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" ); + + return 0; +} + +int +osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + const char *new_path = &argv[0]->s; + const char *display_name = &argv[1]->s; + const char *client_id = &argv[2]->s; + + if ( nsm_client_id ) + free(nsm_client_id); + + nsm_client_id = strdup( client_id ); + + if ( nsm_display_name ) + free( nsm_display_name ); + + nsm_display_name = strdup( display_name ); + + char *new_filename; + + mkdir( new_path, 0777 ); + + chdir( new_path ); + + asprintf( &new_filename, "%s/%s", new_path, CONFIG_FILE_NAME ); + + struct stat st; + + if ( 0 == stat( new_filename, &st ) ) + { + if ( open( new_path ) ) + { + } + else + { + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/error", "sis", path, -1, "Could not open file" ); + return 0; + } + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" ); + } + else + { + show_gui(); + } + + if ( project_file ) + free( project_file ); + + project_file = strdup( new_path ); + +// new_filename; + + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" ); + + if ( gui_addr ) + nsm_proxy->update( gui_addr ); + + return 0; +} + + + +/* GUI */ + +int +osc_label ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + nsm_proxy->label( &argv[0]->s ); + + return 0; +} + +int +osc_save_signal ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + nsm_proxy->save_signal( argv[0]->i ); + + return 0; +} + +int +osc_start ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + nsm_proxy->start( &argv[0]->s, &argv[1]->s ); + + return 0; +} + +int +osc_kill ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + nsm_proxy->kill(); + + return 0; +} + +int +osc_update ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) +{ + lo_address to = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) )); + + nsm_proxy->update( to ); + + gui_addr = to; + + return 0; +} + + + +void +signal_handler ( int x ) +{ + die_now = 1; +} + +void +set_traps ( void ) +{ + signal( SIGHUP, signal_handler ); + signal( SIGINT, signal_handler ); +// signal( SIGQUIT, signal_handler ); +// signal( SIGSEGV, signal_handler ); +// signal( SIGPIPE, signal_handler ); + signal( SIGTERM, signal_handler ); +} + + +void +init_osc ( const char *osc_port ) +{ + losrv = lo_server_new( osc_port, NULL ); +//error_handler ); + + char *url = lo_server_get_url(losrv); + printf("OSC: %s\n",url); + free(url); + + /* NSM */ + lo_server_add_method( losrv, "/nsm/client/save", "", osc_save, NULL ); + lo_server_add_method( losrv, "/nsm/client/open", "sss", osc_open, NULL ); + lo_server_add_method( losrv, "/nsm/client/show_optional_gui", "", osc_show_gui, NULL ); + lo_server_add_method( losrv, "/nsm/client/hide_optional_gui", "", osc_hide_gui, NULL ); + lo_server_add_method( losrv, "/error", "sis", osc_announce_error, NULL ); + lo_server_add_method( losrv, "/reply", "ssss", osc_announce_reply, NULL ); + + /* GUI */ + lo_server_add_method( losrv, "/nsm/proxy/label", "s", osc_label, NULL ); + lo_server_add_method( losrv, "/nsm/proxy/save_signal", "i", osc_save_signal, NULL ); + lo_server_add_method( losrv, "/nsm/proxy/kill", "", osc_kill, NULL ); + lo_server_add_method( losrv, "/nsm/proxy/start", "ss", osc_start, NULL ); + lo_server_add_method( losrv, "/nsm/proxy/update", "", osc_update, NULL ); + +} + +void +die ( void ) +{ + if ( gui_pid ) + { + DMESSAGE( "Killing GUI" ); + + kill( gui_pid, SIGTERM ); + } + + nsm_proxy->kill(); + + exit(0); +} + + +void handle_sigchld ( ) +{ + for ( ;; ) + { + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + + if (pid <= 0) + break; + + if ( pid == gui_pid ) + { + lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" ); + + /* don't care... */ + continue; + } + + /* otherwise, it was our proxied process that died, so we should die too */ + printf( "proxied process died... nsm-proxy dying too\n" ); + + die_now = 1; + } +} + +int +main ( int argc, char **argv ) +{ + set_traps(); + + sigset_t mask; + + sigemptyset( &mask ); + sigaddset( &mask, SIGCHLD ); + + sigprocmask(SIG_BLOCK, &mask, NULL ); + + signal_fd = signalfd( -1, &mask, SFD_NONBLOCK ); + + nsm_proxy = new NSM_Proxy(); + + init_osc( NULL ); + + const char *nsm_url = getenv( "NSM_URL" ); + + if ( nsm_url ) + { + announce( nsm_url, APP_TITLE, argv[0] ); + } + else + { + fprintf( stderr, "Could not register as NSM client.\n" ); + exit(1); + } + + + struct signalfd_siginfo fdsi; + + /* listen for sigchld signals and process OSC messages forever */ + for ( ;; ) + { + ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo)); + + if (s == sizeof(struct signalfd_siginfo)) + { + if (fdsi.ssi_signo == SIGCHLD) + handle_sigchld(); + } + + lo_server_recv_noblock( losrv, 500 ); + + if ( die_now ) + die(); + } +}