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.

389 lines
12KB

  1. commit 109070a71a1e780385b803731fbdd2689752c644
  2. Author: Robin Gareus <robin@gareus.org>
  3. Date: Tue Jan 7 17:48:52 2014 +0100
  4. add support for externalUI
  5. diff --git a/jalv/src/jalv.c b/jalv/src/jalv.c
  6. index e80f9f9..e0aa1a0 100644
  7. --- a/jalv/src/jalv.c
  8. +++ b/jalv/src/jalv.c
  9. @@ -649,32 +649,73 @@ void
  10. jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
  11. {
  12. jalv->ui_host = suil_host_new(jalv_ui_write, jalv_ui_port_index, NULL, NULL);
  13. + jalv->extuiptr = NULL;
  14. +
  15. + if (jalv->externalui) {
  16. +
  17. + const LV2_Feature external_lv_feature = {
  18. + LV2_EXTERNAL_UI_DEPRECATED_URI, parent
  19. + };
  20. + const LV2_Feature external_kx_feature = {
  21. + LV2_EXTERNAL_UI__Host, parent
  22. + };
  23. + const LV2_Feature instance_feature = {
  24. + NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance)
  25. + };
  26. + const LV2_Feature* ui_features[] = {
  27. + &uri_map_feature, &map_feature, &unmap_feature,
  28. + &instance_feature,
  29. + &log_feature,
  30. + &external_lv_feature,
  31. + &external_kx_feature,
  32. + &options_feature,
  33. + NULL
  34. + };
  35. +
  36. + jalv->ui_instance = suil_instance_new(
  37. + jalv->ui_host,
  38. + jalv,
  39. + native_ui_type,
  40. + lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)),
  41. + lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)),
  42. + lilv_node_as_uri(jalv->ui_type),
  43. + lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))),
  44. + lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))),
  45. + ui_features);
  46. - const LV2_Feature parent_feature = {
  47. - LV2_UI__parent, parent
  48. - };
  49. - const LV2_Feature instance_feature = {
  50. - NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance)
  51. - };
  52. - const LV2_Feature* ui_features[] = {
  53. - &uri_map_feature, &map_feature, &unmap_feature,
  54. - &instance_feature,
  55. - &log_feature,
  56. - &parent_feature,
  57. - &options_feature,
  58. - NULL
  59. - };
  60. + if (jalv->ui_instance) {
  61. + jalv->extuiptr = suil_instance_get_widget((SuilInstance*)jalv->ui_instance);
  62. + } else {
  63. + jalv->externalui = false;
  64. + }
  65. + } else {
  66. - jalv->ui_instance = suil_instance_new(
  67. - jalv->ui_host,
  68. - jalv,
  69. - native_ui_type,
  70. - lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)),
  71. - lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)),
  72. - lilv_node_as_uri(jalv->ui_type),
  73. - lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))),
  74. - lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))),
  75. - ui_features);
  76. + const LV2_Feature parent_feature = {
  77. + LV2_UI__parent, parent
  78. + };
  79. + const LV2_Feature instance_feature = {
  80. + NS_EXT "instance-access", lilv_instance_get_handle(jalv->instance)
  81. + };
  82. + const LV2_Feature* ui_features[] = {
  83. + &uri_map_feature, &map_feature, &unmap_feature,
  84. + &instance_feature,
  85. + &log_feature,
  86. + &parent_feature,
  87. + &options_feature,
  88. + NULL
  89. + };
  90. +
  91. + jalv->ui_instance = suil_instance_new(
  92. + jalv->ui_host,
  93. + jalv,
  94. + native_ui_type,
  95. + lilv_node_as_uri(lilv_plugin_get_uri(jalv->plugin)),
  96. + lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)),
  97. + lilv_node_as_uri(jalv->ui_type),
  98. + lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_bundle_uri(jalv->ui))),
  99. + lilv_uri_to_path(lilv_node_as_uri(lilv_ui_get_binary_uri(jalv->ui))),
  100. + ui_features);
  101. + }
  102. /* Set initial control values on UI */
  103. if (jalv->ui_instance) {
  104. @@ -688,6 +729,7 @@ jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
  105. }
  106. }
  107. +
  108. bool
  109. jalv_ui_is_resizable(Jalv* jalv)
  110. {
  111. @@ -795,6 +837,9 @@ jalv_emit_ui_events(Jalv* jalv)
  112. jalv_ui_port_event(jalv, ev.index, ev.size, ev.protocol, buf);
  113. }
  114. }
  115. + if (jalv->externalui && jalv->extuiptr) {
  116. + LV2_EXTERNAL_UI_RUN(jalv->extuiptr);
  117. + }
  118. return true;
  119. }
  120. @@ -938,6 +983,8 @@ main(int argc, char** argv)
  121. jalv.nodes.rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
  122. jalv.nodes.work_interface = lilv_new_uri(world, LV2_WORKER__interface);
  123. jalv.nodes.work_schedule = lilv_new_uri(world, LV2_WORKER__schedule);
  124. + jalv.nodes.ui_externallv = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external");
  125. + jalv.nodes.ui_externalkx = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget");
  126. jalv.nodes.end = NULL;
  127. /* Get plugin URI from loaded state or command line */
  128. @@ -1013,11 +1060,31 @@ main(int argc, char** argv)
  129. }
  130. }
  131. }
  132. + if (!jalv.ui) {
  133. + LILV_FOREACH(uis, u, jalv.uis) {
  134. + const LilvUI* ui = lilv_uis_get(jalv.uis, u);
  135. + const LilvNodes* types = lilv_ui_get_classes(ui);
  136. + LILV_FOREACH(nodes, t, types) {
  137. + const char * pt = lilv_node_as_uri(lilv_nodes_get(types, t));
  138. + if (!strcmp(pt, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget")) {
  139. + jalv.externalui = true;
  140. + jalv.ui = ui;
  141. + jalv.ui_type = jalv.nodes.ui_externalkx;
  142. + } else if (!strcmp(pt, "http://lv2plug.in/ns/extensions/ui#external")) {
  143. + jalv.externalui = true;
  144. + jalv.ui_type = jalv.nodes.ui_externallv;
  145. + jalv.ui = ui;
  146. + }
  147. + }
  148. + }
  149. + }
  150. /* Create ringbuffers for UI if necessary */
  151. if (jalv.ui) {
  152. fprintf(stderr, "UI: %s\n",
  153. lilv_node_as_uri(lilv_ui_get_uri(jalv.ui)));
  154. + fprintf(stderr, "UI Type: %s\n",
  155. + lilv_node_as_uri(jalv.ui_type));
  156. } else {
  157. fprintf(stderr, "No appropriate UI found\n");
  158. }
  159. diff --git a/jalv/src/jalv_gtk.c b/jalv/src/jalv_gtk.c
  160. index bb92dbb..5599c0c 100644
  161. --- a/jalv/src/jalv_gtk.c
  162. +++ b/jalv/src/jalv_gtk.c
  163. @@ -803,18 +803,28 @@ build_menu(Jalv* jalv, GtkWidget* window, GtkWidget* vbox)
  164. gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
  165. }
  166. +void
  167. +on_external_ui_closed(void* controller)
  168. +{
  169. + Jalv* jalv = (Jalv*) controller;
  170. + jalv_close_ui(jalv);
  171. +}
  172. +
  173. int
  174. jalv_open_ui(Jalv* jalv)
  175. {
  176. + LV2_External_UI_Host extui;
  177. GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  178. jalv->window = window;
  179. jalv->has_ui = TRUE;
  180. + extui.ui_closed = on_external_ui_closed;
  181. g_signal_connect(window, "destroy",
  182. G_CALLBACK(on_window_destroy), jalv);
  183. LilvNode* name = lilv_plugin_get_name(jalv->plugin);
  184. gtk_window_set_title(GTK_WINDOW(window), lilv_node_as_string(name));
  185. + extui.plugin_human_id = jalv_strdup(lilv_node_as_string(name));
  186. lilv_node_free(name);
  187. GtkWidget* vbox = new_box(false, 0);
  188. @@ -832,10 +842,16 @@ jalv_open_ui(Jalv* jalv)
  189. /* Attempt to instantiate custom UI if necessary */
  190. if (jalv->ui && !jalv->opts.generic_ui) {
  191. - jalv_ui_instantiate(jalv, jalv_native_ui_type(jalv), alignment);
  192. + if (jalv->externalui) {
  193. + jalv_ui_instantiate(jalv, lilv_node_as_uri(jalv->ui_type), &extui);
  194. + } else {
  195. + jalv_ui_instantiate(jalv, jalv_native_ui_type(jalv), alignment);
  196. + }
  197. }
  198. - if (jalv->ui_instance) {
  199. + if (jalv->externalui && jalv->extuiptr) {
  200. + LV2_EXTERNAL_UI_SHOW(jalv->extuiptr);
  201. + } else if (jalv->ui_instance) {
  202. GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(
  203. jalv->ui_instance);
  204. @@ -843,6 +859,7 @@ jalv_open_ui(Jalv* jalv)
  205. gtk_window_set_resizable(GTK_WINDOW(window), jalv_ui_is_resizable(jalv));
  206. gtk_widget_show_all(vbox);
  207. gtk_widget_grab_focus(widget);
  208. + gtk_window_present(GTK_WINDOW(window));
  209. } else {
  210. GtkWidget* controls = build_control_widget(jalv, window);
  211. GtkWidget* scroll_win = gtk_scrolled_window_new(NULL, NULL);
  212. @@ -862,13 +879,12 @@ jalv_open_ui(Jalv* jalv)
  213. GTK_WINDOW(window),
  214. MAX(MAX(box_size.width, controls_size.width) + 24, 640),
  215. box_size.height + controls_size.height);
  216. + gtk_window_present(GTK_WINDOW(window));
  217. }
  218. g_timeout_add(1000 / jalv->ui_update_hz,
  219. (GSourceFunc)jalv_emit_ui_events, jalv);
  220. - gtk_window_present(GTK_WINDOW(window));
  221. -
  222. gtk_main();
  223. zix_sem_post(jalv->done);
  224. return 0;
  225. diff --git a/jalv/src/jalv_internal.h b/jalv/src/jalv_internal.h
  226. index 4531ee6..f24c05a 100644
  227. --- a/jalv/src/jalv_internal.h
  228. +++ b/jalv/src/jalv_internal.h
  229. @@ -44,6 +44,8 @@
  230. #include "lv2_evbuf.h"
  231. #include "symap.h"
  232. +#include "lv2_external_ui.h"
  233. +
  234. #ifdef __cplusplus
  235. extern "C" {
  236. #endif
  237. @@ -139,6 +141,8 @@ typedef struct {
  238. LilvNode* rsz_minimumSize;
  239. LilvNode* work_interface;
  240. LilvNode* work_schedule;
  241. + LilvNode* ui_externallv;
  242. + LilvNode* ui_externalkx;
  243. LilvNode* end; ///< NULL terminator for easy freeing of entire structure
  244. } JalvNodes;
  245. @@ -204,6 +208,8 @@ typedef struct {
  246. bool buf_size_set; ///< True iff buffer size callback fired
  247. bool exit; ///< True iff execution is finished
  248. bool has_ui; ///< True iff a control UI is present
  249. + bool externalui; ///< True iff plugin has an external-ui
  250. + LV2_External_UI_Widget* extuiptr; ///< data structure used for external-ui
  251. } Jalv;
  252. int
  253. diff --git a/jalv/src/lv2_external_ui.h b/jalv/src/lv2_external_ui.h
  254. new file mode 100644
  255. index 0000000..2c9e6ee
  256. --- /dev/null
  257. +++ b/jalv/src/lv2_external_ui.h
  258. @@ -0,0 +1,109 @@
  259. +/*
  260. + LV2 External UI extension
  261. + This work is in public domain.
  262. +
  263. + This file is distributed in the hope that it will be useful,
  264. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  265. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  266. +
  267. + If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com>
  268. + or ask in #lad channel, FreeNode IRC network.
  269. +*/
  270. +
  271. +/**
  272. + @file lv2_external_ui.h
  273. + C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>.
  274. +*/
  275. +
  276. +#ifndef LV2_EXTERNAL_UI_H
  277. +#define LV2_EXTERNAL_UI_H
  278. +
  279. +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
  280. +
  281. +#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui"
  282. +#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#"
  283. +
  284. +#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host"
  285. +#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget"
  286. +
  287. +/** This extension used to be defined by a lv2plug.in URI */
  288. +#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external"
  289. +
  290. +#ifdef __cplusplus
  291. +extern "C" {
  292. +#endif
  293. +
  294. +/**
  295. + * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned
  296. + * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget.
  297. + * UI is created in invisible state.
  298. + */
  299. +typedef struct _LV2_External_UI_Widget {
  300. + /**
  301. + * Host calls this function regulary. UI library implementing the
  302. + * callback may do IPC or redraw the UI.
  303. + *
  304. + * @param _this_ the UI context
  305. + */
  306. + void (*run)(struct _LV2_External_UI_Widget * _this_);
  307. +
  308. + /**
  309. + * Host calls this function to make the plugin UI visible.
  310. + *
  311. + * @param _this_ the UI context
  312. + */
  313. + void (*show)(struct _LV2_External_UI_Widget * _this_);
  314. +
  315. + /**
  316. + * Host calls this function to make the plugin UI invisible again.
  317. + *
  318. + * @param _this_ the UI context
  319. + */
  320. + void (*hide)(struct _LV2_External_UI_Widget * _this_);
  321. +
  322. +} LV2_External_UI_Widget;
  323. +
  324. +#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
  325. +#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
  326. +#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
  327. +
  328. +/**
  329. + * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature.
  330. + * LV2_Feature::data must be pointer to LV2_External_UI_Host.
  331. + */
  332. +typedef struct _LV2_External_UI_Host {
  333. + /**
  334. + * Callback that plugin UI will call when UI (GUI window) is closed by user.
  335. + * This callback will be called during execution of LV2_External_UI_Widget::run()
  336. + * (i.e. not from background thread).
  337. + *
  338. + * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup().
  339. + * If host wants to make the UI visible again, the UI must be reinstantiated.
  340. + *
  341. + * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI,
  342. + * some hosts will not call LV2UI_Descriptor::cleanup() as they should,
  343. + * and may call show() again without re-initialization.
  344. + *
  345. + * @param controller Host context associated with plugin UI, as
  346. + * supplied to LV2UI_Descriptor::instantiate().
  347. + */
  348. + void (*ui_closed)(LV2UI_Controller controller);
  349. +
  350. + /**
  351. + * Optional (may be NULL) "user friendly" identifier which the UI
  352. + * may display to allow a user to easily associate this particular
  353. + * UI instance with the correct plugin instance as it is represented
  354. + * by the host (e.g. "track 1" or "channel 4").
  355. + *
  356. + * If supplied by host, the string will be referenced only during
  357. + * LV2UI_Descriptor::instantiate()
  358. + */
  359. + const char * plugin_human_id;
  360. +
  361. +} LV2_External_UI_Host;
  362. +
  363. +#ifdef __cplusplus
  364. +} /* extern "C" */
  365. +#endif
  366. +
  367. +#endif /* LV2_EXTERNAL_UI_H */