lv2gui.hpp
1 /****************************************************************************
2 
3  lv2gui.hpp - Wrapper library to make it easier to write LV2 GUIs in C++
4 
5  Copyright (C) 2006-2008 Lars Luthman <lars.luthman@gmail.com>
6  Modified by Dave Robillard, 2008 (URI map mixin)
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301 USA
22 
23 ****************************************************************************/
24 
25 #ifndef LV2GUI_HPP
26 #define LV2GUI_HPP
27 
28 #include <cstdlib>
29 #include <cstring>
30 #include <map>
31 
32 #include <gtkmm/box.h>
33 #include <gtkmm/main.h>
34 #include <gtkmm/widget.h>
35 
36 #include <lv2_ui.h>
37 #include <lv2_ui_presets.h>
38 #include <lv2_uri_map.h>
39 #include <lv2_event_helpers.h>
40 #include <lv2_osc.h>
41 #include <lv2types.hpp>
42 
43 
44 namespace LV2 {
45 
46 
49  typedef std::vector<LV2UI_Descriptor*> GUIDescList;
50 
51 
55  GUIDescList& get_lv2g2g_descriptors();
56 
57 
104  template <bool Required = true>
105  struct NoUserResize {
106 
111  template <class Derived> struct I : public Extension<Required> {
112 
114  static void map_feature_handlers(FeatureHandlerMap& hmap) {
115  hmap["http://ll-plugins.nongnu.org/lv2/dev/ui#noUserResize"] =
117  }
118 
120  static void handle_feature(void* instance, void* data) {
121  Derived* d = reinterpret_cast<Derived*>(instance);
122  I<Derived>* e = static_cast<I<Derived>*>(d);
123  e->m_ok = true;
124  }
125 
126  };
127 
128  };
129 
130 
140  template <bool Required = true>
141  struct FixedSize {
142 
147  template <class Derived> struct I : public Extension<Required> {
148 
150  static void map_feature_handlers(FeatureHandlerMap& hmap) {
151  hmap["http://ll-plugins.nongnu.org/lv2/dev/ui#fixedSize"] =
153  }
154 
156  static void handle_feature(void* instance, void* data) {
157  Derived* d = reinterpret_cast<Derived*>(instance);
158  I<Derived>* e = static_cast<I<Derived>*>(d);
159  e->m_ok = true;
160  }
161 
162  };
163 
164  };
165 
166 
175  template <bool Required = true>
176  struct Presets {
177 
182  template <class Derived> struct I : public Extension<Required> {
183 
185  I() : m_hdesc(0), m_host_support(false) { }
186 
188  static void map_feature_handlers(FeatureHandlerMap& hmap) {
189  hmap[LV2_UI_PRESETS_URI] = &I<Derived>::handle_feature;
190  }
191 
193  static void handle_feature(void* instance, void* data) {
194  Derived* d = reinterpret_cast<Derived*>(instance);
195  I<Derived>* e = static_cast<I<Derived>*>(d);
196  e->m_hdesc = static_cast<LV2UI_Presets_Feature*>(data);
197  e->m_ok = (e->m_hdesc != 0);
198  e->m_host_support = (e->m_hdesc != 0);
199  }
200 
201 
207  void preset_added(uint32_t number,
208  char const* name) {
209 
210  }
211 
216  void preset_removed(uint32_t number) {
217 
218  }
219 
224 
225  }
226 
233  void current_preset_changed(uint32_t number) {
234 
235  }
236 
240  static void const* extension_data(char const* uri) {
241  static LV2UI_Presets_GDesc desc = { &_preset_added,
242  &_preset_removed,
243  &_presets_cleared,
244  &_current_preset_changed };
245  if (!std::strcmp(uri, LV2_UI_PRESETS_URI))
246  return &desc;
247  return 0;
248  }
249 
250  protected:
251 
254  void change_preset(uint32_t preset) {
255  if (m_hdesc)
256  m_hdesc->change_preset(static_cast<Derived*>(this)->controller(),
257  preset);
258  }
259 
263  void save_preset(uint32_t preset, char const* name) {
264  if (m_hdesc)
265  m_hdesc->save_preset(static_cast<Derived*>(this)->controller(),
266  preset, name);
267  }
268 
271  bool host_supports_presets() const {
272  return m_host_support;
273  }
274 
275  private:
276 
277  static void _preset_added(LV2UI_Handle gui,
278  uint32_t number,
279  char const* name) {
280  static_cast<Derived*>(gui)->preset_added(number, name);
281  }
282 
283  static void _preset_removed(LV2UI_Handle gui,
284  uint32_t number) {
285  static_cast<Derived*>(gui)->preset_removed(number);
286  }
287 
288  static void _presets_cleared(LV2UI_Handle gui) {
289  static_cast<Derived*>(gui)->presets_cleared();
290  }
291 
292  static void _current_preset_changed(LV2UI_Handle gui,
293  uint32_t number) {
294  static_cast<Derived*>(gui)->current_preset_changed(number);
295  }
296 
297 
298  LV2UI_Presets_Feature* m_hdesc;
299  bool m_host_support;
300 
301  };
302 
303  };
304 
305 
312  template <bool Required = true>
313  struct WriteMIDI {
314 
315  enum {
316  EVENT_BUFFER_SIZE = 4
317  };
318 
323  template <class Derived> struct I : Extension<Required> {
324 
326  I() : m_midi_type(0) {
327  m_buffer = lv2_event_buffer_new(sizeof(LV2_Event) + EVENT_BUFFER_SIZE,
328  0);
329  }
330 
332  bool check_ok() {
333  Derived* d = static_cast<Derived*>(this);
334  m_midi_type = d->
335  uri_to_id(LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent");
336  m_event_buffer_format = d->
337  uri_to_id(LV2_UI_URI, "http://lv2plug.in/ns/extensions/ui#Events");
338  return !Required || (m_midi_type && m_event_buffer_format);
339  }
340 
341  protected:
342 
353  bool write_midi(uint32_t port, uint32_t size, const uint8_t* data) {
354  if (m_midi_type == 0)
355  return false;
356  LV2_Event_Buffer* buffer;
357  if (size <= 4)
358  buffer = m_buffer;
359  else
360  buffer = lv2_event_buffer_new(sizeof(LV2_Event) + size, 0);
361  lv2_event_buffer_reset(m_buffer, 0, m_buffer->data);
362  LV2_Event_Iterator iter;
363  lv2_event_begin(&iter, m_buffer);
364  lv2_event_write(&iter, 0, 0, m_midi_type, size, data);
365  static_cast<Derived*>(this)->
366  write(port, m_buffer->header_size + m_buffer->capacity,
367  m_event_buffer_format, m_buffer);
368  if (size > 4)
369  std::free(buffer);
370  return true;
371  }
372 
373  uint32_t m_midi_type;
374  uint32_t m_event_buffer_format;
375  LV2_Event_Buffer* m_buffer;
376 
377  };
378 
379  };
380 
381 
391  template <bool Required = true>
392  struct WriteOSC {
393 
398  template <class Derived> struct I : Extension<Required> {
399 
400  I() : m_osc_type(0) {
401  m_buffer = lv2_event_buffer_new(sizeof(LV2_Event) + 256, 0);
402  }
403 
404  bool check_ok() {
405  Derived* d = static_cast<Derived*>(this);
406  m_osc_type = d->
407  uri_to_id(LV2_EVENT_URI, "http://lv2plug.in/ns/ext/osc#OscEvent");
408  m_event_buffer_format = d->
409  uri_to_id(LV2_UI_URI, "http://lv2plug.in/ns/extensions/ui#Events");
410  return !Required || (m_osc_type && m_event_buffer_format);
411  }
412 
413  protected:
414 
415  bool write_osc(uint32_t port, const char* path, const char* types, ...) {
416  if (m_osc_type == 0)
417  return false;
418  // XXX handle all sizes here - this is dangerous
419  lv2_event_buffer_reset(m_buffer, 0, m_buffer->data);
420  LV2_Event_Iterator iter;
421  lv2_event_begin(&iter, m_buffer);
422  va_list ap;
423  va_start(ap, types);
424  uint32_t size = lv2_osc_event_vsize(path, types, ap);
425  va_end(ap);
426  if (!size)
427  return false;
428  va_start(ap, types);
429  bool success = lv2_osc_buffer_vappend(&iter, 0, 0, m_osc_type,
430  path, types, size, ap);
431  va_end(ap);
432  if (success) {
433  static_cast<Derived*>(this)->
434  write(port, m_buffer->header_size + m_buffer->capacity,
435  m_event_buffer_format, m_buffer);
436  return true;
437  }
438  return false;
439  }
440 
441  uint32_t m_osc_type;
442  uint32_t m_event_buffer_format;
443  LV2_Event_Buffer* m_buffer;
444 
445  };
446 
447  };
448 
449 
466  template<class Derived, class Ext1 = End, class Ext2 = End, class Ext3 = End,
467  class Ext4 = End, class Ext5 = End, class Ext6 = End,
468  class Ext7 = End, class Ext8 = End, class Ext9 = End>
469  class GUI : public Gtk::HBox, public MixinTree<Derived,
470  Ext1, Ext2, Ext3, Ext4,
471  Ext5, Ext6, Ext7, Ext8, Ext9> {
472  public:
473 
477  inline GUI() {
478  m_ctrl = s_ctrl;
479  m_wfunc = s_wfunc;
480  m_features = s_features;
481  m_bundle_path = s_bundle_path;
482  s_ctrl = 0;
483  s_wfunc = 0;
484  s_features = 0;
485  s_bundle_path = 0;
486  if (m_features) {
487  FeatureHandlerMap hmap;
488  Derived::map_feature_handlers(hmap);
489  for (const Feature* const* iter = m_features; *iter != 0; ++iter) {
490  FeatureHandlerMap::iterator miter;
491  miter = hmap.find((*iter)->URI);
492  if (miter != hmap.end())
493  miter->second(static_cast<Derived*>(this), (*iter)->data);
494  }
495  }
496  }
497 
500  inline void port_event(uint32_t port, uint32_t buffer_size,
501  uint32_t format, void const* buffer) { }
502 
504  static int register_class(char const* uri) {
505  LV2UI_Descriptor* desc = new LV2UI_Descriptor;
506  std::memset(desc, 0, sizeof(LV2UI_Descriptor));
507  desc->URI = strdup(uri);
508  desc->instantiate = &Derived::create_ui_instance;
509  desc->cleanup = &Derived::delete_ui_instance;
510  desc->port_event = &Derived::_port_event;
511  desc->extension_data = &Derived::extension_data;
512  get_lv2g2g_descriptors().push_back(desc);
513  return get_lv2g2g_descriptors().size() - 1;
514  }
515 
516  protected:
517 
522  inline void write(uint32_t port, uint32_t buffer_size,
523  uint32_t format, void const* buffer) {
524  (*m_wfunc)(m_ctrl, port, buffer_size, format, buffer);
525  }
526 
528  inline void write_control(uint32_t port, float value) {
529  write(port, sizeof(float), 0, &value);
530  }
531 
534  inline LV2::Feature const* const* features() {
535  return m_features;
536  }
537 
539  inline char const* bundle_path() const {
540  return m_bundle_path;
541  }
542 
543  public:
547  inline void* controller() {
548  return m_ctrl;
549  }
550 
551 
552  private:
553 
554  // This is quite ugly but needed to allow these mixins to call
555  // protected functions in the GUI class, which we want.
556  friend class WriteMIDI<true>::I<Derived>;
557  friend class WriteMIDI<false>::I<Derived>;
558  friend class WriteOSC<true>::I<Derived>;
559  friend class WriteOSC<false>::I<Derived>;
560 
565  static LV2UI_Handle create_ui_instance(struct _LV2UI_Descriptor const*
566  descriptor,
567  char const* plugin_uri,
568  char const* bundle_path,
569  LV2UI_Write_Function write_func,
570  LV2UI_Controller ctrl,
571  LV2UI_Widget* widget,
572  Feature const* const* features) {
573 
574  // copy some data to static variables so the subclasses don't have to
575  // bother with it - this is threadsafe since hosts are not allowed
576  // to instantiate the same plugin concurrently
577  s_ctrl = ctrl;
578  s_wfunc = write_func;
579  s_features = features;
580  s_bundle_path = bundle_path;
581 
582  // this is needed to initialise gtkmm stuff in case we're running in
583  // a Gtk+ or PyGtk host or some other language
584  Gtk::Main::init_gtkmm_internals();
585 
586  // create the GUI object
587  Derived* t = new Derived(plugin_uri);
588  *widget = static_cast<Gtk::Widget*>(t)->gobj();
589 
590  // check that everything is OK
591  if (t->check_ok())
592  return reinterpret_cast<LV2UI_Handle>(t);
593  delete t;
594  return 0;
595  }
596 
597 
602  static void delete_ui_instance(LV2UI_Handle instance) {
603  delete static_cast<Derived*>(instance);
604  }
605 
606 
610  static void _port_event(LV2UI_Handle instance, uint32_t port,
611  uint32_t buffer_size, uint32_t format,
612  void const* buffer) {
613  static_cast<Derived*>(instance)->port_event(port, buffer_size,
614  format, buffer);
615  }
616 
617 
618  void* m_ctrl;
619  LV2UI_Write_Function m_wfunc;
620  LV2::Feature const* const* m_features;
621  char const* m_bundle_path;
622 
623  static void* s_ctrl;
624  static LV2UI_Write_Function s_wfunc;
625  static LV2::Feature const* const* s_features;
626  static char const* s_bundle_path;
627 
628  };
629 
630 
631  /* Yes, static variables are messy. */
632  template<class Derived, class Ext1, class Ext2, class Ext3, class Ext4,
633  class Ext5, class Ext6, class Ext7, class Ext8, class Ext9>
634  void* GUI<Derived, Ext1, Ext2, Ext3, Ext4,
635  Ext5, Ext6, Ext7, Ext8, Ext9>::s_ctrl = 0;
636 
637  template<class Derived, class Ext1, class Ext2, class Ext3, class Ext4,
638  class Ext5, class Ext6, class Ext7, class Ext8, class Ext9>
639  LV2UI_Write_Function GUI<Derived, Ext1, Ext2, Ext3, Ext4,
640  Ext5, Ext6, Ext7, Ext8, Ext9>::s_wfunc = 0;
641 
642  template<class Derived, class Ext1, class Ext2, class Ext3, class Ext4,
643  class Ext5, class Ext6, class Ext7, class Ext8, class Ext9>
644  LV2::Feature const* const* GUI<Derived, Ext1, Ext2, Ext3, Ext4,
645  Ext5, Ext6, Ext7, Ext8, Ext9>::s_features = 0;
646 
647  template<class Derived, class Ext1, class Ext2, class Ext3, class Ext4,
648  class Ext5, class Ext6, class Ext7, class Ext8, class Ext9>
649  char const* GUI<Derived, Ext1, Ext2, Ext3, Ext4,
650  Ext5, Ext6, Ext7, Ext8, Ext9>::s_bundle_path = 0;
651 
652 
653 }
654 
655 
656 #endif