Browse Source

Add non-session-manager, nsm-proxy and nsm-proxy-gui targets

Signed-off-by: falkTX <falktx@falktx.com>
tags/v1.3
falkTX 5 years ago
parent
commit
4892c114aa
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
8 changed files with 3135 additions and 5 deletions
  1. +39
    -5
      meson.build
  2. +212
    -0
      src/FL/Fl_Packscroller.H
  3. +250
    -0
      src/FL/Fl_Scalepack.C
  4. +43
    -0
      src/FL/Fl_Scalepack.H
  5. +100
    -0
      src/NSM_Proxy_UI.fl
  6. +291
    -0
      src/nsm-proxy-gui.cpp
  7. +760
    -0
      src/nsm-proxy.cpp
  8. +1440
    -0
      src/session-manager.cpp

+ 39
- 5
meson.build View File

@@ -23,11 +23,10 @@ liblodep = dependency('liblo') #and not 'lo'
threaddep = dependency('threads')
jackdep = dependency('jack') #and not 'libjack'

executable('nsmd',
sources: ['src/nsmd.cpp', 'src/debug.cpp', 'src/Endpoint.cpp', 'src/file.cpp', 'src/Thread.cpp'],
dependencies: [liblodep, threaddep],
install: true,
)
cc = meson.get_compiler('c')
fluid = find_program('fluid')
fltkdep = cc.find_library('fltk', required: true)
fltkimagesdep = cc.find_library('fltk_images', required: true)

