Fix LV2 UI related memory errors with new SLV2.
[ardour.git] / gtk2_ardour / lv2_plugin_ui.cc
1 /*
2     Copyright (C) 2008-2011 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "ardour/lv2_plugin.h"
22 #include "ardour/plugin_manager.h"
23 #include "ardour/processor.h"
24
25 #include "ardour_ui.h"
26 #include "gui_thread.h"
27 #include "lv2_plugin_ui.h"
28
29 using namespace Gtk;
30 using namespace ARDOUR;
31 using namespace PBD;
32
33 #ifdef HAVE_NEW_SLV2
34 SLV2UIHost LV2PluginUI::ui_host  = NULL;
35 SLV2Value  LV2PluginUI::ui_GtkUI = NULL;
36 #endif
37
38 void
39 LV2PluginUI::lv2_ui_write(LV2UI_Controller controller,
40                           uint32_t         port_index,
41                           uint32_t         /*buffer_size*/,
42                           uint32_t         /*format*/,
43                           const void*      buffer)
44 {
45         LV2PluginUI* me = (LV2PluginUI*)controller;
46         me->_controllables[port_index]->set_value(*(float*)buffer);
47 }
48
49 void
50 LV2PluginUI::on_external_ui_closed(LV2UI_Controller controller)
51 {
52         LV2PluginUI* me = (LV2PluginUI*)controller;
53         me->_screen_update_connection.disconnect();
54         me->_external_ui_ptr = NULL;
55 }
56
57 void
58 LV2PluginUI::parameter_changed(uint32_t port_index, float val)
59 {
60         PlugUIBase::parameter_changed(port_index, val);
61
62         if (val != _values[port_index]) {
63                 parameter_update(port_index, val);
64         }
65 }
66
67 void
68 LV2PluginUI::parameter_update(uint32_t port_index, float val)
69 {
70         if (!_inst) {
71                 return;
72         }
73
74 #ifdef HAVE_NEW_SLV2
75         slv2_ui_instance_port_event(_inst, port_index, 4, 0, &val);
76 #else
77         const LV2UI_Descriptor* ui_desc   = slv2_ui_instance_get_descriptor(_inst);
78         LV2UI_Handle            ui_handle = slv2_ui_instance_get_handle(_inst);
79         if (ui_desc->port_event) {
80                 ui_desc->port_event(ui_handle, port_index, 4, 0, &val);
81         }
82 #endif
83         _values[port_index] = val;
84 }
85
86 bool
87 LV2PluginUI::start_updating(GdkEventAny*)
88 {
89         if (!_output_ports.empty()) {
90                 _screen_update_connection.disconnect();
91                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
92                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
93         }
94         return false;
95 }
96
97 bool
98 LV2PluginUI::stop_updating(GdkEventAny*)
99 {
100         //cout << "stop_updating" << endl;
101
102         if ( //!_external_ui_ptr &&
103             !_output_ports.empty()) {
104                 _screen_update_connection.disconnect();
105         }
106         return false;
107 }
108
109 void
110 LV2PluginUI::output_update()
111 {
112         //cout << "output_update" << endl;
113         if (_external_ui_ptr) {
114                 LV2_EXTERNAL_UI_RUN(_external_ui_ptr);
115         }
116
117         /* FIXME only works with control output ports (which is all we support now anyway) */
118         uint32_t nports = _output_ports.size();
119         for (uint32_t i = 0; i < nports; ++i) {
120                 uint32_t index = _output_ports[i];
121                 parameter_changed(index, _lv2->get_parameter(index));
122         }
123
124 }
125
126 LV2PluginUI::LV2PluginUI(boost::shared_ptr<PluginInsert> pi,
127                          boost::shared_ptr<LV2Plugin>    lv2p)
128         : PlugUIBase(pi)
129         , _lv2(lv2p)
130         , _inst(NULL)
131         , _values(NULL)
132         , _external_ui_ptr(NULL)
133 {
134         if (!_lv2->is_external_ui()) {
135                 lv2ui_instantiate("gtk2gui");
136         }
137 }
138
139 void
140 LV2PluginUI::lv2ui_instantiate(const std::string& title)
141 {
142         LV2_Feature** features;
143         LV2_Feature** features_src;
144         LV2_Feature** features_dst;
145         size_t        features_count;
146         bool          is_external_ui;
147
148         is_external_ui = _lv2->is_external_ui();
149
150         if (is_external_ui) {
151                 _external_ui_host.ui_closed       = LV2PluginUI::on_external_ui_closed;
152                 _external_ui_host.plugin_human_id = strdup(title.c_str());
153
154                 _external_ui_feature.URI  = LV2_EXTERNAL_UI_URI;
155                 _external_ui_feature.data = &_external_ui_host;
156
157                 features_src = features = (LV2_Feature**)_lv2->features();
158                 features_count = 2;
159                 while (*features++) {
160                         features_count++;
161                 }
162                 features_dst = features = (LV2_Feature**)malloc(
163                         sizeof(LV2_Feature*) * features_count);
164                 features_dst[--features_count] = NULL;
165                 features_dst[--features_count] = &_external_ui_feature;
166                 while (features_count--) {
167                         *features++ = *features_src++;
168                 }
169         } else {
170                 features_dst = (LV2_Feature**)_lv2->features();
171         }
172
173 #ifdef HAVE_NEW_SLV2
174         if (!LV2PluginUI::ui_host) {
175                 LV2PluginUI::ui_GtkUI = slv2_value_new_uri(
176                         ARDOUR::PluginManager::the_manager()->lv2_world()->world,
177                         "http://lv2plug.in/ns/extensions/ui#GtkUI");
178                 LV2PluginUI::ui_host = slv2_ui_host_new(
179                 LV2PluginUI::lv2_ui_write, NULL, NULL, NULL);
180         }
181         _inst = slv2_ui_instance_new(
182                 _lv2->slv2_plugin(), _lv2->slv2_ui(), ui_GtkUI, ui_host, this, features_dst);
183 #else
184         _inst = slv2_ui_instantiate(
185                 _lv2->slv2_plugin(), _lv2->slv2_ui(), LV2PluginUI::lv2_ui_write, this,
186                 features_dst);
187 #endif
188
189         if (is_external_ui) {
190                 free(features_dst);
191         }
192
193         uint32_t num_ports = slv2_plugin_get_num_ports(_lv2->slv2_plugin());
194         for (uint32_t i = 0; i < num_ports; ++i) {
195                 if (_lv2->parameter_is_output(i)
196                     && _lv2->parameter_is_control(i)
197                     && is_update_wanted(i)) {
198                         _output_ports.push_back(i);
199                 }
200         }
201
202         _external_ui_ptr = NULL;
203         if (_inst) {
204                 if (!is_external_ui) {
205                         GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_inst);
206                         _gui_widget = Glib::wrap(c_widget);
207                         _gui_widget->show_all();
208                         pack_start(*_gui_widget, true, true);
209                 } else {
210                         _external_ui_ptr = (struct lv2_external_ui*)slv2_ui_instance_get_widget(_inst);
211                 }
212         }
213
214         _values = new float[num_ports];
215         _controllables.resize(num_ports);
216         for (uint32_t i = 0; i < num_ports; ++i) {
217                 bool     ok;
218                 uint32_t port = _lv2->nth_parameter(i, ok);
219                 if (ok) {
220                         _values[port]        = _lv2->get_parameter(port);
221                         _controllables[port] = boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (
222                                 insert->control(Evoral::Parameter(PluginAutomation, 0, port)));
223
224                         if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) {
225                                 parameter_update(port, _values[port]);
226                         }
227                 }
228         }
229 }
230
231 LV2PluginUI::~LV2PluginUI ()
232 {
233         //cout << "LV2PluginUI destructor called" << endl;
234
235         if (_values) {
236                 delete[] _values;
237         }
238
239         /* Close and delete GUI. */
240 #ifdef HAVE_NEW_SLV2
241         slv2_ui_instance_free(_inst);
242 #else
243         const LV2UI_Descriptor* ui_desc   = slv2_ui_instance_get_descriptor(_inst);
244         LV2UI_Handle            ui_handle = slv2_ui_instance_get_handle(_inst);
245
246         if (ui_desc) {
247                 ui_desc->cleanup(ui_handle);
248         }
249 #endif
250
251         _screen_update_connection.disconnect();
252
253         if (_lv2->is_external_ui()) {
254                 /* External UI is no longer valid.
255                    on_window_hide() will not try to use it if is NULL.
256                 */
257                 _external_ui_ptr = NULL;
258         }
259 }
260
261 int
262 LV2PluginUI::get_preferred_height()
263 {
264         Gtk::Requisition r = size_request();
265         return r.height;
266 }
267
268 int
269 LV2PluginUI::get_preferred_width()
270 {
271         Gtk::Requisition r = size_request();
272         return r.width;
273 }
274
275 int
276 LV2PluginUI::package(Gtk::Window& win)
277 {
278         if (_external_ui_ptr) {
279                 _win_ptr = &win;
280         } else {
281                 /* forward configure events to plugin window */
282                 win.signal_configure_event().connect(
283                         sigc::mem_fun(*this, &LV2PluginUI::configure_handler));
284                 win.signal_map_event().connect(
285                         sigc::mem_fun(*this, &LV2PluginUI::start_updating));
286                 win.signal_unmap_event().connect(
287                         sigc::mem_fun(*this, &LV2PluginUI::stop_updating));
288         }
289         return 0;
290 }
291
292 bool
293 LV2PluginUI::configure_handler(GdkEventConfigure*)
294 {
295         std::cout << "CONFIGURE" << std::endl;
296         return false;
297 }
298
299 bool
300 LV2PluginUI::is_update_wanted(uint32_t /*index*/)
301 {
302         /* FIXME: use port notification properties
303            and/or new UI extension subscription methods
304         */
305         return true;
306 }
307
308 bool
309 LV2PluginUI::on_window_show(const std::string& title)
310 {
311         //cout << "on_window_show - " << title << endl; flush(cout);
312
313         if (_lv2->is_external_ui()) {
314                 if (_external_ui_ptr) {
315                         LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
316                         return false;
317                 }
318                 lv2ui_instantiate(title);
319                 if (!_external_ui_ptr) {
320                         return false;
321                 }
322
323                 LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
324                 _screen_update_connection.disconnect();
325                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
326                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
327                 return false;
328         }
329
330         return true;
331 }
332
333 void
334 LV2PluginUI::on_window_hide()
335 {
336         //cout << "on_window_hide" << endl; flush(cout);
337
338         if (_external_ui_ptr) {
339                 LV2_EXTERNAL_UI_HIDE(_external_ui_ptr);
340                 //slv2_ui_instance_get_descriptor(_inst)->cleanup(_inst);
341                 //_external_ui_ptr = NULL;
342                 //_screen_update_connection.disconnect();
343         }
344 }