Uncrustify (no functional changes).
[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 void
34 LV2PluginUI::lv2_ui_write(LV2UI_Controller controller,
35                           uint32_t         port_index,
36                           uint32_t         /*buffer_size*/,
37                           uint32_t         /*format*/,
38                           const void*      buffer)
39 {
40         LV2PluginUI* me = (LV2PluginUI*)controller;
41         me->_controllables[port_index]->set_value(*(float*)buffer);
42 }
43
44 void
45 LV2PluginUI::on_external_ui_closed(LV2UI_Controller controller)
46 {
47         LV2PluginUI* me = (LV2PluginUI*)controller;
48         me->_screen_update_connection.disconnect();
49         me->_external_ui_ptr = NULL;
50 }
51
52 void
53 LV2PluginUI::parameter_changed(uint32_t port_index, float val)
54 {
55         PlugUIBase::parameter_changed(port_index, val);
56
57         if (val != _values[port_index]) {
58                 parameter_update(port_index, val);
59         }
60 }
61
62 void
63 LV2PluginUI::parameter_update(uint32_t port_index, float val)
64 {
65         if (!_inst) {
66                 return;
67         }
68
69 #ifdef HAVE_NEW_SLV2
70         slv2_ui_instance_port_event(_inst, port_index, 4, 0, &val);
71 #else
72         const LV2UI_Descriptor* ui_desc   = slv2_ui_instance_get_descriptor(_inst);
73         LV2UI_Handle            ui_handle = slv2_ui_instance_get_handle(_inst);
74         if (ui_desc->port_event) {
75                 ui_desc->port_event(ui_handle, port_index, 4, 0, &val);
76         }
77 #endif
78         _values[port_index] = val;
79 }
80
81 bool
82 LV2PluginUI::start_updating(GdkEventAny*)
83 {
84         if (!_output_ports.empty()) {
85                 _screen_update_connection.disconnect();
86                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
87                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
88         }
89         return false;
90 }
91
92 bool
93 LV2PluginUI::stop_updating(GdkEventAny*)
94 {
95         //cout << "stop_updating" << endl;
96
97         if ( //!_external_ui_ptr &&
98             !_output_ports.empty()) {
99                 _screen_update_connection.disconnect();
100         }
101         return false;
102 }
103
104 void
105 LV2PluginUI::output_update()
106 {
107         //cout << "output_update" << endl;
108         if (_external_ui_ptr) {
109                 LV2_EXTERNAL_UI_RUN(_external_ui_ptr);
110         }
111
112         /* FIXME only works with control output ports (which is all we support now anyway) */
113         uint32_t nports = _output_ports.size();
114         for (uint32_t i = 0; i < nports; ++i) {
115                 uint32_t index = _output_ports[i];
116                 parameter_changed(index, _lv2->get_parameter(index));
117         }
118
119 }
120
121 LV2PluginUI::LV2PluginUI(boost::shared_ptr<PluginInsert> pi,
122                          boost::shared_ptr<LV2Plugin>    lv2p)
123         : PlugUIBase(pi)
124         , _lv2(lv2p)
125         , _inst(NULL)
126         , _values(NULL)
127         , _external_ui_ptr(NULL)
128 {
129         if (!_lv2->is_external_ui()) {
130                 lv2ui_instantiate("gtk2gui");
131         }
132 }
133
134 void
135 LV2PluginUI::lv2ui_instantiate(const std::string& title)
136 {
137         LV2_Feature** features;
138         LV2_Feature** features_src;
139         LV2_Feature** features_dst;
140         size_t        features_count;
141         bool          is_external_ui;
142
143         is_external_ui = _lv2->is_external_ui();
144
145         if (is_external_ui) {
146                 _external_ui_host.ui_closed       = LV2PluginUI::on_external_ui_closed;
147                 _external_ui_host.plugin_human_id = strdup(title.c_str());
148
149                 _external_ui_feature.URI  = LV2_EXTERNAL_UI_URI;
150                 _external_ui_feature.data = &_external_ui_host;
151
152                 features_src = features = (LV2_Feature**)_lv2->features();
153                 features_count = 2;
154                 while (*features++) {
155                         features_count++;
156                 }
157                 features_dst = features = (LV2_Feature**)malloc(
158                         sizeof(LV2_Feature*) * features_count);
159                 features_dst[--features_count] = NULL;
160                 features_dst[--features_count] = &_external_ui_feature;
161                 while (features_count--) {
162                         *features++ = *features_src++;
163                 }
164         } else {
165                 features_dst = (LV2_Feature**)_lv2->features();
166         }
167
168 #ifdef HAVE_NEW_SLV2
169         SLV2UIHost ui_host = slv2_ui_host_new(
170                 this, LV2PluginUI::lv2_ui_write, NULL, NULL, NULL);
171         SLV2Value gtk_ui = slv2_value_new_uri(
172                 ARDOUR::PluginManager::the_manager()->lv2_world()->world,
173                 "http://lv2plug.in/ns/extensions/ui#GtkUI");
174         _inst = slv2_ui_instance_new(
175                 _lv2->slv2_plugin(), _lv2->slv2_ui(), gtk_ui, ui_host, features_dst);
176         slv2_value_free(gtk_ui);
177         slv2_ui_host_free(ui_host);
178 #else
179         _inst = slv2_ui_instantiate(
180                 _lv2->slv2_plugin(), _lv2->slv2_ui(), LV2PluginUI::lv2_ui_write, this,
181                 features_dst);
182 #endif
183
184         if (is_external_ui) {
185                 free(features_dst);
186         }
187
188         uint32_t num_ports = slv2_plugin_get_num_ports(_lv2->slv2_plugin());
189         for (uint32_t i = 0; i < num_ports; ++i) {
190                 if (_lv2->parameter_is_output(i)
191                     && _lv2->parameter_is_control(i)
192                     && is_update_wanted(i)) {
193                         _output_ports.push_back(i);
194                 }
195         }
196
197         _external_ui_ptr = NULL;
198         if (_inst) {
199                 if (!is_external_ui) {
200                         GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_inst);
201                         _gui_widget = Glib::wrap(c_widget);
202                         _gui_widget->show_all();
203                         pack_start(*_gui_widget, true, true);
204                 } else {
205                         _external_ui_ptr = (struct lv2_external_ui*)slv2_ui_instance_get_widget(_inst);
206                 }
207         }
208
209         _values = new float[num_ports];
210         _controllables.resize(num_ports);
211         for (uint32_t i = 0; i < num_ports; ++i) {
212                 bool     ok;
213                 uint32_t port = _lv2->nth_parameter(i, ok);
214                 if (ok) {
215                         _values[port]        = _lv2->get_parameter(port);
216                         _controllables[port] = boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (
217                                 insert->control(Evoral::Parameter(PluginAutomation, 0, port)));
218
219                         if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) {
220                                 parameter_update(port, _values[port]);
221                         }
222                 }
223         }
224 }
225
226 LV2PluginUI::~LV2PluginUI ()
227 {
228         //cout << "LV2PluginUI destructor called" << endl;
229
230         if (_values) {
231                 delete[] _values;
232         }
233
234         /* Close and delete GUI. */
235 #ifdef HAVE_NEW_SLV2
236         slv2_ui_instance_free(_inst);
237 #else
238         const LV2UI_Descriptor* ui_desc   = slv2_ui_instance_get_descriptor(_inst);
239         LV2UI_Handle            ui_handle = slv2_ui_instance_get_handle(_inst);
240
241         if (ui_desc) {
242                 ui_desc->cleanup(ui_handle);
243         }
244 #endif
245
246         _screen_update_connection.disconnect();
247
248         if (_lv2->is_external_ui()) {
249                 /* External UI is no longer valid.
250                    on_window_hide() will not try to use it if is NULL.
251                 */
252                 _external_ui_ptr = NULL;
253         }
254 }
255
256 int
257 LV2PluginUI::get_preferred_height()
258 {
259         Gtk::Requisition r = size_request();
260         return r.height;
261 }
262
263 int
264 LV2PluginUI::get_preferred_width()
265 {
266         Gtk::Requisition r = size_request();
267         return r.width;
268 }
269
270 int
271 LV2PluginUI::package(Gtk::Window& win)
272 {
273         if (_external_ui_ptr) {
274                 _win_ptr = &win;
275         } else {
276                 /* forward configure events to plugin window */
277                 win.signal_configure_event().connect(
278                         sigc::mem_fun(*this, &LV2PluginUI::configure_handler));
279                 win.signal_map_event().connect(
280                         sigc::mem_fun(*this, &LV2PluginUI::start_updating));
281                 win.signal_unmap_event().connect(
282                         sigc::mem_fun(*this, &LV2PluginUI::stop_updating));
283         }
284         return 0;
285 }
286
287 bool
288 LV2PluginUI::configure_handler(GdkEventConfigure*)
289 {
290         std::cout << "CONFIGURE" << std::endl;
291         return false;
292 }
293
294 bool
295 LV2PluginUI::is_update_wanted(uint32_t /*index*/)
296 {
297         /* FIXME: use port notification properties
298            and/or new UI extension subscription methods
299         */
300         return true;
301 }
302
303 bool
304 LV2PluginUI::on_window_show(const std::string& title)
305 {
306         //cout << "on_window_show - " << title << endl; flush(cout);
307
308         if (_lv2->is_external_ui()) {
309                 if (_external_ui_ptr) {
310                         LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
311                         return false;
312                 }
313                 lv2ui_instantiate(title);
314                 if (!_external_ui_ptr) {
315                         return false;
316                 }
317
318                 LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
319                 _screen_update_connection.disconnect();
320                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
321                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
322                 return false;
323         }
324
325         return true;
326 }
327
328 void
329 LV2PluginUI::on_window_hide()
330 {
331         //cout << "on_window_hide" << endl; flush(cout);
332
333         if (_external_ui_ptr) {
334                 LV2_EXTERNAL_UI_HIDE(_external_ui_ptr);
335                 //slv2_ui_instance_get_descriptor(_inst)->cleanup(_inst);
336                 //_external_ui_ptr = NULL;
337                 //_screen_update_connection.disconnect();
338         }
339 }