1 
2 /*************************************************************************/
3 /* Copyright (C) 2012 Jonathan Moore Liles                               */
4 /*                                                                       */
5 /* Permission to use, copy, modify, and/or distribute this software for  */
6 /* any purpose with or without fee is hereby granted, provided that the  */
7 /* above copyright notice and this permission notice appear in all       */
8 /* copies.                                                               */
9 /*                                                                       */
10 /* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL         */
11 /* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED         */
12 /* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE      */
13 /* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL  */
14 /* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR */
15 /* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER        */
16 /* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR      */
17 /* PERFORMANCE OF THIS SOFTWARE.                                         */
18 /*************************************************************************/
19 
20 
21 /*************************************************************/
22 /* A simple, callback based C API for NSM clients.           */
23 /*                                                           */
24 /* Simplified Example:                                       */
25 /*                                                           */
26 /* #include "nsm.h"                                          */
27 /*                                                           */
28 /* int                                                       */
29 /* cb_nsm_open ( const char *name,                           */
30 /*               const char *display_name,                   */
31 /*               const char *client_id,                      */
32 /*               char **out_msg,                             */
33 /*               void *userdata )                            */
34 /* {                                                         */
35 /*         do_open_stuff();                                  */
36 /*         return ERR_OK;                                    */
37 /* }                                                         */
38 /*                                                           */
39 /* int                                                       */
40 /* cb_nsm_save ( char **out_msg,                             */
41 /*               void *userdata )                            */
42 /* {                                                         */
43 /*     do_save_stuff();                                      */
44 /*     return ERR_OK;                                        */
45 /* }                                                         */
46 /*                                                           */
47 /* static nsm_client_t *nsm = 0                              */
48 /*                                                           */
49 /* int main( int argc, char **argv )                         */
50 /* {                                                         */
51 /*     const char *nsm_url = getenv( "NSM_URL" );            */
52 /*                                                           */
53 /*     if ( nsm_url )                                        */
54 /*     {                                                     */
55 /*         nsm = nsm_new();                                  */
56 /*                                                           */
57 /*         nsm_set_open_callback( nsm, cb_nsm_open, 0 );     */
58 /*         nsm_set_save_callback( nsm, cb_nsm_save, 0 );     */
59 /*                                                           */
60 /*         if ( 0 == nsm_init( nsm, nsm_url ) )              */
61 /*         {                                                 */
62 /*             nsm_send_announce( nsm, "FOO", "", argv[0] ); */
63 /*         }                                                 */
64 /*         else                                              */
65 /*         {                                                 */
66 /*             nsm_free( nsm );                              */
67 /*             nsm = 0;                                      */
68 /*         }                                                 */
69 /*     }                                                     */
70 /* }                                                         */
71 /*************************************************************/
72 
73 #ifndef _NSM_H
74 #define _NSM_H
75 
76 #define NSM_API_VERSION_MAJOR 1
77 #define NSM_API_VERSION_MINOR 0
78 
79 #include <lo/lo.h>
80 #include <string.h>
81 #include <sys/types.h>
82 #include <unistd.h>
83 #include <stdlib.h>
84 #include <stdio.h>
85 
86 typedef void * nsm_client_t;
87 typedef int (nsm_open_callback)( const char *name, const char *display_name, const char *client_id, char **out_msg, void *userdata );
88 typedef int (nsm_save_callback)( char **out_msg, void *userdata );
89 typedef void (nsm_active_callback)( int b, void *userdata );
90 typedef void (nsm_session_is_loaded_callback)( void *userdata );
91 typedef int (nsm_broadcast_callback)( const char *, lo_message m, void *userdata );
92 
93 #define _NSM() ((struct _nsm_client_t*)nsm)
94 
95 #define NSM_EXPORT __attribute__((unused)) static
96 
97 /* private parts */
98 struct _nsm_client_t
99 {
100     const char *nsm_url;
101 
102     lo_server _server;
103     lo_server_thread _st;
104     lo_address nsm_addr;
105 
106     int nsm_is_active;
107     char *nsm_client_id;
108     char *_session_manager_name;
109 
110     nsm_open_callback *open;
111     void *open_userdata;
112 
113     nsm_save_callback *save;
114     void *save_userdata;
115 
116     nsm_active_callback *active;
117     void *active_userdata;
118 
119     nsm_session_is_loaded_callback *session_is_loaded;
120     void *session_is_loaded_userdata;
121 
122     nsm_broadcast_callback *broadcast;
123     void *broadcast_userdata;
124 };
125 
126 enum
127 {
128     ERR_OK = 0,
129     ERR_GENERAL    = -1,
130     ERR_INCOMPATIBLE_API = -2,
131     ERR_BLACKLISTED      = -3,
132     ERR_LAUNCH_FAILED    = -4,
133     ERR_NO_SUCH_FILE     = -5,
134     ERR_NO_SESSION_OPEN  = -6,
135     ERR_UNSAVED_CHANGES  = -7,
136     ERR_NOT_NOW          = -8
137 };
138 
139 NSM_EXPORT
140 int
141 nsm_is_active ( nsm_client_t *nsm )
142 {
143     return _NSM()->nsm_is_active;
144 }
145 
146 NSM_EXPORT
147 const char *
148 nsm_get_session_manager_name ( nsm_client_t *nsm )
149 {
150     return _NSM()->_session_manager_name;
151 }
152 
153 NSM_EXPORT
154 nsm_client_t *
155 nsm_new ( void )
156 {
157     struct _nsm_client_t *nsm = (struct _nsm_client_t*)malloc( sizeof( struct _nsm_client_t ) );
158 
159     nsm->nsm_url = 0;
160 
161     nsm->nsm_is_active = 0;
162     nsm->nsm_client_id = 0;
163 
164     nsm->_server = 0;
165     nsm->_st = 0;
166     nsm->nsm_addr = 0;
167     nsm->_session_manager_name = 0;
168 
169     nsm->open = 0;
170     nsm->save = 0;
171     nsm->active = 0;
172     nsm->session_is_loaded = 0;
173     nsm->broadcast = 0;
174 
175     return (nsm_client_t *)nsm;
176 }
177 
178 /*******************************************/
179 /* CLIENT TO SERVER INFORMATIONAL MESSAGES */
180 /*******************************************/
181 
182 NSM_EXPORT
183 void
184 nsm_send_is_dirty ( nsm_client_t *nsm )
185 {
186     if ( _NSM()->nsm_is_active )
187         lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_dirty", "" );
188 }
189 
190 NSM_EXPORT
191 void
192 nsm_send_is_clean ( nsm_client_t *nsm )
193 {
194     if ( _NSM()->nsm_is_active )
195         lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/is_clean", "" );
196 }
197 
198 NSM_EXPORT
199 void
200 nsm_send_progress ( nsm_client_t *nsm, float p )
201 {
202     if ( _NSM()->nsm_is_active )
203         lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/progress", "f", p );
204 }
205 
206 NSM_EXPORT
207 void
208 nsm_send_message ( nsm_client_t *nsm, int priority, const char *msg )
209 {
210    if ( _NSM()->nsm_is_active )
211        lo_send_from( _NSM()->nsm_addr, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/client/message", "is", priority, msg );
212 }
213 
214 NSM_EXPORT void
215 nsm_send_announce ( nsm_client_t *nsm, const char *app_name, const char *capabilities, const char *process_name )
216 {
217     lo_address to = lo_address_new_from_url( _NSM()->nsm_url );
218 
219     if ( ! to )
220     {
221         fprintf( stderr, "NSM: Bad address!" );
222         return;
223     }
224 
225     int pid = (int)getpid();
226 
227     lo_send_from( to, _NSM()->_server, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
228                   app_name,
229                   capabilities,
230                   process_name,
231                   NSM_API_VERSION_MAJOR,
232                   NSM_API_VERSION_MINOR,
233                   pid );
234 
235     lo_address_free( to );
236 }
237 
238 NSM_EXPORT void
239 nsm_send_broadcast ( nsm_client_t *nsm, lo_message msg )
240 {
241    if ( _NSM()->nsm_is_active )
242        lo_send_message_from( _NSM()->nsm_addr, _NSM()->_server, "/nsm/server/broadcast", msg );
243 }
244 
245 
246 
247 NSM_EXPORT
248 void
249 nsm_check_wait ( nsm_client_t *nsm, int timeout )
250 {
251     if ( lo_server_wait( _NSM()->_server, timeout ) )
252         while ( lo_server_recv_noblock( _NSM()->_server, 0 ) ) {}
253 }
254 
255 NSM_EXPORT
256 void
257 nsm_check_nowait (nsm_client_t *nsm )
258 {
259     nsm_check_wait( nsm, 0 );
260 }
261 
262 
263 NSM_EXPORT
264 void
265 nsm_thread_start ( nsm_client_t *nsm )
266 {
267     lo_server_thread_start( _NSM()->_st );
268 }
269 
270 
271 NSM_EXPORT
272 void
273 nsm_thread_stop ( nsm_client_t *nsm )
274 {
275     lo_server_thread_stop( _NSM()->_st );
276 }
277 
278 
279 
280 NSM_EXPORT void
281 nsm_free ( nsm_client_t *nsm )
282 {
283     if ( _NSM()->_st )
284         nsm_thread_stop( nsm );
285 
286     if ( _NSM()->_st )
287         lo_server_thread_free( _NSM()->_st );
288     else
289         lo_server_free( _NSM()->_server );
290 
291     free( _NSM() );
292 }
293 
294 /*****************/
295 /* SET CALLBACKS */
296 /*****************/
297 
298 NSM_EXPORT
299 void
300 nsm_set_open_callback( nsm_client_t *nsm, nsm_open_callback *open_callback, void *userdata )
301 {
302     _NSM()->open = open_callback;
303     _NSM()->open_userdata = userdata;
304 }
305 
306 NSM_EXPORT
307 void
308 nsm_set_save_callback( nsm_client_t *nsm, nsm_save_callback *save_callback, void *userdata )
309 {
310     _NSM()->save = save_callback;
311     _NSM()->save_userdata = userdata;
312 
313 }
314 
315 NSM_EXPORT
316 void
317 nsm_set_active_callback( nsm_client_t *nsm, nsm_active_callback *active_callback, void *userdata )
318 {
319     _NSM()->active = active_callback;
320     _NSM()->active_userdata = userdata;
321 }
322 
323 NSM_EXPORT
324 void
325 nsm_set_session_is_loaded_callback( nsm_client_t *nsm, nsm_session_is_loaded_callback *session_is_loaded_callback, void *userdata )
326 {
327     _NSM()->session_is_loaded = session_is_loaded_callback;
328     _NSM()->session_is_loaded_userdata = userdata;
329 }
330 
331 
332 NSM_EXPORT
333 void
334 nsm_set_broadcast_callback( nsm_client_t *nsm, nsm_broadcast_callback *broadcast_callback, void *userdata )
335 {
336     _NSM()->broadcast = broadcast_callback;
337     _NSM()->broadcast_userdata = userdata;
338 }
339 
340 
341 
342 /****************/
343 /* OSC HANDLERS */
344 /****************/
345 
346 #undef OSC_REPLY
347 #undef OSC_REPLY_ERR
348 
349 #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 )
350 
351 #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 )
352 
353 
354 NSM_EXPORT int _nsm_osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
355 {
356     (void) types;
357     (void) argc;
358     (void) msg;
359 
360     char *out_msg = NULL;
361 
362     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
363 
364     nsm->nsm_client_id = strdup( &argv[2]->s );
365 
366     if ( ! nsm->open )
367         return 0;
368 
369     int r = nsm->open( &argv[0]->s, &argv[1]->s, &argv[2]->s, &out_msg, nsm->open_userdata );
370 
371     if ( r )
372         OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") );
373     else
374         OSC_REPLY( "OK" );
375 
376     if ( out_msg )
377         free( out_msg );
378 
379     return 0;
380 }
381 
382 NSM_EXPORT int _nsm_osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
383 {
384     (void) types;
385     (void) argv;
386     (void) argc;
387     (void) msg;
388 
389     char *out_msg = NULL;
390 
391     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
392 
393     if ( ! nsm->save )
394         return 0;
395 
396     int r = nsm->save(&out_msg, nsm->save_userdata );
397 
398     if ( r )
399         OSC_REPLY_ERR( r, ( out_msg ? out_msg : "") );
400     else
401         OSC_REPLY( "OK" );
402 
403     if ( out_msg )
404         free( out_msg );
405 
406     return 0;
407 }
408 
409 NSM_EXPORT int _nsm_osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
410 {
411     (void) path;
412     (void) types;
413     (void) argc;
414 
415     if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) )
416         return -1;
417 
418     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
419 
420     fprintf( stderr, "NSM: Successfully registered. NSM says: %s", &argv[1]->s );
421 
422     nsm->nsm_is_active = 1;
423     nsm->_session_manager_name = strdup( &argv[2]->s );
424     nsm->nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ));
425 
426     if ( nsm->active )
427         nsm->active( nsm->nsm_is_active, nsm->active_userdata );
428 
429     return 0;
430 }
431 
432 NSM_EXPORT int _nsm_osc_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
433 {
434     (void) path;
435     (void) types;
436     (void) argc;
437     (void) msg;
438 
439     if ( strcmp( &argv[0]->s, "/nsm/server/announce" ) )
440         return -1;
441 
442     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
443 
444     fprintf( stderr, "NSM: Failed to register with NSM server: %s", &argv[2]->s );
445 
446     nsm->nsm_is_active = 0;
447 
448     if ( nsm->active )
449         nsm->active( nsm->nsm_is_active, nsm->active_userdata );
450 
451     return 0;
452 }
453 
454 NSM_EXPORT int _nsm_osc_session_is_loaded ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
455 {
456     (void) path;
457     (void) types;
458     (void) argv;
459     (void) argc;
460     (void) msg;
461 
462     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
463 
464     if ( ! nsm->session_is_loaded )
465         return 0;
466 
467     nsm->session_is_loaded( nsm->session_is_loaded_userdata );
468 
469     return 0;
470 }
471 
472 NSM_EXPORT int _nsm_osc_broadcast ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
473 {
474     (void) types;
475     (void) argv;
476     (void) argc;
477 
478     struct _nsm_client_t *nsm = (struct _nsm_client_t*)user_data;
479 
480     if ( ! nsm->broadcast )
481         return 0;
482 
483     return nsm->broadcast( path, msg, nsm->broadcast_userdata );
484 }
485 
486 
487 
488 NSM_EXPORT
489 int
490 nsm_init ( nsm_client_t *nsm, const char *nsm_url )
491 {
492     _NSM()->nsm_url = nsm_url;
493 
494     lo_address addr = lo_address_new_from_url( nsm_url );
495     int proto = lo_address_get_protocol( addr );
496     lo_address_free( addr );
497 
498     _NSM()->_server = lo_server_new_with_proto( NULL, proto, NULL );
499 
500     if ( ! _NSM()->_server )
501         return -1;
502 
503     lo_server_add_method( _NSM()->_server, "/error", "sis", _nsm_osc_error, _NSM() );
504     lo_server_add_method( _NSM()->_server, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() );
505     lo_server_add_method( _NSM()->_server, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() );
506     lo_server_add_method( _NSM()->_server, "/nsm/client/save", "", _nsm_osc_save, _NSM() );
507     lo_server_add_method( _NSM()->_server, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() );
508     lo_server_add_method( _NSM()->_server, NULL, NULL, _nsm_osc_broadcast, _NSM() );
509 
510     return 0;
511 }
512 
513 
514 NSM_EXPORT
515 int
516 nsm_init_thread ( nsm_client_t *nsm, const char *nsm_url )
517 {
518     _NSM()->nsm_url = nsm_url;
519 
520     lo_address addr = lo_address_new_from_url( nsm_url );
521     int proto = lo_address_get_protocol( addr );
522     lo_address_free( addr );
523 
524     _NSM()->_st = lo_server_thread_new_with_proto( NULL, proto, NULL );
525     _NSM()->_server = lo_server_thread_get_server( _NSM()->_st );
526 
527     if ( ! _NSM()->_server )
528         return -1;
529 
530     lo_server_thread_add_method( _NSM()->_st, "/error", "sis", _nsm_osc_error, _NSM() );
531     lo_server_thread_add_method( _NSM()->_st, "/reply", "ssss", _nsm_osc_announce_reply, _NSM() );
532     lo_server_thread_add_method( _NSM()->_st, "/nsm/client/open", "sss", _nsm_osc_open, _NSM() );
533     lo_server_thread_add_method( _NSM()->_st, "/nsm/client/save", "", _nsm_osc_save, _NSM() );
534     lo_server_thread_add_method( _NSM()->_st, "/nsm/client/session_is_loaded", "", _nsm_osc_session_is_loaded, _NSM() );
535     lo_server_thread_add_method( _NSM()->_st, NULL, NULL, _nsm_osc_broadcast, _NSM() );
536 
537     return 0;
538 }
539 
540 #endif /* NSM_H */
541