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