From 885ef30a88c2da80de8c8531f07353fe4ea3d12e Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Sun, 8 Apr 2012 20:48:06 -0700 Subject: [PATCH] NSM: Add support for clients with optional GUIs. --- session-manager/doc/API.mu | 34 +++- session-manager/src/nsmd.C | 145 +++++++++++++++-- session-manager/src/session-manager.C | 216 ++++++++++++++++++++++---- 3 files changed, 347 insertions(+), 48 deletions(-) diff --git a/session-manager/doc/API.mu b/session-manager/doc/API.mu index a868b42..ffae33b 100644 --- a/session-manager/doc/API.mu +++ b/session-manager/doc/API.mu @@ -2,7 +2,7 @@ ! title Non Session Management API ! author Jonathan Moore Liles #(email,male@tuxfamily.org) ! date August 1, 2010 -! revision Version 1.0 +! revision Version 1.1 ! extra #(image,logo,icon.png) -- Table Of Contents @@ -156,6 +156,7 @@ [[ dirty, client knows when it has unsaved changes [[ progress, client can send progress updates during time-consuming operations [[ message, client can send textual status updates +[[ optional-gui, client has an optional GUI :::: Response @@ -179,6 +180,7 @@ [[ Name, Description [[ server_control, client-to-server control [[ broadcast, server responds to /nsm/server/broadcast message +[[ optional-gui, server responds to optional-gui messages--if this capability is not present then clients with optional-guis MUST always keep them visible A client should not consider itself to be under session management until it receives this response. For example, the Non applications @@ -366,6 +368,22 @@ This message does not require a response. +:::: Show Optional Gui + + If the client has specified the `optional-gui` capability, then it + may receive this message from the server when the user wishes to + change the visibility state of the GUI. It doesn't matter if the + optional GUI is integrated with the program or if it is a separate + program \(as is the case with SooperLooper\). When the GUI is + hidden, there should be no window mapped and if the GUI is a + separate program, it should be killed. + +> /nsm/client/show_optional_gui + +> /nsm/client/hide_optional_gui + + No response is message is required. + ::: Client to Server Informational Messages These are optional messages which a client can send to the NSM @@ -375,6 +393,20 @@ appropriate value to its `capabilities` string when composing the `announce` message. +:::: Optional GUI + + If the client has specified the `optional-gui` capability, then it + *MUST* send this message whenever the state of visibility of the + optional GUI has changed. It also *MUST* send this message after + it's announce message to indicate the initial visibility state of + the optional GUI. + +> /nsm/client/gui_is_hidden + +> /nsm/client/gui_is_shown + + No response will be delivered. + :::: Progress > /nsm/client/progress f:progress diff --git a/session-manager/src/nsmd.C b/session-manager/src/nsmd.C index 5181d56..8bb9e62 100644 --- a/session-manager/src/nsmd.C +++ b/session-manager/src/nsmd.C @@ -36,9 +36,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -97,6 +95,10 @@ private: int _pending_command; /* */ struct timeval _command_sent_time; + bool _gui_visible; + + char *_label; + public: lo_address addr; /* */ @@ -105,7 +107,6 @@ public: int pid; /* PID of client process */ float progress; /* */ bool active; /* client has registered via announce */ - bool dead_because_we_said; // bool stopped; /* the client quit, but not because we told it to--user still has to decide to remove it from the session */ char *client_id; /* short part of client ID */ char *capabilities; /* client capabilities... will be null for dumb clients */ @@ -113,14 +114,32 @@ public: bool pre_existing; const char *status; + const char *label ( void ) const { return _label; } + void label ( const char *l ) + { + if ( _label ) + free( _label ); + _label = strdup( l ); + } + + bool gui_visible ( void ) const + { + return _gui_visible; + } + + void gui_visible ( bool b ) + { + _gui_visible = b; + } + bool - has_error ( void ) + has_error ( void ) const { return _reply_errcode != 0; } int - error_code ( void ) + error_code ( void ) const { return _reply_errcode; } @@ -173,12 +192,20 @@ public: return _pending_command; } +// capability should be enclosed in colons. I.e. ":switch:" + bool + is_capable_of ( const char *capability ) const + { + return capabilities && + strstr( capabilities, capability ); + } + Client ( ) { + _gui_visible = true; addr = 0; _reply_errcode = 0; _reply_message = 0; - dead_because_we_said = false; pid = 0; progress = -0; _pending_command = 0; @@ -265,10 +292,12 @@ handle_client_process_death ( int pid ) { MESSAGE( "Client %s died.", c->name ); + bool dead_because_we_said = false; + if ( c->pending_command() == COMMAND_KILL || c->pending_command() == COMMAND_QUIT ) { - c->dead_because_we_said = true; + dead_because_we_said = true; } c->pending_command( COMMAND_NONE ); @@ -276,7 +305,7 @@ handle_client_process_death ( int pid ) c->active = false; c->pid = 0; - if ( c->dead_because_we_said ) + if ( dead_because_we_said ) { if ( gui_is_active ) osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" ); @@ -770,6 +799,9 @@ OSC_HANDLER( announce ) { osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name ); osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "open" ); + + if ( c->is_capable_of( ":optional-gui:" ) ) + osc_server->send( gui_addr, "/nsm/gui/client/has_optional_gui", c->client_id ); } { @@ -824,13 +856,6 @@ client_by_name ( const char *name, return NULL; } -// capability should be enclosed in colons. I.e. ":switch:" -bool -client_is_capable_of ( Client *c, const char *capability ) -{ - return c->capabilities && - strstr( c->capabilities, capability ); -} bool dumb_clients_are_alive ( ) @@ -1109,7 +1134,7 @@ load_session_file ( const char * path ) i != client.end(); ++i ) { - if ( ! client_is_capable_of( *i, ":switch:" ) + if ( ! (*i)->is_capable_of( ":switch:" ) || ! client_by_name( (*i)->name, &new_clients ) ) { @@ -1636,6 +1661,39 @@ OSC_HANDLER( is_clean ) return 0; } +OSC_HANDLER( gui_is_hidden ) +{ + MESSAGE( "Client sends gui hidden" ); + + Client *c = get_client_by_address( lo_message_get_source( msg ) ); + + if ( ! c ) + return 0; + + c->gui_visible( false ); + + if ( gui_is_active ) + osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() ); + + return 0; +} + +OSC_HANDLER( gui_is_shown ) +{ + MESSAGE( "Client sends gui shown" ); + + Client *c = get_client_by_address( lo_message_get_source( msg ) ); + + if ( ! c ) + return 0; + + c->gui_visible( true ); + + if ( gui_is_active ) + osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() ); + + return 0; +} OSC_HANDLER( message ) { @@ -1650,6 +1708,24 @@ OSC_HANDLER( message ) return 0; } +OSC_HANDLER( label ) +{ + Client *c = get_client_by_address( lo_message_get_source( msg ) ); + + if ( ! c ) + return 0; + + if ( strcmp( types, "s" ) ) + return -1; + + c->label( &argv[0]->s ); + + if ( gui_is_active ) + osc_server->send( gui_addr, "/nsm/gui/client/label", c->client_id, &argv[0]->s ); + + return 0; +} + /**********************/ /* Response Handlers */ /**********************/ @@ -1779,6 +1855,38 @@ OSC_HANDLER( client_save ) return 0; } +OSC_HANDLER( client_show_optional_gui ) +{ + Client *c = get_client_by_id( &client, &argv[0]->s ); + + /* FIXME: return error if no such client? */ + if ( c ) + { + if ( c->active ) + { + osc_server->send( c->addr, "/nsm/client/show_optional_gui" ); + } + } + + return 0; +} + +OSC_HANDLER( client_hide_optional_gui ) +{ + Client *c = get_client_by_id( &client, &argv[0]->s ); + + /* FIXME: return error if no such client? */ + if ( c ) + { + if ( c->active ) + { + osc_server->send( c->addr, "/nsm/client/hide_optional_gui" ); + } + } + + return 0; +} + void announce_gui( const char *url, bool is_reply ) { @@ -1927,12 +2035,17 @@ int main(int argc, char *argv[]) osc_server->add_method( "/nsm/client/is_dirty", "", OSC_NAME( is_dirty ), NULL, "dirtiness" ); osc_server->add_method( "/nsm/client/is_clean", "", OSC_NAME( is_clean ), NULL, "dirtiness" ); osc_server->add_method( "/nsm/client/message", "is", OSC_NAME( message ), NULL, "message" ); + osc_server->add_method( "/nsm/client/gui_is_hidden", "", OSC_NAME( gui_is_hidden ), NULL, "message" ); + osc_server->add_method( "/nsm/client/gui_is_shown", "", OSC_NAME( gui_is_shown ), NULL, "message" ); + osc_server->add_method( "/nsm/client/label", "s", OSC_NAME( label ), NULL, "message" ); /* */ osc_server->add_method( "/nsm/gui/gui_announce", "", OSC_NAME( gui_announce ), NULL, "" ); osc_server->add_method( "/nsm/gui/client/remove", "s", OSC_NAME( remove ), NULL, "client_id" ); osc_server->add_method( "/nsm/gui/client/resume", "s", OSC_NAME( resume ), NULL, "client_id" ); osc_server->add_method( "/nsm/gui/client/save", "s", OSC_NAME( client_save ), NULL, "client_id" ); + osc_server->add_method( "/nsm/gui/client/show_optional_gui", "s", OSC_NAME( client_show_optional_gui ), NULL, "client_id" ); + osc_server->add_method( "/nsm/gui/client/hide_optional_gui", "s", OSC_NAME( client_hide_optional_gui ), NULL, "client_id" ); osc_server->add_method( "/osc/ping", "", OSC_NAME( ping ), NULL, "" ); diff --git a/session-manager/src/session-manager.C b/session-manager/src/session-manager.C index a98f809..13b9b2c 100644 --- a/session-manager/src/session-manager.C +++ b/session-manager/src/session-manager.C @@ -83,19 +83,56 @@ static std::list daemon_list; /* list class NSM_Client : public Fl_Group { char *_client_id; + char *_client_label; + char *_client_name; // Fl_Box *client_name; Fl_Progress *_progress; Fl_Light_Button *_dirty; + Fl_Light_Button *_gui; Fl_Button *_remove_button; Fl_Button *_restart_button; + void + set_label ( void ) + { + char *l; + + if ( _client_label ) + asprintf( &l, "%s (%s)", _client_name, _client_label ); + else + l = strdup( _client_name ); + + if ( label() ) + free((char*)label()); + + label( l ); + + redraw(); + } + public: void name ( const char *v ) { - label( strdup( v ) ); + if ( _client_name ) + free( _client_name ); + + _client_name = strdup( v ); + + set_label(); + } + + void + client_label ( const char *s ) + { + if ( _client_label ) + free( _client_label ); + + _client_label = strdup( s ); + + set_label(); } void @@ -121,6 +158,21 @@ public: _dirty->redraw(); } + void + gui_visible ( bool b ) + { + _gui->value( b ); + _gui->redraw(); + } + + + void + has_optional_gui ( void ) + { + _gui->show(); + _gui->redraw(); + } + void stopped ( bool b ) { @@ -193,7 +245,18 @@ public: osc->send( (*d)->addr, "/nsm/gui/client/save", _client_id ); } } - if ( o == _remove_button ) + else if ( o == _gui ) + { + MESSAGE( "Sending hide/show GUI."); + foreach_daemon ( d ) + { + if ( !_gui->value() ) + osc->send( (*d)->addr, "/nsm/gui/client/show_optional_gui", _client_id ); + else + osc->send( (*d)->addr, "/nsm/gui/client/hide_optional_gui", _client_id ); + } + } + else if ( o == _remove_button ) { MESSAGE( "Sending remove."); foreach_daemon ( d ) @@ -221,46 +284,119 @@ public: { _client_id = NULL; + _client_name = NULL; + _client_label = NULL; align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE ); color( fl_darker( FL_RED ) ); box( FL_UP_BOX ); + + int yy = Y + H * 0.25; + int hh = H * 0.50; + int xx = X + W - ( 200 + Fl::box_dw( box() ) ); + int ss = 2; - { Fl_Progress *o = _progress = new Fl_Progress( ( X + W ) - ( W / 4) - 20, Y + 5, ( W / 4 ), H - 10, NULL ); + /* dummy group */ + { Fl_Group *o = new Fl_Group( X, Y, 50, 50 ); + o->end(); + resizable( o ); + } + + { Fl_Progress *o = _progress = new Fl_Progress( xx, Y + H * 0.25, 200, H * 0.50, NULL ); + o->box( FL_FLAT_BOX ); + o->color( FL_BLACK ); o->label( strdup( "launch" ) ); o->minimum( 0.0f ); o->maximum( 1.0f ); } - { Fl_Light_Button *o = _dirty = new Fl_Light_Button( _progress->x() - 30, Y + 7, 25, 25 ); - o->box( FL_UP_BOX ); - o->type(0); - o->color(); - o->selection_color( FL_YELLOW ); - o->value( 0 ); - o->callback( cb_button, this ); + + { Fl_Group *o = new Fl_Group( X + W - 400, Y, 400, H ); + + xx -= 50 + ss; + + { Fl_Light_Button *o = _dirty = new Fl_Light_Button( xx, yy, 50, hh, "SAVE" ); + + o->align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE ); + o->labelsize( 9 ); + o->box( FL_UP_BOX ); + o->type(0); + o->color(); + o->selection_color( FL_YELLOW ); + o->value( 0 ); + o->callback( cb_button, this ); + } + + xx -= 40 + ss; + + { Fl_Light_Button *o = _gui = new Fl_Light_Button( xx, yy, 40, hh, "GUI" ); + + o->align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE ); + o->labelsize( 9 ); + o->box( FL_UP_BOX ); + o->type(0); + o->color(); + o->selection_color( FL_YELLOW ); + o->value( 0 ); + o->hide(); + o->callback( cb_button, this ); + } + + + xx -= 25 + ss; + + { Fl_Button *o = _restart_button = new Fl_Button( xx, yy, 25, hh ); + + + o->box( FL_UP_BOX ); + o->type(0); + o->color( FL_GREEN ); + o->value( 0 ); + o->label( "@>" ); + o->tooltip( "Resume" ); + o->hide(); + o->callback( cb_button, this ); + } + + xx -= 25 + ss; + + { Fl_Button *o = _remove_button = new Fl_Button( xx, yy, 25, hh ); + + + o->box( FL_UP_BOX ); + o->type(0); + o->color( FL_RED ); + o->value( 0 ); + o->label( "X" ); + o->tooltip( "Remove" ); + o->hide(); + o->callback( cb_button, this ); + } + + + o->end(); } - { Fl_Button *o = _remove_button = new Fl_Button( _progress->x() - 60, Y + 7, 25, 25 ); - o->box( FL_UP_BOX ); - o->type(0); - o->color( FL_RED ); - o->value( 0 ); - o->label( "X" ); - o->tooltip( "Remove" ); - o->hide(); - o->callback( cb_button, this ); - } - { Fl_Button *o = _restart_button = new Fl_Button( _progress->x() - 90, Y + 7, 25, 25 ); - o->box( FL_UP_BOX ); - o->type(0); - o->color( FL_GREEN ); - o->value( 0 ); - o->label( "@>" ); - o->tooltip( "Resume" ); - o->hide(); - o->callback( cb_button, this ); + end(); + } + + ~NSM_Client ( ) + { + if ( _client_name ) + { + free( _client_name ); + _client_name = NULL; } - end(); + if ( _client_label ) + { + free( _client_label ); + _client_label = NULL; + } + + if ( label() ) + { + free( (char*)label() ); + label( NULL ); + } } }; @@ -735,6 +871,9 @@ public: osc->add_method( "/nsm/gui/client/switch", "ss", osc_handler, osc, "path,display_name" ); osc->add_method( "/nsm/gui/client/progress", "sf", osc_handler, osc, "path,display_name" ); osc->add_method( "/nsm/gui/client/dirty", "si", osc_handler, osc, "path,display_name" ); + osc->add_method( "/nsm/gui/client/has_optional_gui", "s", osc_handler, osc, "path,display_name" ); + osc->add_method( "/nsm/gui/client/gui_visible", "si", osc_handler, osc, "path,display_name" ); + osc->add_method( "/nsm/gui/client/label", "ss", osc_handler, osc, "path,display_name" ); osc->start(); @@ -852,7 +991,7 @@ private: if ( !strncmp( path, "/nsm/gui/client/", strlen( "/nsm/gui/client/" ) ) ) { if ( !strcmp( path, "/nsm/gui/client/new" ) && - !strcmp( types, "ss" ) ) + !strcmp( types, "ss" ) ) { controller->client_new( &argv[0]->s, &argv[1]->s ); } @@ -877,6 +1016,21 @@ private: { c->dirty( argv[1]->i ); } + else if ( !strcmp( path, "/nsm/gui/client/gui_visible" ) && + !strcmp( types, "si" )) + { + c->gui_visible( argv[1]->i ); + } + else if ( !strcmp( path, "/nsm/gui/client/label" ) && + !strcmp( types, "ss" )) + { + c->client_label( &argv[1]->s ); + } + else if ( !strcmp( path, "/nsm/gui/client/has_optional_gui" ) && + !strcmp( types, "s" )) + { + c->has_optional_gui(); + } else if ( !strcmp( path, "/nsm/gui/client/switch" ) && !strcmp( types, "ss" )) {