From a138c37a0ed25d1ce841cbd893f4bb83df279e19 Mon Sep 17 00:00:00 2001 From: Albert Graef Date: Thu, 16 Aug 2018 00:10:41 +0200 Subject: [PATCH] Add basic Jack session support. --- jackdriver.c | 31 ++++++++++++++++-- jackdriver.h | 4 ++- midizap.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++---- midizap.h | 1 + 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/jackdriver.c b/jackdriver.c index 5c59e84..4f3ab81 100644 --- a/jackdriver.c +++ b/jackdriver.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "jackdriver.h" @@ -369,13 +370,35 @@ int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no) return 0; } -int jack_shutdown; +int jack_quit; void shutdown_callback() { // we can't do anything fancy here, just ping the main thread - jack_shutdown = 1; + jack_quit = -1; +} + +char *jack_command_line = "midizap"; + +void +session_callback(jack_session_event_t *event, void *seqq) +{ + JACK_SEQ* seq = (JACK_SEQ*)seqq; + // XXXTODO: In order to better support Jack session management in the future + // we may want to copy over the loaded midizaprc file and store it in the + // session dir, so that we can reload it from there later. For the time + // being, we simply record the command line here. + //printf("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit"); + + event->command_line = strdup(jack_command_line); + jack_session_reply(seq->jack_client, event); + + if (event->type == JackSessionSaveAndQuit) { + jack_quit = 1; + } + + jack_session_event_free (event); } //////////////////////////////// @@ -409,6 +432,9 @@ init_jack(JACK_SEQ* seq, uint8_t verbose) if(verbose)printf("assigning shutdown callback...\n"); jack_on_shutdown(seq->jack_client, shutdown_callback, (void*)seq); + if(verbose)printf("assigning session callback...\n"); + jack_set_session_callback(seq->jack_client, session_callback, (void*)seq); + if(verbose)printf("assigning process callback...\n"); err = jack_set_process_callback(seq->jack_client, process_callback, (void*)seq); if (err) @@ -519,4 +545,5 @@ void close_jack(JACK_SEQ* seq) for (k = 0; k < seq->n_in; k++) jack_ringbuffer_free(seq->ringbuffer_in[k]); } + jack_client_close(seq->jack_client); } diff --git a/jackdriver.h b/jackdriver.h index 4092ca4..1fbec0f 100644 --- a/jackdriver.h +++ b/jackdriver.h @@ -15,7 +15,9 @@ typedef struct _jseq uint8_t n_out; } JACK_SEQ; -extern int jack_shutdown; +extern int jack_quit; +// This is supposed to be set properly by main(). +extern char *jack_command_line; int init_jack(JACK_SEQ* seq, uint8_t verbose); void close_jack(JACK_SEQ* seq); diff --git a/midizap.c b/midizap.c index 8dace48..a9d152e 100644 --- a/midizap.c +++ b/midizap.c @@ -798,6 +798,58 @@ void quitter() quit = 1; } +// Helper functions to process the command line, so that we can pass it to +// Jack session management. + +static char *command_line; +static size_t len; + +static void add_command(char *arg) +{ + char *a = arg; + // Do some simplistic quoting if the argument contains blanks. This won't do + // the right thing if the argument also contains quotes. Oh well. + if ((strchr(a, ' ') || strchr(a, '\t')) && !strchr(a, '"')) { + a = malloc(strlen(arg)+3); + sprintf(a, "\"%s\"", arg); + } + if (!command_line) { + len = strlen(a); + command_line = malloc(len+1); + strcpy(command_line, a); + } else { + size_t l = strlen(a)+1; + command_line = realloc(command_line, len+l+1); + command_line[len] = ' '; + strcpy(command_line+len+1, a); + len += l; + } + if (a != arg) free(a); +} + +static char *absolute_path(char *name) +{ + if (*name == '/') { + return name; + } else { + // This is a relative pathname, we turn it into a canonicalized absolute + // path. NOTE: This requires glibc. We should probably rewrite this code + // to be more portable. + char *pwd = getcwd(NULL, 0); + if (!pwd) { + perror("getcwd"); + return name; + } else { + char *path = malloc(strlen(pwd)+strlen(name)+2); + static char abspath[PATH_MAX]; + sprintf(path, "%s/%s", pwd, name); + realpath(path, abspath); + free(path); free(pwd); + return abspath; + } + } +} + // poll interval in microsec (this shouldn't be too large to avoid jitter) #define POLL_INTERVAL 1000 // how often we check the config file per sec (> 0, < 1000000/POLL_INTERVAL) @@ -810,6 +862,9 @@ main(int argc, char **argv) uint8_t msg[3]; int opt, count = 0; + // Start recording the command line to be passed to Jack session management. + add_command(argv[0]); + while ((opt = getopt(argc, argv, "hko::d::j:r:")) != -1) { switch (opt) { case 'h': @@ -818,23 +873,29 @@ main(int argc, char **argv) case 'k': // see comment on -k and keydown_tracker above keydown_tracker = 1; + add_command("-k"); break; case 'o': jack_num_outputs = 1; if (optarg && *optarg) { const char *a = optarg; - if (*a == '2') { + if (!strcmp(a, "2")) { jack_num_outputs = 2; - } else if (*a && *a != '1') { + add_command("-o2"); + } else if (strcmp(a, "1")) { fprintf(stderr, "%s: wrong port number (-o), must be 1 or 2\n", argv[0]); fprintf(stderr, "Try -h for help.\n"); exit(1); - } - } + } else + add_command("-o1"); + } else + add_command("-o"); break; case 'd': if (optarg && *optarg) { - const char *a = optarg; + const char *a = optarg; char buf[100]; + snprintf(buf, 100, "-d%s", optarg); + add_command(buf); while (*a) { switch (*a) { case 'r': @@ -863,13 +924,20 @@ main(int argc, char **argv) default_debug_regex = default_debug_strokes = default_debug_keys = default_debug_midi = 1; debug_jack = 1; + add_command("-d"); } break; case 'j': jack_client_name = optarg; + add_command("-j"); + add_command(optarg); break; case 'r': config_file_name = optarg; + add_command("-r"); + // We need to convert this to an absolute pathname for Jack session + // management. + add_command(absolute_path(optarg)); break; default: fprintf(stderr, "Try -h for help.\n"); @@ -882,6 +950,8 @@ main(int argc, char **argv) exit(1); } + if (command_line) jack_command_line = command_line; + initdisplay(); // Force the config file to be loaded initially, so that we pick up the Jack @@ -897,11 +967,14 @@ main(int argc, char **argv) exit(1); } + int do_flush = debug_regex || debug_strokes || debug_keys || debug_midi || + debug_jack; signal(SIGINT, quitter); while (!quit) { uint8_t portno; - if (jack_shutdown) { - fprintf(stderr, "%s: jack shutting down, exiting\n", argv[0]); + if (jack_quit) { + printf("[jack %s, exiting]\n", + (jack_quit>0)?"asked us to quit":"shutting down"); close_jack(&seq); exit(0); } @@ -918,6 +991,9 @@ main(int argc, char **argv) if (read_config_file()) last_focused_window = 0; count = 0; } + // Make sure that debugging output gets flushed every once in a while (may + // be buffered when midizap is running inside a QjackCtl session). + if (do_flush) fflush(NULL); } printf(" [exiting]\n"); close_jack(&seq); diff --git a/midizap.h b/midizap.h index 3f0f836..98e17d9 100644 --- a/midizap.h +++ b/midizap.h @@ -2,6 +2,7 @@ // Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact) // Copyright 2018 Albert Graef +#include #include #include #include