25ee0f2aea5eefecdbed3e7529c63c836bee3d6a
[ardour.git] / gtk2_ardour / lxvst_plugin_ui.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 #include "gtkmm2ext/gui_thread.h"
22 #include "ardour/lxvst_plugin.h"
23 #include "ardour/linux_vst_support.h"
24 #include "lxvst_plugin_ui.h"
25
26 #include <gdk/gdkx.h> /* must come later than glibmm/object.h */
27
28 #define LXVST_H_FIDDLE 40
29
30 using namespace std;
31 using namespace Gtk;
32 using namespace ARDOUR;
33 using namespace PBD;
34
35 LXVSTPluginUI::LXVSTPluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr<VSTPlugin> lxvp)
36         : VSTPluginUI (pi, lxvp)
37 {
38         vstfx_run_editor (_vst->state ());
39 }
40
41 LXVSTPluginUI::~LXVSTPluginUI ()
42 {
43         _resize_connection.disconnect();
44
45         // plugin destructor destroys the custom GUI, via the vstfx engine,
46         // and then our PluginUIWindow does the rest
47 }
48
49 void
50 LXVSTPluginUI::resize_callback ()
51 {
52         void* gtk_parent_window = _vst->state()->gtk_window_parent;
53
54         if (gtk_parent_window) {
55                 int width  = _vst->state()->width;
56                 int height = _vst->state()->height;
57 #ifndef NDEBUG
58                 printf ("LXVSTPluginUI::resize_callback %d x %d\n", width, height);
59 #endif
60                 _socket.set_size_request(
61                                 width  + _vst->state()->hoffset,
62                                 height + _vst->state()->voffset);
63
64                 ((Gtk::Window*) gtk_parent_window)->resize (width, height + LXVST_H_FIDDLE);
65                 if (_vst->state()->linux_plugin_ui_window) {
66                 }
67         }
68 }
69
70 int
71 LXVSTPluginUI::get_preferred_height ()
72 {
73         /* XXX: FIXME */
74
75         /* We have to return the required height of the plugin UI window +  a fiddle factor
76            because we can't know how big the preset menu bar is until the window is realised
77            and we can't realise it until we have told it how big we would like it to be
78            which we can't do until it is realised etc
79         */
80
81         // May not be 40 for all screen res etc
82         return VSTPluginUI::get_preferred_height () + LXVST_H_FIDDLE;
83 }
84
85 int
86 LXVSTPluginUI::package (Gtk::Window& win)
87 {
88         VSTPluginUI::package (win);
89         _vst->state()->gtk_window_parent = (void*) (&win);
90
91         /* Map the UI start and stop updating events to 'Map' events on the Window */
92
93         _vst->VSTSizeWindow.connect (_resize_connection, invalidator (*this), boost::bind (&LXVSTPluginUI::resize_callback, this), gui_context());
94         return 0;
95 }
96
97 void
98 LXVSTPluginUI::forward_key_event (GdkEventKey* gdk_key)
99 {
100         if (!_vst->state()->gtk_window_parent) {
101                 return;
102         }
103
104         Glib::RefPtr<Gdk::Window> gdk_window = ((Gtk::Window*) _vst->state()->gtk_window_parent)->get_window();
105
106         if (!gdk_window) {
107                 return;
108         }
109
110         XEvent xev;
111         int mask;
112
113         switch (gdk_key->type) {
114         case GDK_KEY_PRESS:
115                 xev.xany.type = KeyPress;
116                 mask = KeyPressMask;
117                 break;
118         case GDK_KEY_RELEASE:
119                 xev.xany.type = KeyPress;
120                 mask = KeyReleaseMask;
121                 break;
122         default:
123                 return;
124         }
125
126         /* XXX relies on GDK using X11 definitions for these fields */
127
128         xev.xkey.state = gdk_key->state;
129         xev.xkey.keycode = gdk_key->hardware_keycode; /* see gdk/x11/gdkevents-x11.c:translate_key_event() */
130
131         xev.xkey.x = 0;
132         xev.xkey.y = 0;
133         xev.xkey.x_root = 0;
134         xev.xkey.y_root = 0;
135         xev.xkey.root = gdk_x11_get_default_root_xwindow();
136         xev.xkey.window = _vst->state()->xid;
137         xev.xkey.subwindow = None;
138         xev.xkey.time = gdk_key->time;
139
140         xev.xany.serial = 0; /* we don't have one */
141         xev.xany.send_event = true; /* pretend we are using XSendEvent */
142         xev.xany.display = GDK_WINDOW_XDISPLAY (gdk_window->gobj());
143         xev.xany.window = _vst->state()->xid;
144
145         if (!_vst->state()->eventProc) {
146                 XSendEvent (xev.xany.display, xev.xany.window, TRUE, mask, &xev);
147         } else {
148                 _vst->state()->eventProc (&xev);
149         }
150 }
151
152 int
153 LXVSTPluginUI::get_XID ()
154 {
155         /* Wait for the lock to become free - otherwise
156            the window might be in the process of being
157            created and we get bad Window errors when trying
158            to embed it in the GTK UI
159         */
160
161         pthread_mutex_lock (&(_vst->state()->lock));
162
163         /* The Window may be scheduled for creation
164            but not actually created by the gui_event_loop yet -
165            spin here until it has been activated.  Possible
166            deadlock if the window never gets activated but
167            should not be called here if the window doesn't
168            exist or will never exist
169         */
170
171         while (!(_vst->state()->been_activated)) {
172                 Glib::usleep (1000);
173         }
174
175         int const id = _vst->state()->xid;
176
177         pthread_mutex_unlock (&(_vst->state()->lock));
178
179         /* Finally it might be safe to return the ID -
180            problems will arise if we return either a zero ID
181            and GTK tries to socket it or if we return an ID
182            which hasn't yet become real to the server
183         */
184
185         return id;
186 }
187
188 typedef int (*error_handler_t)( Display *, XErrorEvent *);
189 static Display *the_gtk_display;
190 static error_handler_t vstfx_error_handler;
191 static error_handler_t gtk_error_handler;
192
193 static int
194 gtk_xerror_handler (Display*, XErrorEvent*)
195 {
196         std::cerr << "** ERROR ** LXVSTPluginUI : Trapped an X Window System Error" << std::endl;
197
198         return 0;
199 }
200
201 void
202 gui_init (int* argc, char** argv[])
203 {
204         vstfx_error_handler = XSetErrorHandler (NULL);
205         gtk_init (argc, argv);
206         the_gtk_display = gdk_x11_display_get_xdisplay (gdk_display_get_default());
207         gtk_error_handler = XSetErrorHandler (gtk_xerror_handler);
208 }