executable('jackpatch',
'src/jackpatch.c',
@@ -35,5 +34,40 @@ executable('jackpatch',
install: true,
)

executable('non-session-manager',
sources: ['src/session-manager.cpp', 'src/debug.cpp', 'src/Endpoint.cpp', 'src/Thread.cpp', 'src/FL/Fl_Scalepack.C'],
dependencies: [fltkimagesdep, fltkdep, liblodep, threaddep],
install: true,
)

executable('nsm-proxy',
sources: ['src/nsm-proxy.cpp', 'src/debug.cpp'],
dependencies: [liblodep, threaddep],
install: true,
)

NSM_Proxy_UI_cpp = custom_target(
'NSM_Proxy_UI.cpp',
output : 'NSM_Proxy_UI.cpp',
input : 'src/NSM_Proxy_UI.fl',
command : [fluid, '-c', '-o', '@OUTPUT@', '@INPUT@'],
)

NSM_Proxy_UI_h = custom_target(
'NSM_Proxy_UI.h',
output : 'NSM_Proxy_UI.h',
input : 'src/NSM_Proxy_UI.fl',
command : [fluid, '-c', '-h', '@OUTPUT@', '@INPUT@'],
)

executable('nsm-proxy-gui',
sources: ['src/nsm-proxy-gui.cpp', [NSM_Proxy_UI_cpp, NSM_Proxy_UI_h]],
dependencies: [fltkdep, liblodep, threaddep],
install: true,
)

executable('nsmd',
sources: ['src/nsmd.cpp', 'src/debug.cpp', 'src/Endpoint.cpp', 'src/file.cpp', 'src/Thread.cpp'],
dependencies: [liblodep, threaddep],
install: true,
)

+ 212
- 0
src/FL/Fl_Packscroller.H View File

@@ -0,0 +1,212 @@

/*******************************************************************************/
/* Copyright (C) 2010 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. */
/*******************************************************************************/

/* Scrolling group suitable for containing a single child (a
* pack). When the Fl_Packscroller is resized, the child will be resized
* too. No scrollbars are displayed, but the widget responds to
* FL_MOUSEWHEEL events. */

#pragma once

#include <FL/Fl_Group.H>
#include <FL/fl_draw.H>
#include <FL/Fl.H>

/* FIXME: Optimize scroll */

class Fl_Packscroller : public Fl_Group
{
int _increment;
int _yposition;
static const int sbh = 15; /* scroll button height */

public:

Fl_Packscroller ( int X, int Y, int W, int H, const char *L = 0 ) : Fl_Group( X, Y, W, H, L )
{
_increment = 30;
_yposition = 0;
// color( FL_WHITE );
}

int increment ( void ) const { return _increment; }
void increment ( int v ) { _increment = v; }

void yposition ( int v )
{
if ( ! children() )
return;

int Y = v;

if ( Y > 0 )
Y = 0;

const int H = h();
// - (sbh * 2);

Fl_Widget *o = child( 0 );

if ( o->h() > H &&
Y + o->h() < H )
Y = H - o->h();
else if ( o->h() < H )
Y = 0;

if ( _yposition != Y )
{
_yposition = Y;

damage( FL_DAMAGE_SCROLL );
}
}

int yposition ( void ) const
{
if ( children() )
return child( 0 )->y() - (y() + sbh);

return 0;
}

void bbox ( int &X, int &Y, int &W, int &H )
{
X = x();
Y = y() + ( sbh * top_sb_visible() );
W = w();
H = h() - ( sbh * ( top_sb_visible() + bottom_sb_visible() ) );
}

int top_sb_visible ( void )
{
return children() && child(0)->y() != y() ? 1 : 0;
}

int bottom_sb_visible ( void )
{
if ( children() )
{
Fl_Widget *o = child( 0 );
if ( o->h() > h() && o->y() + o->h() != y() + h() )
return 1;
}

return 0;
}

virtual void
draw ( void )
{
if ( damage() & FL_DAMAGE_ALL )
{
fl_rectf( x(), y(), w(), h(), color() );
}

if ( ! children() )
return;

Fl_Widget *o = child( 0 );

o->position( x(), y() + _yposition );

const int top_sb = top_sb_visible();
const int bottom_sb = bottom_sb_visible();
fl_push_clip( x(), y() + ( sbh * top_sb ), w(), h() - (sbh * (top_sb + bottom_sb) ));
draw_children();

fl_pop_clip();
fl_font( FL_HELVETICA, 12 );
if ( top_sb )
{
fl_draw_box( box(), x(), y(), w(), sbh, color() );
fl_color( fl_contrast( FL_FOREGROUND_COLOR, color() ) );
fl_draw( "@2<", x(), y(), w(), sbh, FL_ALIGN_CENTER );
}
if ( bottom_sb )
{
fl_draw_box( box(), x(), y() + h() - sbh, w(), sbh, color() );
fl_color( fl_contrast( FL_FOREGROUND_COLOR, color() ) );
fl_draw( "@2>", x(), y() + h() - sbh, w(), sbh, FL_ALIGN_CENTER );
}
}

virtual int
handle ( int m )
{
switch ( m )
{
case FL_PUSH:
if ( top_sb_visible() &&
Fl::event_inside( x(), y(), w(), sbh ) )
{
return 1;
}
else if ( bottom_sb_visible() &&
Fl::event_inside( x(), y() + h() - sbh, w(), sbh ) )
{
return 1;
}
break;
case FL_RELEASE:
{
if ( top_sb_visible() &&
Fl::event_inside( x(), y(), w(), sbh ) )
{
yposition( yposition() + ( h() / 4 ) );
return 1;
}
else if ( bottom_sb_visible() &&
Fl::event_inside( x(), y() + h() - sbh, w(), sbh ) )
{
yposition( yposition() - (h() / 4 ) );
return 1;
}
break;
}
case FL_KEYBOARD:
{
if ( Fl::event_key() == FL_Up )
{
yposition( yposition() + ( h() / 4 ) );
return 1;
}
else if ( Fl::event_key() == FL_Down )
{
yposition( yposition() - (h() / 4 ) );
return 1;
}
break;
}
case FL_MOUSEWHEEL:
{
yposition( yposition() - ( Fl::event_dy() * _increment ) );

return 1;
}
}

return Fl_Group::handle( m );
}
};

+ 250
- 0
src/FL/Fl_Scalepack.C View File

@@ -0,0 +1,250 @@

/*******************************************************************************/
/* Copyright (C) 2008 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. */
/*******************************************************************************/


/* Fl_Scalepack

This is similar to an Fl_Pack, but instead of the pack resizing
itself to enclose its children, this pack resizes its children to
fit itself. Of course, this only works well with highly flexible
widgets, but the task comes up often enough to warrent this class.

If and child happens to be the resizable() widget, then it will be
resized so the all the other children can fit around it, with their
current sizes (and the size of the Fl_Scalepack) maintained.

NOTES: An Fl_Pack as a direct child will not work, because Fl_Pack
changes its size in draw(), which throws off our resize
calculation. The whole idea of widgets being able to resize
themselves within draw() is horribly broken...
*/

#include "Fl_Scalepack.H"

#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <stdio.h>

Fl_Scalepack::Fl_Scalepack ( int X, int Y, int W, int H, const char *L ) :
Fl_Group( X, Y, W, H, L )
{
resizable( 0 );
_spacing = 0;
}

void
Fl_Scalepack::resize ( int X, int Y, int W, int H )
{
/* Fl_Group's resize will change our child widget sizes, which
interferes with our own resizing method. */
long dx = X - x();
long dy = Y - y();
bool r = W != w() || H != h();

Fl_Widget::resize( X, Y, W, H );

Fl_Widget*const* a = array();

for (int i=children(); i--;)
{
Fl_Widget* o = *a++;

o->position( o->x() + dx, o->y() + dy );
}

if ( r )
redraw();
}

void
Fl_Scalepack::draw ( void )
{

if ( resizable() == this )
/* this resizable( this ) is the default for Fl_Group and is
* reset by Fl_Group::clear(), but it is not our default... */
resizable( 0 );

int tx = x() + Fl::box_dx( box() );
int ty = y() + Fl::box_dy( box() );
int tw = w() - Fl::box_dw( box() );
int th = h() - Fl::box_dh( box() );

if ( damage() & FL_DAMAGE_ALL )
{
draw_box();

draw_label();
}

int v = 0;

int cth = 0;
int ctw = 0;

Fl_Widget * const * a = array();

for ( int i = children(); i--; )
{
Fl_Widget *o = *a++;

if ( o->visible() )
{
++v;

if ( o != this->resizable() )
{
cth += o->h();
ctw += o->w();
}

cth += _spacing;
ctw += _spacing;
}
}

ctw -= _spacing;
cth -= _spacing;

if ( 0 == v )
return;

if ( this->resizable() )
{
int pos = 0;

Fl_Widget * const * a = array();

for ( int i = children(); i--; )
{
Fl_Widget *o = *a++;

if ( o->visible() )
{
int X, Y, W, H;

if ( type() == HORIZONTAL )
{
X = tx + pos;
Y = ty;
W = o->w();
H = th;
}
else
{
X = tx;
Y = ty + pos;
W = tw;
H = o->h();
}

if ( this->resizable() == o )
{
if ( type() == HORIZONTAL )
W = tw - ctw - 3;
else
H = th - cth;
}

if (X != o->x() || Y != o->y() || W != o->w() || H != o->h() )
{
o->resize(X,Y,W,H);
o->clear_damage(FL_DAMAGE_ALL);
}

if ( damage() & FL_DAMAGE_ALL )
{
draw_child( *o );
draw_outside_label( *o );
}
else
update_child( *o );

/* if ( this->resizable() == o ) */
/* fl_rect( o->x(), o->y(), o->w(), o->h(), type() == VERTICAL ? FL_GREEN : FL_BLUE ); */

if ( type() == HORIZONTAL )
pos += o->w() + spacing();
else
pos += o->h() + spacing();

}
}
}
else
{
int sz = 0;
int pos = 0;

if ( type() == HORIZONTAL )
sz = (tw - (_spacing * (v - 1))) / v;
else
sz = (th - (_spacing * (v - 1))) / v;

Fl_Widget * const * a = array();

for ( int i = children(); i--; )
{
Fl_Widget *o = *a++;

if ( o->visible() )
{
int X, Y, W, H;

if ( type() == HORIZONTAL )
{
X = tx + pos;
Y = ty;
W = sz;
H = th;
}
else
{
X = tx;
Y = ty + pos;
W = tw;
H = sz;
}

if (X != o->x() || Y != o->y() || W != o->w() || H != o->h() )
{
o->resize(X,Y,W,H);
o->clear_damage(FL_DAMAGE_ALL);
}

if ( damage() & FL_DAMAGE_ALL )
{
draw_child( *o );
draw_outside_label( *o );
}
else
update_child( *o );

// fl_rect( o->x(), o->y(), o->w(), o->h(), type() == VERTICAL ? FL_RED : FL_YELLOW );

if ( type() == HORIZONTAL )
pos += o->w() + spacing();
else
pos += o->h() + spacing();

}
}
}
}

+ 43
- 0
src/FL/Fl_Scalepack.H View File

@@ -0,0 +1,43 @@

/*******************************************************************************/
/* Copyright (C) 2008 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. */
/*******************************************************************************/

#pragma once

#include <FL/Fl_Group.H>

class Fl_Scalepack : public Fl_Group
{

int _spacing;

public:

enum { VERTICAL, HORIZONTAL };

Fl_Scalepack ( int X, int Y, int W, int H, const char *L = 0 );
virtual ~Fl_Scalepack ( ) { }

int spacing ( void ) const { return _spacing; }
void spacing ( int v ) { _spacing = v; redraw(); }

virtual void resize ( int, int, int, int );

virtual void draw ( void );

};

+ 100
- 0
src/NSM_Proxy_UI.fl View File

@@ -0,0 +1,100 @@
# 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 {644 190 635 665} type Double color 47 labelcolor 55 xclass {NSM-Proxy} visible
} {
Fl_Box {} {
label {Command-line options are incompatible with robust session management for a variety of reasons, so the NSM server does not support them directly. 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.
}
xywh {15 11 610 139} box BORDER_BOX color 41 labelfont 8 labelsize 12 labelcolor 55 align 128
}
Fl_File_Input executable_input {
label {Executable: }
xywh {115 162 495 31}
}
Fl_Input arguments_input {
label {Arguments:}
xywh {110 310 350 28}
}
Fl_Input label_input {
label {Label:}
xywh {110 340 350 28}
}
Fl_Return_Button start_button {
label Start
xywh {535 630 88 25}
}
Fl_Button kill_button {
label Kill
xywh {295 625 80 25} color 72 hide
}
Fl_Choice save_signal_choice {
label {Save Signal:} open
xywh {110 468 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}
}
}
Fl_Box {} {
label {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. The variable $CONFIG_FILE will contain the name of the config file selected above.}
xywh {15 235 610 69} box BORDER_BOX color 41 labelfont 8 labelsize 12 labelcolor 55 align 128
}
Fl_Box {} {
label {Some (very few) programs may respond to a specific Unix signal by somehow saving their state. If 'Save Signal' is set to something other than 'None', then NSM Proxy will deliver the specified signal to the proxied process upon an NSM 'Save' event. Most programs will treat these signals just like SIGTERM and die. You have been warned.}
xywh {15 378 610 79} box BORDER_BOX color 41 labelfont 8 labelsize 12 labelcolor 55 align 128
}
Fl_Choice stop_signal_choice {
label {Stop Signal:} open
xywh {108 592 170 25} down_box BORDER_BOX
} {
MenuItem {} {
label SIGTERM
xywh {10 10 40 24}
}
MenuItem {} {
label SIGINT
xywh {40 40 40 24}
}
MenuItem {} {
label SIGHUP
xywh {50 50 40 24}
}
}
Fl_Box {} {
label {Most programs will shutdown gracefully when sent a SIGTERM or SIGINT signal. It's impossible to know which signal a specific program will respond to. A unhandled signal will simply kill the process, and may cause problems with the audio subsystem (e.g. JACK). Check the program's documentation or source code to determine which signal to use to stop it gracefully.}
xywh {15 502 610 79} box BORDER_BOX color 41 labelfont 8 labelsize 12 labelcolor 55 align 128
}
Fl_File_Input config_file_input {
label {Config File:}
xywh {114 195 406 31}
}
Fl_Button config_file_browse_button {
label Browse
xywh {530 195 85 25}
}
}
}
}

