/* -*- Mode: C ; c-basic-offset: 2 -*- */ /***************************************************************************** * * Copyright (C) 2009 Nedko Arnaudov * * 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 #include #include #include #include #include #include #include #include "lv2_external_ui.h" #define MAX_OSC_PATH 1024 struct control { LV2_External_UI_Widget 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( LV2_External_UI_Widget * _this_) { //printf("run() called\n"); while (lo_server_recv_noblock(control_ptr->osc_server, 0) != 0) {} } static void show( LV2_External_UI_Widget * _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( LV2_External_UI_Widget * _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; 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__Host) == 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; }