2f108efc8a928616b38d56d96abe9c4b8a5bc512
[ardour.git] / gtk2_ardour / lv2_plugin_ui.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3     Author: Dave 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/processor.h"
22 #include "ardour/lv2_plugin.h"
23
24 #include "ardour_ui.h"
25 #include "lv2_plugin_ui.h"
26
27 using namespace Gtk;
28 using namespace ARDOUR;
29 using namespace PBD;
30
31 std::vector<struct lv2_external_ui*> g_external_uis;
32
33 void close_external_ui_windows()
34 {
35         struct lv2_external_ui* external_ui_ptr;
36
37         //cout << "close_external_ui_windows" << endl;
38
39         while (!g_external_uis.empty()) {
40                 //cout << "pop" << endl;
41                 external_ui_ptr = g_external_uis.back();
42                 LV2_EXTERNAL_UI_HIDE(external_ui_ptr);
43                 g_external_uis.pop_back();
44         }
45 }
46
47 void
48 LV2PluginUI::lv2_ui_write(
49                 LV2UI_Controller controller,
50                 uint32_t         port_index,
51                 uint32_t         /*buffer_size*/,
52                 uint32_t         /*format*/,
53                 const void*      buffer)
54 {
55         //cout << "lv2_ui_write" << endl;
56         LV2PluginUI* me = (LV2PluginUI*)controller;
57         if (*(float*)buffer != me->_values[port_index]) {
58                 //cout << "set_parameter " << port_index << ":"  << *(float*)buffer << endl;
59                 me->_lv2->set_parameter(port_index, *(float*)buffer);
60   }
61 }
62
63 void LV2PluginUI::on_external_ui_closed(LV2UI_Controller controller)
64 {
65         //cout << "on_external_ui_closed" << endl;
66
67         LV2PluginUI* me = (LV2PluginUI*)controller;
68         me->_screen_update_connection.disconnect();
69         //me->insert->set_gui(0);
70
71         for (std::vector<struct lv2_external_ui*>::iterator it = g_external_uis.begin() ; it < g_external_uis.end(); it++) {
72                 if (*it == me->_external_ui_ptr) {
73                         g_external_uis.erase(it);
74                 }
75         }
76
77         //slv2_ui_instance_get_descriptor(me->_inst)->cleanup(me->_inst);
78         me->_external_ui_ptr = NULL;
79 }
80
81 void
82 LV2PluginUI::parameter_changed (uint32_t port_index, float val)
83 {
84         //cout << "parameter_changed" << endl;
85         if (val != _values[port_index]) {
86                 parameter_update(port_index, val);
87         }
88 }
89
90 void
91 LV2PluginUI::parameter_update (uint32_t port_index, float val)
92 {
93         if (!_inst) {
94                 return;
95         }
96
97         const LV2UI_Descriptor* ui_desc = slv2_ui_instance_get_descriptor(_inst);
98         LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(_inst);
99         if (ui_desc->port_event)
100                 ui_desc->port_event(ui_handle, port_index, 4, 0, &val);
101         _values[port_index] = val;
102 }
103
104 bool
105 LV2PluginUI::start_updating(GdkEventAny*)
106 {
107         if (!_output_ports.empty()) {
108                 _screen_update_connection.disconnect();
109                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
110                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
111         }
112         return false;
113 }
114
115 bool
116 LV2PluginUI::stop_updating(GdkEventAny*)
117 {
118         //cout << "stop_updating" << endl;
119
120         if (//!_external_ui_ptr &&
121                         !_output_ports.empty()) {
122                 _screen_update_connection.disconnect();
123         }
124         return false;
125 }
126
127 void
128 LV2PluginUI::output_update()
129 {
130         //cout << "output_update" << endl;
131         if (_external_ui_ptr) {
132                 LV2_EXTERNAL_UI_RUN(_external_ui_ptr);
133         }
134
135         /* FIXME only works with control output ports (which is all we support now anyway) */
136         uint32_t nports = _output_ports.size();
137         for (uint32_t i = 0; i < nports; ++i) {
138                 uint32_t index = _output_ports[i];
139                 parameter_changed(index, _lv2->get_parameter(index));
140         }
141
142 }
143
144 LV2PluginUI::LV2PluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr<LV2Plugin> lv2p)
145         : PlugUIBase (pi)
146         , _lv2(lv2p)
147         , _inst(NULL)
148         , _values(NULL)
149         , _external_ui_ptr(NULL)
150 {
151         if (!_lv2->is_external_ui()) {
152                 lv2ui_instantiate("gtk2gui");
153         }
154 }
155
156 void
157 LV2PluginUI::lv2ui_instantiate(const Glib::ustring& title)
158 {
159         LV2_Feature** features;
160         LV2_Feature** features_src;
161         LV2_Feature** features_dst;
162         size_t features_count;
163         bool is_external_ui;
164
165         is_external_ui = _lv2->is_external_ui();
166
167         if (is_external_ui) {
168                 _external_ui_host.ui_closed = LV2PluginUI::on_external_ui_closed;
169                 _external_ui_host.plugin_human_id = strdup(title.c_str());
170
171                 _external_ui_feature.URI = LV2_EXTERNAL_UI_URI;
172                 _external_ui_feature.data = &_external_ui_host;
173
174                 features_src = features = (LV2_Feature**)_lv2->features();
175                 features_count = 2;
176                 while (*features++) {
177                         features_count++;
178                 }
179
180                 features_dst = features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * features_count);
181                 features_dst[--features_count] = NULL;
182                 features_dst[--features_count] = &_external_ui_feature;
183                 while (features_count--) {
184                         *features++ = *features_src++;
185                 }
186         } else {
187                 features_dst = (LV2_Feature**)_lv2->features();
188         }
189
190         _inst = slv2_ui_instantiate(
191                         _lv2->slv2_plugin(), _lv2->slv2_ui(), LV2PluginUI::lv2_ui_write, this,
192                         features_dst);
193
194         if (is_external_ui) {
195                 free(features_dst);
196         }
197
198         uint32_t num_ports = slv2_plugin_get_num_ports(_lv2->slv2_plugin());
199         for (uint32_t i = 0; i < num_ports; ++i) {
200                 if (_lv2->parameter_is_output(i) && _lv2->parameter_is_control(i) && is_update_wanted(i)) {
201                         _output_ports.push_back(i);
202                 }
203         }
204
205         _external_ui_ptr = NULL;
206         if (_inst) {
207                 if (!is_external_ui) {
208                         GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_inst);
209                         _gui_widget = Glib::wrap(c_widget);
210                         _gui_widget->show_all();
211                         pack_start(*_gui_widget, true, true);
212                 } else {
213                         _external_ui_ptr = (struct lv2_external_ui *)slv2_ui_instance_get_widget(_inst);
214                         g_external_uis.push_back(_external_ui_ptr);
215                 }
216         }
217
218         _values = new float[num_ports];
219         for (uint32_t i = 0; i < num_ports; ++i) {
220                 bool ok;
221                 uint32_t port = _lv2->nth_parameter(i, ok);
222                 if (ok) {
223                         _values[port] = _lv2->get_parameter(port);
224                         if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) {
225                                 parameter_update(port, _values[port]);
226                         }
227                 }
228         }
229
230         _lv2->ParameterChanged.connect (parameter_connection, boost::bind (&LV2PluginUI::parameter_changed, this, _1, _2));
231 }
232
233 LV2PluginUI::~LV2PluginUI ()
234 {
235         //cout << "LV2PluginUI destructor called" << endl;
236
237         if (_values) {
238                 delete[] _values;
239         }
240         // plugin destructor destroys the GTK GUI
241 }
242
243 int
244 LV2PluginUI::get_preferred_height ()
245 {
246         Gtk::Requisition r = size_request();
247         return r.height;
248 }
249
250 int
251 LV2PluginUI::get_preferred_width ()
252 {
253         Gtk::Requisition r = size_request();
254         return r.width;
255 }
256
257 int
258 LV2PluginUI::package (Gtk::Window& win)
259 {
260         //cout << "package" << endl;
261         if (_external_ui_ptr) {
262                 _win_ptr = &win;
263         } else {
264                 /* forward configure events to plugin window */
265                 win.signal_configure_event().connect (sigc::mem_fun (*this, &LV2PluginUI::configure_handler));
266                 win.signal_map_event().connect (sigc::mem_fun (*this, &LV2PluginUI::start_updating));
267                 win.signal_unmap_event().connect (sigc::mem_fun (*this, &LV2PluginUI::stop_updating));
268         }
269         return 0;
270 }
271
272 bool
273 LV2PluginUI::configure_handler (GdkEventConfigure*)
274 {
275         std::cout << "CONFIGURE" << std::endl;
276         return false;
277 }
278
279 bool
280 LV2PluginUI::is_update_wanted(uint32_t /*index*/)
281 {
282         /* FIXME this should check the port notification properties, which nobody sets now anyway :) */
283         return true;
284 }
285
286 bool
287 LV2PluginUI::on_window_show(const Glib::ustring& title)
288 {
289         //cout << "on_window_show - " << title << endl; flush(cout);
290
291         if (_lv2->is_external_ui()) {
292                 if (_external_ui_ptr) {
293                         LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
294                         return false;
295                 }
296                 lv2ui_instantiate(title);
297                 if (!_external_ui_ptr) {
298                         return false;
299                 }
300
301                 LV2_EXTERNAL_UI_SHOW(_external_ui_ptr);
302                 _screen_update_connection.disconnect();
303                 _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
304                         (sigc::mem_fun(*this, &LV2PluginUI::output_update));
305                 return false;
306         }
307
308         return true;
309 }
310
311 void
312 LV2PluginUI::on_window_hide()
313 {
314         //cout << "on_window_hide" << endl; flush(cout);
315
316         if (_external_ui_ptr) {
317                 LV2_EXTERNAL_UI_HIDE(_external_ui_ptr);
318                 //slv2_ui_instance_get_descriptor(_inst)->cleanup(_inst);
319                 //_external_ui_ptr = NULL;
320                 //_screen_update_connection.disconnect();
321         }
322 }