+ 291
- 0
src/nsm-proxy-gui.cpp View File

@@ -0,0 +1,291 @@

/*******************************************************************************/
/* 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. */
/*******************************************************************************/

#pragma GCC diagnostic ignored "-Wunused-parameter"


#define _MODULE_ "nsm-proxy-gui"

#define APP_NAME "NSM Proxy"
#define APP_TITLE "NSM Proxy"

#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Text_Display.H>
#include "NSM_Proxy_UI.H"
#include <lo/lo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

lo_server losrv;
lo_address nsmp_addr;

static NSM_Proxy_UI *ui;

static char *client_error;

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/config_file" ))
ui->config_file_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 );
}
else if (!strcmp( path, "/nsm/proxy/stop_signal" ))
{
if ( argv[0]->i == SIGTERM )
ui->stop_signal_choice->value( 0 );
else if ( argv[0]->i == SIGINT )
ui->stop_signal_choice->value( 1 );
else if ( argv[0]->i == SIGHUP )
ui->stop_signal_choice->value( 2 );
}
if (!strcmp( path, "/nsm/proxy/client_error" ))
{
if ( client_error != NULL )
free(client_error);

client_error = NULL;

if ( strlen(&argv[0]->s) > 0 )
client_error = strdup(&argv[0]->s);
}

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/config_file", "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_add_method( loth, "/nsm/proxy/stop_signal", "i", osc_update, NULL );
lo_server_thread_add_method( loth, "/nsm/proxy/client_error", "s", 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", "sss",
ui->executable_input->value(),
ui->arguments_input->value(),
ui->config_file_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_config_file ( Fl_Widget *o, void *v )
{
}

void
handle_config_file_browse ( Fl_Widget *o, void *v )
{
const char * file = fl_file_chooser( "Pick file", "*", NULL, 1 );

ui->config_file_input->value( file );
}

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
handle_stop_signal ( Fl_Widget *o, void *v )
{
int sig = SIGTERM;
const char* picked = ui->stop_signal_choice->mvalue()->label();
if ( !strcmp( picked, "SIGTERM" ) )
sig = SIGTERM;
else if ( !strcmp( picked, "SIGINT" ) )
sig = SIGINT;
else if ( !strcmp( picked, "SIGHUP" ) )
sig = SIGHUP;

lo_send_from( nsmp_addr, losrv, LO_TT_IMMEDIATE,"/nsm/proxy/stop_signal", "i",
sig );
}

void
connect_ui ( void )
{
ui->executable_input->callback( handle_executable, NULL );
ui->config_file_input->callback( handle_config_file, 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->stop_signal_choice->callback( handle_stop_signal, NULL );
ui->label_input->callback( handle_label, NULL );
ui->config_file_browse_button->callback( handle_config_file_browse, NULL );
}


void cb_dismiss_button ( Fl_Widget *w, void *v )
{
w->window()->hide();
}

void
check_error ( void *v )
{
if ( client_error )
{
{
Fl_Double_Window *o = new Fl_Double_Window(600,300+15,"Abnormal Termination");
{
Fl_Box *o = new Fl_Box(0+15,0+15,600-30,50);
o->box(FL_BORDER_BOX);
o->color(FL_RED);
o->labelcolor(FL_WHITE);
o->align(FL_ALIGN_CENTER|FL_ALIGN_WRAP);
o->copy_label( client_error );
}
{
Fl_Text_Display *o = new Fl_Text_Display(0+15,50+15,600-30,300-75-30);
o->buffer(new Fl_Text_Buffer());
o->buffer()->loadfile( "error.log" );
}
{
Fl_Button *o = new Fl_Button(600-75-15,300-25,75,25,"Dismiss");
o->callback(cb_dismiss_button,0);
}

o->show();
}
free(client_error);
client_error = NULL;
}
Fl::repeat_timeout( 0.5f, check_error, v );
}

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::add_timeout( 0.5f, check_error, NULL );

Fl::run();

return 0;
}

+ 760
- 0
src/nsm-proxy.cpp View File

@@ -0,0 +1,760 @@

/*******************************************************************************/
/* 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. */
/*******************************************************************************/

#pragma GCC diagnostic ignored "-Wunused-parameter"

#define _MODULE_ "nsm-proxy"
#define APP_NAME "NSM Proxy"
#define APP_TITLE "NSM Proxy"

#include "debug.h"

#include <lo/lo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signalfd.h>
#include <sys/stat.h>
#include <sys/wait.h>

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"

void show_gui ( void );

class NSM_Proxy {

char *_label;
char *_executable;
char *_config_file;
char *_arguments;
int _save_signal;
int _stop_signal;
int _pid;
char *_client_error;

public:

int stop_signal ( void ) {return _stop_signal;}

NSM_Proxy ( )
{
_label = _executable = _arguments = _config_file = 0;
_save_signal = 0;
_stop_signal = SIGTERM;
_pid = 0;
_client_error = 0;
}

~NSM_Proxy ( )
{
}

void handle_client_death ( int status )
{
printf( "proxied process died unexpectedly... not dying\n" );
/* proxied process died unexpectedly */

if ( _client_error != NULL )
free(_client_error);

asprintf(&_client_error, "The proxied process terminated abnormally during invocation. Exit status: %i.", status );

show_gui();

_pid = 0;
}

void kill ( void )
{
if ( _pid )
{
::kill( _pid, _stop_signal );
}
}

bool start ( const char *executable, const char *arguments, const char *config_file )
{
if ( _executable )
free( _executable );
if ( _arguments )
free( _arguments );
if ( _config_file )
free( _config_file );

_executable = strdup( executable );

if ( arguments )
_arguments = strdup( arguments );
else
_arguments = NULL;

if ( config_file )
_config_file = strdup( config_file );
else
_config_file = NULL;

return start();
}

bool start ( void )
{
dump( project_file );

if ( _pid )
/* already running */
return true;

if ( !_executable )
{
WARNING( "Executable is null." );
return false;
}

int pid;
if ( ! (pid = fork()) )
{
MESSAGE( "Launching %s\n", _executable );
// char *args[] = { strdup( executable ), NULL };

char *cmd;

if ( _arguments )
asprintf( &cmd, "exec %s %s >error.log 2>&1", _executable, _arguments );
else
asprintf( &cmd, "exec %s >error.log 2>&1", _executable );

char *args[] = { strdup("/bin/sh"), strdup( "-c" ), cmd, NULL };
setenv( "NSM_CLIENT_ID", nsm_client_id, 1 );
setenv( "NSM_SESSION_NAME", nsm_display_name, 1 );
if ( _config_file )
setenv( "CONFIG_FILE", _config_file, 1 );
unsetenv( "NSM_URL" );
if ( -1 == execvp( "/bin/sh", args ) )
{
WARNING( "Error starting process: %s", strerror( errno ) );
exit(1);
}
}

_pid = pid;

return _pid > 0;
}

void save_signal ( int s )
{
_save_signal = s;
}

void stop_signal ( int s )
{
_stop_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 )
{
char *fname;
asprintf( &fname, "%s/%s", path, CONFIG_FILE_NAME );
FILE *fp = fopen( fname, "w" );

free( fname );
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 );

if ( _config_file && strlen(_config_file) )
fprintf( fp, "config file\n\t%s\n", _config_file );

fprintf( fp, "save signal\n\t%i\n", _save_signal );

fprintf( fp, "stop signal\n\t%i\n", _stop_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, "%m[^\n]\n\t%m[^\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, "config file" ) )
_config_file = value;
else if ( !strcmp( name, "save signal" ) )
{
_save_signal = atoi( value );
free( value );
}
else if ( !strcmp( name, "stop signal" ) )
{
_stop_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 : "" );
lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/config_file", "s", _config_file ? _config_file : "" );
lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/stop_signal", "i", _stop_signal );
lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/client_error", "s", _client_error ? _client_error : "" );


}
};

NSM_Proxy *nsm_proxy;

bool
snapshot ( const char *file )
{
return nsm_proxy->dump(file);
}
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
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, strdup( "--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;
}

void
hide_gui ( void )
{
if ( gui_pid )
{
kill( gui_pid, SIGTERM );
}
}

int
osc_hide_gui ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
{
hide_gui();

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_stop_signal ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
{
nsm_proxy->stop_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 )
{
snapshot( project_file );

if ( nsm_proxy->start( &argv[0]->s, &argv[1]->s, &argv[2]->s ) )
{
hide_gui();
}
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/stop_signal", "i", osc_stop_signal, NULL );
lo_server_add_method( losrv, "/nsm/proxy/kill", "", osc_kill, NULL );
lo_server_add_method( losrv, "/nsm/proxy/start", "sss", 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", "" );

gui_pid = 0;

/* don't care... */
continue;
}

if ( WIFSIGNALED(status) )
{
/* process was killed via signal */
if (WTERMSIG(status) == SIGTERM ||
WTERMSIG(status) == SIGHUP ||
WTERMSIG(status) == SIGINT ||
WTERMSIG(status) == SIGKILL )
{
/* process was killed via an appropriate signal */
MESSAGE( "child was killed (maybe by us)\n" );
die_now = 1;
continue;
}
}
else if ( WIFEXITED(status) )
{
/* child called exit() or returned from main() */

MESSAGE( "child exit status: %i", WEXITSTATUS(status) );

if ( WEXITSTATUS(status) == 0 )
{
/* apparently normal termination */
MESSAGE( "child exited without error.");
die_now = 1;
continue;
}
else
{
MESSAGE("child exited abnormally.");
nsm_proxy->handle_client_death(WEXITSTATUS(status));
}
}
}
}

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();
}
}

+ 1440
- 0
src/session-manager.cpp
File diff suppressed because it is too large
View File


Loading…
Cancel
Save