KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
8.1KB

  1. /* -*- Mode: C ; c-basic-offset: 2 -*- */
  2. /*****************************************************************************
  3. *
  4. * Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
  5. *
  6. * LV2 UI bundle shared library for communicating with a DSSI UI
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation; either version 2 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be
  14. * useful, but WITHOUT ANY WARRANTY; without even the implied
  15. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  16. * PURPOSE. See the GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with this program; if not, write to the Free
  20. * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21. * MA 02111-1307, USA.
  22. *
  23. *****************************************************************************/
  24. #include <stdbool.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <sys/types.h>
  29. #include <unistd.h>
  30. #include <errno.h>
  31. #include <lo/lo.h>
  32. #include "lv2_external_ui.h"
  33. #define MAX_OSC_PATH 1024
  34. struct control
  35. {
  36. LV2_External_UI_Widget virt; /* WARNING: code assumes this is the first struct member */
  37. LV2UI_Controller controller;
  38. LV2UI_Write_Function write_function;
  39. void (* ui_closed)(LV2UI_Controller controller);
  40. lo_server osc_server;
  41. bool running; /* true if UI launched and 'exiting' not received */
  42. bool visible; /* true if 'show' sent */
  43. lo_address osc_address; /* non-NULL if 'update' received */
  44. char osc_control_path[MAX_OSC_PATH];
  45. char osc_hide_path[MAX_OSC_PATH];
  46. char osc_quit_path[MAX_OSC_PATH];
  47. char osc_show_path[MAX_OSC_PATH];
  48. };
  49. #undef control_ptr
  50. static
  51. int
  52. osc_debug_handler(
  53. const char * path,
  54. const char * types,
  55. lo_arg ** argv,
  56. int argc,
  57. void * data,
  58. void * user_data)
  59. {
  60. int i;
  61. printf("got unhandled OSC message:\n");
  62. printf("path: <%s>\n", path);
  63. fflush(stdout);
  64. for (i = 0; i < argc; i++)
  65. {
  66. printf("arg %d '%c' ", i, types[i]);
  67. lo_arg_pp(types[i], argv[i]);
  68. printf("\n");
  69. }
  70. fflush(stdout);
  71. return 1;
  72. }
  73. int
  74. osc_exiting_handler(
  75. struct control * control_ptr,
  76. lo_arg ** argv)
  77. {
  78. //printf("OSC: got UI exit notification\n");
  79. control_ptr->running = false;
  80. control_ptr->visible = false;
  81. if (control_ptr->osc_address)
  82. {
  83. lo_address_free(control_ptr->osc_address);
  84. }
  85. control_ptr->ui_closed(control_ptr->controller);
  86. return 0;
  87. }
  88. int
  89. osc_control_handler(
  90. struct control * control_ptr,
  91. lo_arg ** argv)
  92. {
  93. int port = argv[0]->i;
  94. float value = argv[1]->f;
  95. //printf("OSC control handler: port %d = %f\n", port, value);
  96. control_ptr->write_function(control_ptr->controller, (uint32_t)port, sizeof(float), 0, &value);
  97. return 0;
  98. }
  99. int
  100. osc_update_handler(
  101. struct control * control_ptr,
  102. lo_arg ** argv)
  103. {
  104. const char * url = &argv[0]->s;
  105. char * path;
  106. char * host;
  107. char * port;
  108. //printf("OSC: got update request from <%s>\n", url);
  109. if (control_ptr->osc_address)
  110. {
  111. return 0;
  112. }
  113. host = lo_url_get_hostname(url);
  114. port = lo_url_get_port(url);
  115. control_ptr->osc_address = lo_address_new(host, port);
  116. free(host);
  117. free(port);
  118. path = lo_url_get_path(url);
  119. sprintf(control_ptr->osc_control_path, "%scontrol", path);
  120. sprintf(control_ptr->osc_hide_path, "%shide", path);
  121. sprintf(control_ptr->osc_show_path, "%sshow", path);
  122. sprintf(control_ptr->osc_quit_path, "%squit", path);
  123. free(path);
  124. control_ptr->running = true;
  125. return 0;
  126. }
  127. #define control_ptr ((struct control *)user_data)
  128. static
  129. int
  130. osc_message_handler(
  131. const char * path,
  132. const char * types,
  133. lo_arg ** argv,
  134. int argc,
  135. void * data,
  136. void * user_data)
  137. {
  138. const char *method;
  139. method = path;
  140. if (method[0] != '/' || method[1] != '/')
  141. return osc_debug_handler(path, types, argv, argc, data, user_data);
  142. method += 2;
  143. if (!strcmp(method, "update") && argc == 1 && !strcmp(types, "s"))
  144. {
  145. return osc_update_handler(control_ptr, argv);
  146. }
  147. else if (!strcmp(method, "control") && argc == 2 && !strcmp(types, "if"))
  148. {
  149. return osc_control_handler(control_ptr, argv);
  150. }
  151. else if (!strcmp(method, "exiting") && argc == 0)
  152. {
  153. return osc_exiting_handler(control_ptr, argv);
  154. }
  155. return osc_debug_handler(path, types, argv, argc, data, user_data);
  156. }
  157. #undef control_ptr
  158. #define control_ptr ((struct control *)_this_)
  159. static
  160. void
  161. run(
  162. LV2_External_UI_Widget * _this_)
  163. {
  164. //printf("run() called\n");
  165. while (lo_server_recv_noblock(control_ptr->osc_server, 0) != 0) {}
  166. }
  167. static
  168. void
  169. show(
  170. LV2_External_UI_Widget * _this_)
  171. {
  172. //printf("show() called\n");
  173. if (control_ptr->visible)
  174. {
  175. return;
  176. }
  177. if (control_ptr->osc_address)
  178. {
  179. lo_send(control_ptr->osc_address, control_ptr->osc_show_path, "");
  180. control_ptr->visible = true;
  181. }
  182. }
  183. static
  184. void
  185. hide(
  186. LV2_External_UI_Widget * _this_)
  187. {
  188. //printf("hide() called\n");
  189. if (!control_ptr->visible || !control_ptr->osc_address)
  190. {
  191. return;
  192. }
  193. lo_send(control_ptr->osc_address, control_ptr->osc_hide_path, "");
  194. control_ptr->visible = false;
  195. }
  196. #undef control_ptr
  197. static
  198. LV2UI_Handle
  199. instantiate(
  200. const struct _LV2UI_Descriptor * descriptor,
  201. const char * plugin_uri,
  202. const char * bundle_path,
  203. LV2UI_Write_Function write_function,
  204. LV2UI_Controller controller,
  205. LV2UI_Widget * widget,
  206. const LV2_Feature * const * features)
  207. {
  208. struct control * control_ptr;
  209. LV2_External_UI_Host * ui_host_ptr;
  210. char * filename;
  211. char * osc_url;
  212. //printf("instantiate('%s', '%s') called\n", plugin_uri, bundle_path);
  213. ui_host_ptr = NULL;
  214. while (*features != NULL)
  215. {
  216. if (strcmp((*features)->URI, LV2_EXTERNAL_UI__Host) == 0)
  217. {
  218. ui_host_ptr = (*features)->data;
  219. }
  220. features++;
  221. }
  222. if (ui_host_ptr == NULL)
  223. {
  224. goto fail;
  225. }
  226. control_ptr = malloc(sizeof(struct control));
  227. if (control_ptr == NULL)
  228. {
  229. goto fail;
  230. }
  231. control_ptr->virt.run = run;
  232. control_ptr->virt.show = show;
  233. control_ptr->virt.hide = hide;
  234. control_ptr->controller = controller;
  235. control_ptr->write_function = write_function;
  236. control_ptr->ui_closed = ui_host_ptr->ui_closed;
  237. filename = malloc(strlen(bundle_path) + strlen(UI_EXECUTABLE) + 1);
  238. if (filename == NULL)
  239. {
  240. goto fail_free_control;
  241. }
  242. strcpy(filename, bundle_path);
  243. strcat(filename, UI_EXECUTABLE);
  244. control_ptr->running = false;
  245. control_ptr->visible = false;
  246. control_ptr->osc_address = NULL;
  247. control_ptr->osc_server = lo_server_new(NULL, NULL);
  248. osc_url = lo_server_get_url(control_ptr->osc_server);
  249. //printf("host OSC URL is %s\n", osc_url);
  250. lo_server_add_method(control_ptr->osc_server, NULL, NULL, osc_message_handler, control_ptr);
  251. if (fork() == 0)
  252. {
  253. execlp(
  254. filename,
  255. filename,
  256. osc_url,
  257. plugin_uri,
  258. plugin_uri,
  259. ui_host_ptr->plugin_human_id != NULL ? ui_host_ptr->plugin_human_id : "",
  260. NULL);
  261. fprintf(stderr, "exec of UI failed: %s", strerror(errno));
  262. exit(1);
  263. }
  264. while (!control_ptr->running)
  265. {
  266. if (lo_server_recv_noblock(control_ptr->osc_server, 0) == 0)
  267. {
  268. usleep(300000);
  269. }
  270. }
  271. *widget = (LV2UI_Widget)control_ptr;
  272. return (LV2UI_Handle)control_ptr;
  273. fail_free_control:
  274. free(control_ptr);
  275. fail:
  276. return NULL;
  277. }
  278. #define control_ptr ((struct control *)ui)
  279. static
  280. void
  281. cleanup(
  282. LV2UI_Handle ui)
  283. {
  284. //printf("cleanup() called\n");
  285. free(control_ptr);
  286. }
  287. static
  288. void
  289. port_event(
  290. LV2UI_Handle ui,
  291. uint32_t port_index,
  292. uint32_t buffer_size,
  293. uint32_t format,
  294. const void * buffer)
  295. {
  296. //printf("port_event(%u, %f) called\n", (unsigned int)port_index, *(float *)buffer);
  297. lo_send(control_ptr->osc_address, control_ptr->osc_control_path, "if", (int)port_index, *(float *)buffer);
  298. }
  299. #undef control_ptr
  300. static LV2UI_Descriptor descriptors[] =
  301. {
  302. {UI_URI, instantiate, cleanup, port_event, NULL}
  303. };
  304. const LV2UI_Descriptor *
  305. lv2ui_descriptor(
  306. uint32_t index)
  307. {
  308. //printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
  309. if (index >= sizeof(descriptors) / sizeof(descriptors[0]))
  310. {
  311. return NULL;
  312. }
  313. return descriptors + index;
  314. }