ExternalUIExtension
From Lv2
[edit] C header file (lv2_external_ui.h)
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* This work is in public domain.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* If you have questions, contact Nedko Arnaudov <nedko@arnaudov.name> or
* ask in #lad channel, FreeNode IRC network.
*
*****************************************************************************/
#ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED
#define LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED
/** UI extension suitable for out-of-process UIs */
#define LV2_EXTERNAL_UI_URI "http://lv2plug.in/ns/extensions/ui#external"
#ifdef __cplusplus
extern "C" {
#endif
#if 0
} /* Adjust editor indent */
#endif
/**
* When LV2_EXTERNAL_UI_URI UI is instantiated, the returned
* LV2UI_Widget handle must be cast to pointer to struct lv2_external_ui.
* UI is created in invisible state.
*/
struct lv2_external_ui
{
/**
* Host calls this function regulary. UI library implementing the
* callback may do IPC or redraw the UI.
*
* @param _this_ the UI context
*/
void (* run)(struct lv2_external_ui * _this_);
/**
* Host calls this function to make the plugin UI visible.
*
* @param _this_ the UI context
*/
void (* show)(struct lv2_external_ui * _this_);
/**
* Host calls this function to make the plugin UI invisible again.
*
* @param _this_ the UI context
*/
void (* hide)(struct lv2_external_ui * _this_);
};
#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
/**
* On UI instantiation, host must supply LV2_EXTERNAL_UI_URI
* feature. LV2_Feature::data must be pointer to struct lv2_external_ui_host. */
struct lv2_external_ui_host
{
/**
* Callback that plugin UI will call
* when UI (GUI window) is closed by user.
* This callback wil; be called during execution of lv2_external_ui::run()
* (i.e. not from background thread).
*
* After this callback is called, UI is defunct. Host must call
* LV2UI_Descriptor::cleanup(). If host wants to make the UI visible
* again UI must be reinstantiated.
*
* @param controller Host context associated with plugin UI, as
* supplied to LV2UI_Descriptor::instantiate()
*/
void (* ui_closed)(LV2UI_Controller controller);
/**
* Optional (may be NULL) "user friendly" identifier which the UI
* may display to allow a user to easily associate this particular
* UI instance with the correct plugin instance as it is represented
* by the host (e.g. "track 1" or "channel 4").
*
* If supplied by host, the string will be referenced only during
* LV2UI_Descriptor::instantiate()
*/
const char * plugin_human_id;
};
#if 0
{ /* Adjust editor indent */
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* #ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED */
[edit] DSSI-like universal "wrapper"
Implementation of DSSI like implementation of external UI (made originally for lv2 nekobee). Must be built with defines for UI_EXECUTABLE and UI_URI (strings)
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
*
* LV2 UI bundle shared library for communicating with a DSSI UI
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307, USA.
*
*****************************************************************************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <lo/lo.h>
#include <lv2.h>
#include "lv2_ui.h"
#include "lv2_external_ui.h"
#define MAX_OSC_PATH 1024
struct control
{
struct lv2_external_ui virt; /* WARNING: code assumes this is the first struct member */
LV2UI_Controller controller;
LV2UI_Write_Function write_function;
void (* ui_closed)(LV2UI_Controller controller);
lo_server osc_server;
bool running; /* true if UI launched and 'exiting' not received */
bool visible; /* true if 'show' sent */
lo_address osc_address; /* non-NULL if 'update' received */
char osc_control_path[MAX_OSC_PATH];
char osc_hide_path[MAX_OSC_PATH];
char osc_quit_path[MAX_OSC_PATH];
char osc_show_path[MAX_OSC_PATH];
};
#undef control_ptr
static
int
osc_debug_handler(
const char * path,
const char * types,
lo_arg ** argv,
int argc,
void * data,
void * user_data)
{
int i;
printf("got unhandled OSC message:\n");
printf("path: <%s>\n", path);
fflush(stdout);
for (i = 0; i < argc; i++)
{
printf("arg %d '%c' ", i, types[i]);
lo_arg_pp(types[i], argv[i]);
printf("\n");
}
fflush(stdout);
return 1;
}
int
osc_exiting_handler(
struct control * control_ptr,
lo_arg ** argv)
{
//printf("OSC: got UI exit notification\n");
control_ptr->running = false;
control_ptr->visible = false;
if (control_ptr->osc_address)
{
lo_address_free(control_ptr->osc_address);
}
control_ptr->ui_closed(control_ptr->controller);
return 0;
}
int
osc_control_handler(
struct control * control_ptr,
lo_arg ** argv)
{
int port = argv[0]->i;
float value = argv[1]->f;
//printf("OSC control handler: port %d = %f\n", port, value);
control_ptr->write_function(control_ptr->controller, (uint32_t)port, sizeof(float), 0, &value);
return 0;
}
int
osc_update_handler(
struct control * control_ptr,
lo_arg ** argv)
{
const char * url = &argv[0]->s;
char * path;
char * host;
char * port;
//printf("OSC: got update request from <%s>\n", url);
if (control_ptr->osc_address)
{
return 0;
}
host = lo_url_get_hostname(url);
port = lo_url_get_port(url);
control_ptr->osc_address = lo_address_new(host, port);
free(host);
free(port);
path = lo_url_get_path(url);
sprintf(control_ptr->osc_control_path, "%scontrol", path);
sprintf(control_ptr->osc_hide_path, "%shide", path);
sprintf(control_ptr->osc_show_path, "%sshow", path);
sprintf(control_ptr->osc_quit_path, "%squit", path);
free(path);
control_ptr->running = true;
return 0;
}
#define control_ptr ((struct control *)user_data)
static
int
osc_message_handler(
const char * path,
const char * types,
lo_arg ** argv,
int argc,
void * data,
void * user_data)
{
const char *method;
method = path;
if (method[0] != '/' || method[1] != '/')
return osc_debug_handler(path, types, argv, argc, data, user_data);
method += 2;
if (!strcmp(method, "update") && argc == 1 && !strcmp(types, "s"))
{
return osc_update_handler(control_ptr, argv);
}
else if (!strcmp(method, "control") && argc == 2 && !strcmp(types, "if"))
{
return osc_control_handler(control_ptr, argv);
}
else if (!strcmp(method, "exiting") && argc == 0)
{
return osc_exiting_handler(control_ptr, argv);
}
return osc_debug_handler(path, types, argv, argc, data, user_data);
}
#undef control_ptr
#define control_ptr ((struct control *)_this_)
static
void
run(
struct lv2_external_ui * _this_)
{
//printf("run() called\n");
while (lo_server_recv_noblock(control_ptr->osc_server, 0) != 0) {}
}
static
void
show(
struct lv2_external_ui * _this_)
{
//printf("show() called\n");
if (control_ptr->visible)
{
return;
}
if (control_ptr->osc_address)
{
lo_send(control_ptr->osc_address, control_ptr->osc_show_path, "");
control_ptr->visible = true;
}
}
static
void
hide(
struct lv2_external_ui * _this_)
{
//printf("hide() called\n");
if (!control_ptr->visible || !control_ptr->osc_address)
{
return;
}
lo_send(control_ptr->osc_address, control_ptr->osc_hide_path, "");
control_ptr->visible = false;
}
#undef control_ptr
static
LV2UI_Handle
instantiate(
const struct _LV2UI_Descriptor * descriptor,
const char * plugin_uri,
const char * bundle_path,
LV2UI_Write_Function write_function,
LV2UI_Controller controller,
LV2UI_Widget * widget,
const LV2_Feature * const * features)
{
struct control * control_ptr;
struct lv2_external_ui_host * ui_host_ptr;
char * filename;
char * osc_url;
//printf("instantiate('%s', '%s') called\n", plugin_uri, bundle_path);
ui_host_ptr = NULL;
while (*features != NULL)
{
if (strcmp((*features)->URI, LV2_EXTERNAL_UI_URI) == 0)
{
ui_host_ptr = (*features)->data;
}
features++;
}
if (ui_host_ptr == NULL)
{
goto fail;
}
control_ptr = malloc(sizeof(struct control));
if (control_ptr == NULL)
{
goto fail;
}
control_ptr->virt.run = run;
control_ptr->virt.show = show;
control_ptr->virt.hide = hide;
control_ptr->controller = controller;
control_ptr->write_function = write_function;
control_ptr->ui_closed = ui_host_ptr->ui_closed;
filename = malloc(strlen(bundle_path) + strlen(UI_EXECUTABLE) + 1);
if (filename == NULL)
{
goto fail_free_control;
}
strcpy(filename, bundle_path);
strcat(filename, UI_EXECUTABLE);
control_ptr->running = false;
control_ptr->visible = false;
control_ptr->osc_address = NULL;
control_ptr->osc_server = lo_server_new(NULL, NULL);
osc_url = lo_server_get_url(control_ptr->osc_server);
//printf("host OSC URL is %s\n", osc_url);
lo_server_add_method(control_ptr->osc_server, NULL, NULL, osc_message_handler, control_ptr);
if (fork() == 0)
{
execlp(
filename,
filename,
osc_url,
plugin_uri,
plugin_uri,
ui_host_ptr->plugin_human_id != NULL ? ui_host_ptr->plugin_human_id : "",
NULL);
fprintf(stderr, "exec of UI failed: %s", strerror(errno));
exit(1);
}
while (!control_ptr->running)
{
if (lo_server_recv_noblock(control_ptr->osc_server, 0) == 0)
{
usleep(300000);
}
}
*widget = (LV2UI_Widget)control_ptr;
return (LV2UI_Handle)control_ptr;
fail_free_control:
free(control_ptr);
fail:
return NULL;
}
#define control_ptr ((struct control *)ui)
static
void
cleanup(
LV2UI_Handle ui)
{
//printf("cleanup() called\n");
free(control_ptr);
}
static
void
port_event(
LV2UI_Handle ui,
uint32_t port_index,
uint32_t buffer_size,
uint32_t format,
const void * buffer)
{
//printf("port_event(%u, %f) called\n", (unsigned int)port_index, *(float *)buffer);
lo_send(control_ptr->osc_address, control_ptr->osc_control_path, "if", (int)port_index, *(float *)buffer);
}
#undef control_ptr
static LV2UI_Descriptor descriptors[] =
{
{UI_URI, instantiate, cleanup, port_event, NULL}
};
const LV2UI_Descriptor *
lv2ui_descriptor(
uint32_t index)
{
//printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
if (index >= sizeof(descriptors) / sizeof(descriptors[0]))
{
return NULL;
}
return descriptors + index;
}

