b8f751ff083f51237099bacacac25eda5887b551
[ardour.git] / gtk2_ardour / lxvst_pluginui.cc
1 /*
2     Copyright (C) 2004 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /******************************************************************/
21 /*linuxDSP - pluginui variant for LXVST (native Linux VST) Plugins*/
22 /******************************************************************/
23
24 #include <ardour/vstfx.h>
25 #include <gtk/gtk.h>
26 #include <gtk/gtksocket.h>
27 #include <ardour/processor.h>
28 #include <ardour/lxvst_plugin.h>
29
30 #include "ardour_ui.h"
31 #include "plugin_ui.h"
32 #include "lxvst_plugin_ui.h"
33
34 #include <gdk/gdkx.h>
35
36 #define LXVST_H_FIDDLE 40
37
38 using namespace Gtk;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 LXVSTPluginUI::LXVSTPluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr<LXVSTPlugin> lxvp)
43         : PlugUIBase (pi),
44           lxvst (lxvp)
45 {
46         create_preset_store ();
47
48         vstfx_run_editor (lxvst->vstfx());
49
50         if (lxvst->vstfx()->current_program != -1) {
51                 lxvst_preset_combo.set_active (lxvst->vstfx()->current_program);
52         } else {
53                 lxvst_preset_combo.set_active (0);
54         }
55         
56         preset_box.set_spacing (6);
57         preset_box.set_border_width (6);
58         preset_box.pack_end (bypass_button, false, false, 10);
59         preset_box.pack_end (save_button, false, false);
60         preset_box.pack_end (lxvst_preset_combo, false, false);
61
62         lxvst_preset_combo.signal_changed().connect (mem_fun (*this, &LXVSTPluginUI::preset_chosen));
63
64         if (!insert->active()) {
65                 bypass_button.set_active_state (Gtkmm2ext::Active);
66         } else {
67                 bypass_button.unset_active_state ();
68         }
69         
70         pack_start (preset_box, false, false);
71         pack_start (socket, true, true);
72 }
73
74 LXVSTPluginUI::~LXVSTPluginUI ()
75 {
76
77         _screen_update_connection.disconnect(); 
78         
79         // plugin destructor destroys the custom GUI, via the vstfx engine,
80         // and then our PluginUIWindow does the rest
81 }
82
83
84 bool
85 LXVSTPluginUI::start_updating (GdkEventAny* ignored)
86 {
87         _screen_update_connection.disconnect();
88         _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect 
89                         (mem_fun(*this, &LXVSTPluginUI::resize_callback));
90         return false;
91 }
92
93 bool
94 LXVSTPluginUI::stop_updating (GdkEventAny* ignored)
95 {
96         _screen_update_connection.disconnect();
97         return false;
98 }
99
100
101 void
102 LXVSTPluginUI::resize_callback()
103 {
104         /*We could maybe use this to resize the plugin GTK parent window
105         if required*/
106         
107         if(lxvst->vstfx()->want_resize)
108         {
109                 int new_height = lxvst->vstfx()->height;
110                 int new_width = lxvst->vstfx()->width;
111                 
112                 void* gtk_parent_window = lxvst->vstfx()->extra_data;
113                 
114                 if(gtk_parent_window)
115                         ((Gtk::Window*)gtk_parent_window)->resize(new_width, new_height + LXVST_H_FIDDLE);
116                 
117                 lxvst->vstfx()->want_resize = 0;
118         }
119 }
120
121 void
122 LXVSTPluginUI::preset_selected ()
123 {
124         socket.grab_focus ();
125         PlugUIBase::preset_selected ();
126 }
127
128 void
129 LXVSTPluginUI::preset_chosen ()
130 {
131         // we can't dispatch directly here, too many plugins only expects one GUI thread.
132         
133         lxvst->vstfx()->want_program = lxvst_preset_combo.get_active_row_number ();
134         socket.grab_focus ();
135 }
136
137 int
138 LXVSTPluginUI::get_preferred_height ()
139 {       
140         /*FIXME*/
141         
142         /*We have to return the required height of the plugin UI window +  a fiddle factor
143         because we can't know how big the preset menu bar is until the window is realised
144         and we can't realise it until we have told it how big we would like it to be
145         which we can't do until it is realised etc*/
146
147         return (lxvst->vstfx()->height) + LXVST_H_FIDDLE; //May not be 40 for all screen res etc
148 }
149
150 int
151 LXVSTPluginUI::get_preferred_width ()
152 {
153         return lxvst->vstfx()->width;
154 }
155
156 int
157 LXVSTPluginUI::package (Gtk::Window& win)
158 {
159         /* forward configure events to plugin window */
160
161         win.signal_configure_event().connect (bind (mem_fun (*this, &LXVSTPluginUI::configure_handler), &socket), false);
162         
163         /*Map the UI start and stop updating events to 'Map' events on the Window*/
164         
165         win.signal_map_event().connect (mem_fun (*this, &LXVSTPluginUI::start_updating));
166         win.signal_unmap_event().connect (mem_fun (*this, &LXVSTPluginUI::stop_updating));
167
168         /* this assumes that the window's owner understands the XEmbed protocol. */
169         
170         socket.add_id (vstfx_get_XID (lxvst->vstfx()));
171
172         vstfx_move_window_into_view (lxvst->vstfx());
173         
174         lxvst->vstfx()->extra_data = (void*)(&win);
175         lxvst->vstfx()->want_resize = 0;
176
177         return 0;
178 }
179
180 bool
181 LXVSTPluginUI::configure_handler (GdkEventConfigure* ev, Gtk::Socket *socket)
182 {
183         XEvent event;
184         gint x, y;
185         GdkWindow* w;
186
187         if (socket == 0 || ((w = socket->gobj()->plug_window) == 0)) {
188                 return false;
189         }
190
191         event.xconfigure.type = ConfigureNotify;
192         event.xconfigure.event = GDK_WINDOW_XWINDOW (w);
193         event.xconfigure.window = GDK_WINDOW_XWINDOW (w);
194
195         /* The ICCCM says that synthetic events should have root relative
196          * coordinates. We still aren't really ICCCM compliant, since
197          * we don't send events when the real toplevel is moved.
198          */
199         gdk_error_trap_push ();
200         gdk_window_get_origin (w, &x, &y);
201         gdk_error_trap_pop ();
202
203         event.xconfigure.x = x;
204         event.xconfigure.y = y;
205         event.xconfigure.width = GTK_WIDGET(socket->gobj())->allocation.width;
206         event.xconfigure.height = GTK_WIDGET(socket->gobj())->allocation.height;
207
208         event.xconfigure.border_width = 0;
209         event.xconfigure.above = None;
210         event.xconfigure.override_redirect = False;
211
212         gdk_error_trap_push ();
213         XSendEvent (GDK_WINDOW_XDISPLAY (w), GDK_WINDOW_XWINDOW (w), False, StructureNotifyMask, &event);
214         gdk_error_trap_pop ();
215
216         return false;
217 }
218
219 void
220 LXVSTPluginUI::forward_key_event (GdkEventKey* ev)
221 {
222         std::cerr << "LXVSTPluginUI : keypress forwarding to linuxVSTs unsupported" << std::endl;
223 }
224
225
226 void
227 LXVSTPluginUI::create_preset_store ()
228 {
229         VSTState* vstfx = lxvst->vstfx();
230         
231         int vst_version = vstfx->plugin->dispatcher (vstfx->plugin, effGetVstVersion, 0, 0, NULL, 0.0f);
232
233         preset_model = ListStore::create (preset_columns);
234
235         for (int i = 0; i < vstfx->plugin->numPrograms; ++i) {
236                 char buf[100];
237                 TreeModel::Row row = *(preset_model->append());
238         
239                 snprintf (buf, 90, "preset %d", i);
240         
241                 if (vst_version >= 2) {
242                         vstfx->plugin->dispatcher (vstfx->plugin, 29, i, 0, buf, 0.0);
243                 }
244                 
245                 row[preset_columns.name] = buf;
246                 row[preset_columns.number] = i;
247         }
248         
249         lxvst_preset_combo.set_model (preset_model);
250
251         CellRenderer* renderer = manage (new CellRendererText());
252         lxvst_preset_combo.pack_start (*renderer, true);
253         lxvst_preset_combo.add_attribute (*renderer, "text", 0);
254 }
255
256 typedef int (*error_handler_t)( Display *, XErrorEvent *);
257 static Display *the_gtk_display;
258 static error_handler_t vstfx_error_handler;
259 static error_handler_t gtk_error_handler;
260
261 static int 
262 gtk_xerror_handler( Display *disp, XErrorEvent *ev )
263 {
264         std::cerr << "** ERROR ** LXVSTPluginUI : Trapped an X Window System Error" << std::endl;
265         
266         return 0;
267 }
268
269 void
270 gui_init (int *argc, char **argv[])
271 {
272         vstfx_error_handler = XSetErrorHandler (NULL);
273         gtk_init (argc, argv);
274         the_gtk_display = gdk_x11_display_get_xdisplay (gdk_display_get_default());
275         gtk_error_handler = XSetErrorHandler( gtk_xerror_handler );
276 }
277