Implementing NSM

Session Management for Linux

This article covers how to add NSM support to your application. It assumes you can include a C header, and set C type callbacks. It is quite easy to add NSM support to most programs as there is a header file that does the OSC handling for us, and we are provided with a callback when an open or save event occurs.

This tutorial covers only the basic open / save functionality of NSM. It is capable of much more, please see the NSM API for details. I’ll first describe what happens in pseudo code, and then in actual code.

Steps Involved

  1. Check the “NSM_URL” environment varibale: if it exists, NSM is running.
  2. If NSM_URL does NOT exist: ignore session management.
  3. Else create an NSM client.
  4. NSM says what session to open: including JACK client to use.
  5. Setup program to use JACK client name, and load session as specified.
  6. Periodically call nsm_check() to see if we have events to handle.
  7. Save event: we get a callback. Save over the previously loaded session.
  8. SIGTERM is sent to our process when we should quit: that should be handled gracefully

Setup Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "nsm.h"
// non-session-manager: preferably a class member
nsm_client_t* nsm = 0;
const char *nsm_url = getenv( "NSM_URL" );
if ( nsm_url )
{
nsm = nsm_new();
nsm_set_open_callback( nsm, nsm_open_cb, this );
nsm_set_save_callback( nsm, nsm_save_cb, this );
if ( nsm_init( nsm, nsm_url ) == 0 )
{
nsm_send_announce( nsm, "clientName", "", argv[0] );
} else {
nsm_free( nsm );
nsm = 0;
}
}

Callbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int nsm_open_cb (const char *name,
const char *display_name,
const char *client_id,
char **out_msg,
void *userdata )
{
// setup JACK here, client_id is the JACK client name
Jack::setup( client_id );
// load the NSM provided directory
readSession( name );
// store the save() directory for later
storeLocation();
return ERR_OK;
}
static int nsm_save_cb ( char **out_msg, void *userdata )
{
// open() set the directory to save() into, just save
saveSession();
return 0;
}

SigTERM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// NSM uses SIGTERM to terminate client programs:
// you *must* handle it gracefully or JACK will Xrun
// when quitting your program!
static volatile bool quit_now = false;
void signalHanlder(int sigNum)
{
// flag that SIGTERM arrived
quit_now = true;
}
int main()
{
// register your handler function somewhere
signal(SIGTERM, signalHanlder);
// register a callback to periodically call nsm_check()
// or nsm_check_nowait()
startNsmCheckCallback();
// tell this thread to periodically check if
// quit_now == true, if it is, then quit gracefully
// using jack_client_deactivate() and jack_client_close()
startQuitGracefullyCallback();
}

Conclusion

Its very easy to add NSM support to most programs! The only reason it could be difficult is due to JACK being setup too soon in the program, before NSM tells the program what client name to use. I’ve encountered this in Luppp: however it is quite possible to refactor the initialization stage, and make this work.

NSM is a joy to use, the more applications that support it, the better! If you’ve been thinking about implementing support, please take this post as the final encouragement to do it: user-exerperience will improve.