diff --git a/nonlib/nsm.h b/nonlib/nsm.h new file mode 100644 index 0000000..e6adae4 --- /dev/null +++ b/nonlib/nsm.h @@ -0,0 +1,511 @@ + +/*************************************************************************/ +/* Copyright (C) 2012 Jonathan Moore Liles */ +/* */ +/* Permission to use, copy, modify, and/or distribute this software for */ +/* any purpose with or without fee is hereby granted, provided that the */ +/* above copyright notice and this permission notice appear in all */ +/* copies. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL */ +/* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED */ +/* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE */ +/* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL */ +/* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR */ +/* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER */ +/* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR */ +/* PERFORMANCE OF THIS SOFTWARE. */ +/*************************************************************************/ + + +/*************************************************************/ +/* A simple, callback based C API for NSM clients. */ +/* */ +/* Simplified Example: */ +/* */ +/* #include "nsm.h" */ +/* */ +/* int */ +/* cb_nsm_open ( const char *name, */ +/* const char *display_name, */ +/* const char *client_id, */ +/* char **out_msg, */ +/* void *userdata ) */ +/* { */ +/* do_open_stuff(); */ +/* return ERR_OK; */ +/* } */ +/* */ +/* int */ +/* cb_nsm_save ( char **out_msg, */ +/* void *userdata ) */ +/* { */ +/* do_save_stuff(); */ +/* return ERR_OK; */ +/* } */ +/* */ +/* static nsm_client_t *nsm = 0 */ +/* */ +/* int main( int argc, char **argv ) */ +/* { */ +/* const char *nsm_url = getenv( "NSM_URL" ); */ +/* */ +/* if ( nsm_url ) */ +/* { */ +/* nsm = nsm_new(); */ +/* */ +/* nsm_set_open_callback( nsm, cb_nsm_open, 0 ); */ +/* nsm_set_save_callback( nsm, cb_nsm_save, 0 ); */ +/* */ +/* if ( 0 == nsm_init( nsm, nsm_url ) ) */ +/* { */ +/* nsm_send_announce( nsm, "FOO", "", argv[0] ); */ +/* } */ +/* else */ +/* { */ +/* nsm_free( nsm ); */ +/* nsm = 0; */ +/* } */ +/* } */ +/* } */ +/*************************************************************/ + +#ifndef _NSM_H +#define _NSM_H + +#define NSM_API_VERSION_MAJOR 1 +#define NSM_API_VERSION_MINOR 0 + +#include +#include +#include +#include +#include + +typedef void * nsm_client_t; +typedef int (nsm_open_callback)( const char *name, const char *display_name, const char *client_id, char **out_msg, void *userdata ); +typedef int (nsm_save_callback)( char **out_msg, void *userdata ); +typedef void (nsm_active_callback)( int b, void *userdata ); +typedef void (nsm_session_is_loaded_callback)( void *userdata ); +typedef int (nsm_broadcast_callback)( const char *, lo_message m, void *userdata ); + +#define _NSM() ((_nsm_client_t*)nsm) + +#define NSM_EXPORT __attribute__((unused)) static + +/* private parts */ +struct _nsm_client_t +{ + const char *nsm_url; + + lo_server _server; + lo_server_thread _st; + lo_address nsm_addr; + + int nsm_is_active; + char *nsm_client_id; + char *_session_manager_name; + + nsm_open_callback *open; + void *open_userdata; + + nsm_save_callback *save; + void *save_userdata; + + nsm_active_callback *active; + void *active_userdata; + + nsm_session_is_loaded_callback *session_is_loaded; + void *session_is_loaded_userdata; + + nsm_broadcast_callback *broadcast; + void *broadcast_userdata; +}; + +enum +{ + ERR_OK = 0, + ERR_GENERAL = -1, + ERR_INCOMPATIBLE_API = -2, + ERR_BLACKLISTED = -3, + ERR_LAUNCH_FAILED = -4, + ERR_NO_SUCH_FILE = -5, + ERR_NO_SESSION_OPEN = -6, + ERR_UNSAVED_CHANGES = -7, + ERR_NOT_NOW = -8 +}; + +NSM_EXPORT +int +nsm_is_active ( nsm_client_t *nsm ) +{ + return _NSM()->nsm_is_active; +} + +NSM_EXPORT +const char * +nsm_get_session_manager_name ( nsm_client_t *nsm ) +{ + return _NSM()->_session_manager_name; +} + +NSM_EXPORT +nsm_client_t * +nsm_new ( void ) +{ + struct _nsm_client_t *nsm = (struct _nsm_client_t*)malloc( sizeof( struct _nsm_client_t ) ); + + nsm->nsm_url = 0; + + nsm->nsm_is_active = 0; + nsm->nsm_client_id = 0; + + nsm->_server = 0; + nsm->_st = 0; + nsm->nsm_addr = 0; + nsm->_session_manager_name = 0; + + nsm->open = 0; + nsm->save = 0; + nsm->active = 0; + nsm->session_is_loaded = 0; + nsm->broadcast = 0; + + return (nsm_client_t *)nsm; +} + +/*******************************************/ +/* CLIENT TO SERVER INFORMATIONAL MESSAGES */ +/*******************************************/ + +NSM_EXPORT +void +nsm_send_is_dirty ( nsm_client_t *nsm ) +{ + if ( _NSM()->nsm_is_active ) + lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_dirty", "" ); +} + +NSM_EXPORT +void +nsm_send_is_clean ( nsm_client_t *nsm ) +{ + if ( _NSM()->nsm_is_active ) + lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_clean", "" ); +} + +NSM_EXPORT +void +nsm_send_progress ( nsm_client_t *nsm, float p ) +{ + if ( _NSM()->nsm_is_active ) + lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/progress", "f", p ); +} + +NSM_EXPORT +void +nsm_send_message ( nsm_client_t *nsm, int priority, const char *msg ) +{ + if ( _NSM()->nsm_is_active ) + lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/message", "is", priority, msg ); +} + +NSM_EXPORT void +nsm_send_announce ( nsm_client_t *nsm, const char *app_name, const char *capabilities, const char *process_name ) +{ + lo_address to = lo_address_new_from_url( _NSM()->nsm_url ); + + if ( ! to ) + { + fprintf( stderr, "NSM: Bad address!" ); + return; + } + + int pid = (int)getpid(); + + lo_send_from( to, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", + app_name, + capabilities, + process_name, + NSM_API_VERSION_MAJOR, + NSM_API_VERSION_MINOR, + pid ); + + lo_address_free( to ); +} + +NSM_EXPORT void +nsm_send_broadcast ( nsm_client_t *nsm, lo_message msg ) +{ + if ( _NSM()->nsm_is_active ) + lo_send_message_from( _NSM()->nsm_addr, _NSM()->_server, "/nsm/server/broadcast", msg ); +} + + + +NSM_EXPORT +void +nsm_check_wait ( nsm_client_t *nsm, int timeout ) +{ + if ( lo_server_wait( _NSM()->_server, timeout ) ) + while ( lo_server_recv_noblock( _NSM()->_server, 0 ) ) {} +} + +NSM_EXPORT +void +nsm_check_nowait (nsm_client_t *nsm ) +{ + nsm_check_wait( nsm, 0 ); +} + + +NSM_EXPORT +void +nsm_thread_start ( nsm_client_t *nsm ) +{ + lo_server_thread_start( _NSM()->_st ); +} + + +NSM_EXPORT +void +nsm_thread_stop ( nsm_client_t *nsm ) +{ + lo_server_thread_stop( _NSM()->_st ); +} + + + +NSM_EXPORT void +nsm_free ( nsm_client_t *nsm ) +{ + if ( _NSM()->_st ) + nsm_thread_stop( nsm ); + + if ( _NSM()->_st ) + lo_server_thread_free( _NSM()->_st ); + else + lo_server_free( _NSM()->_server ); + + free( _NSM() ); +} + +/*****************/ +/* SET CALLBACKS */ +/*****************/ + +NSM_EXPORT +void +nsm_set_open_callback( nsm_client_t *nsm, nsm_open_callback *open_callback, void *userdata ) +{ + _NSM()->open = open_callback; + _NSM()->open_userdata = userdata; +} + +NSM_EXPORT +void +nsm_set_save_callback( nsm_client_t *nsm, nsm_save_callback *save_callback, void *userdata ) +{ + _NSM()->save = save_callback; + _NSM()->save_userdata = userdata; + +} + +NSM_EXPORT +void +nsm_set_active_callback( nsm_client_t *nsm, nsm_active_callback *active_callback, void *userdata ) +{ + _NSM()->active = active_callback; + _NSM()->active_userdata = userdata; +} + +NSM_EXPORT +void +nsm_set_session_is_loaded_callback( nsm_client_t *nsm, nsm_session_is_loaded_callback *session_is_loaded_callback, void *userdata ) +{ + _NSM()->session_is_loaded = session_is_loaded_callback; + _NSM()->session_is_loaded_userdata = userdata; +} + + +NSM_EXPORT +void +nsm_set_broadcast_callback( nsm_client_t *nsm, nsm_broadcast_callback *broadcast_callback, void *userdata ) +{ + _NSM()->broadcast = broadcast_callback; + _NSM()->broadcast_userdata = userdata; +} + + + +/****************/ +/* OSC HANDLERS */ +/****************/ + +#undef OSC_REPLY +#undef OSC_REPLY_ERR + +#define OSC_REPLY( value ) lo_send_from( ((struct _nsm_client_t*)user_data)->nsm_addr, ((struct _nsm_client_t*)user_data)->_server, LO_TT_IMMEDIATE, "/reply", "ss", path, value ) + +#define OSC_REPLY_ERR( errcode, value ) lo_send_from( ((struct _nsm_client_t*)user_data)->nsm_addr, ((struct _nsm_client_t*)user_data)->_server, LO_TT_IMMEDIATE, "/error", "sis", path, errcode, value ) + + +NSM_EXPORT int _nsm_osc_open ( const char *path, const char *, lo_arg **argv, int , lo_message, void *user_data ) +{ + char *out_msg = NULL; + + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + nsm->nsm_client_id = strdup( &argv[2]->s ); + + if ( ! nsm->open ) + return 0; + + int r = nsm->open( &argv[0]->s, &argv[1]->s, &argv[2]->s, &out_msg, nsm->open_userdata ); + + if ( r ) + OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") ); + else + OSC_REPLY( "OK" ); + + if ( out_msg ) + free( out_msg ); + + return 0; +} + +NSM_EXPORT int _nsm_osc_save ( const char *path, const char *, lo_arg **, int , lo_message , void *user_data ) +{ + char *out_msg = NULL; + + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + if ( ! nsm->save ) + return 0; + + int r = nsm->save(&out_msg, nsm->save_userdata ); + + if ( r ) + OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") ); + else + OSC_REPLY( "OK" ); + + if ( out_msg ) + free( out_msg ); + + return 0; +} + +NSM_EXPORT int _nsm_osc_announce_reply ( const char *, const char *, lo_arg **argv, int , lo_message msg, void *user_data ) +{ + if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) ) + return -1; + + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + fprintf( stderr, "NSM: Successfully registered. NSM says: %s", &argv[1]->s ); + + nsm->nsm_is_active = 1; + nsm->_session_manager_name = strdup( &argv[2]->s ); + nsm->nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) )); + + if ( nsm->active ) + nsm->active( nsm->nsm_is_active, nsm->active_userdata ); + + return 0; +} + +NSM_EXPORT int _nsm_osc_error ( const char *, const char *, lo_arg **argv, int , lo_message , void *user_data ) +{ + if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) ) + return -1; + + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + fprintf( stderr, "NSM: Failed to register with NSM server: %s", &argv[2]->s ); + + nsm->nsm_is_active = 0; + + if ( nsm->active ) + nsm->active( nsm->nsm_is_active, nsm->active_userdata ); + + return 0; +} + +NSM_EXPORT int _nsm_osc_session_is_loaded ( const char *, const char *, lo_arg **, int , lo_message , void *user_data ) +{ + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + if ( ! nsm->session_is_loaded ) + return 0; + + nsm->session_is_loaded( nsm->session_is_loaded_userdata ); + + return 0; +} + +NSM_EXPORT int _nsm_osc_broadcast ( const char *path, const char *, lo_arg **, int , lo_message msg, void *user_data ) +{ + struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data; + + if ( ! nsm->broadcast ) + return 0; + + return nsm->broadcast( path, msg, nsm->broadcast_userdata ); +} + + + +NSM_EXPORT +int +nsm_init ( nsm_client_t *nsm, const char *nsm_url ) +{ + _NSM()->nsm_url = nsm_url; + + lo_address addr = lo_address_new_from_url( nsm_url ); + int proto = lo_address_get_protocol( addr ); + lo_address_free( addr ); + + _NSM()->_server = lo_server_new_with_proto( NULL, proto, NULL ); + + if ( ! _NSM()->_server ) + return -1; + + lo_server_add_method( _NSM()->_server, "/error", "sis", _nsm_osc_error, _NSM() ); + lo_server_add_method( _NSM()->_server, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() ); + lo_server_add_method( _NSM()->_server, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() ); + lo_server_add_method( _NSM()->_server, "/nsm/client/save", "", _nsm_osc_save, _NSM() ); + lo_server_add_method( _NSM()->_server, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() ); + lo_server_add_method( _NSM()->_server, NULL, NULL, _nsm_osc_broadcast, _NSM() ); + + return 0; +} + + +NSM_EXPORT +int +nsm_init_thread ( nsm_client_t *nsm, const char *nsm_url ) +{ + _NSM()->nsm_url = nsm_url; + + lo_address addr = lo_address_new_from_url( nsm_url ); + int proto = lo_address_get_protocol( addr ); + lo_address_free( addr ); + + _NSM()->_st = lo_server_thread_new_with_proto( NULL, proto, NULL ); + _NSM()->_server = lo_server_thread_get_server( _NSM()->_st ); + + if ( ! _NSM()->_server ) + return -1; + + lo_server_thread_add_method( _NSM()->_st, "/error", "sis", _nsm_osc_error, _NSM() ); + lo_server_thread_add_method( _NSM()->_st, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() ); + lo_server_thread_add_method( _NSM()->_st, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() ); + lo_server_thread_add_method( _NSM()->_st, "/nsm/client/save", "", _nsm_osc_save, _NSM() ); + lo_server_thread_add_method( _NSM()->_st, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() ); + lo_server_thread_add_method( _NSM()->_st, NULL, NULL, _nsm_osc_broadcast, _NSM() ); + + return 0; +} + +#endif /* NSM_H */