VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / bridges-ui / CarlaBridgeToolkitGtk.cpp
blobea4b95010fc07a4c3fa849df4c7372d2cf201383
1 /*
2 * Carla Bridge UI
3 * Copyright (C) 2011-2021 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
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.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaBridgeFormat.hpp"
19 #include "CarlaBridgeToolkit.hpp"
20 #include "CarlaLibUtils.hpp"
22 #ifdef HAVE_X11
23 # include <X11/Xlib.h>
24 #endif
26 struct GtkHandle;
28 enum GtkWidgetType {
29 GTK_WINDOW_TOPLEVEL
32 typedef ulong (*gsym_signal_connect_data)(void* instance,
33 const char* detailed_signal,
34 void (*c_handler)(GtkHandle*, void* data),
35 void* data,
36 void* destroy_data,
37 int connect_flags);
38 typedef uint (*gsym_timeout_add)(uint interval, int (*function)(void* user_data), void* data);
39 typedef void (*gtksym_init)(int* argc, char*** argv);
40 typedef void (*gtksym_main)(void);
41 typedef uint (*gtksym_main_level)(void);
42 typedef void (*gtksym_main_quit)(void);
43 typedef void (*gtksym_container_add)(GtkHandle* container, GtkHandle* widget);
44 typedef void (*gtksym_widget_destroy)(GtkHandle* widget);
45 typedef void (*gtksym_widget_hide)(GtkHandle* widget);
46 typedef void (*gtksym_widget_show_all)(GtkHandle* widget);
47 typedef GtkHandle* (*gtksym_window_new)(GtkWidgetType type);
48 typedef void (*gtksym_window_get_position)(GtkHandle* window, int* root_x, int* root_y);
49 typedef void (*gtksym_window_get_size)(GtkHandle* window, int* width, int* height);
50 typedef void (*gtksym_window_resize)(GtkHandle* window, int width, int height);
51 typedef void (*gtksym_window_set_resizable)(GtkHandle* window, int resizable);
52 typedef void (*gtksym_window_set_title)(GtkHandle* window, const char* title);
53 #ifdef HAVE_X11
54 typedef GtkHandle* (*gtksym_widget_get_window)(GtkHandle* widget);
55 # ifdef BRIDGE_GTK3
56 typedef GtkHandle* (*gdksym_window_get_display)(GtkHandle* window);
57 typedef Display* (*gdksym_x11_display_get_xdisplay)(GtkHandle* display);
58 typedef Window (*gdksym_x11_window_get_xid)(GtkHandle* window);
59 # else
60 typedef Display* (*gdksym_x11_drawable_get_xdisplay)(GtkHandle* drawable);
61 typedef XID (*gdksym_x11_drawable_get_xid)(GtkHandle* drawable);
62 # endif
63 #endif
65 CARLA_BRIDGE_UI_START_NAMESPACE
67 // -------------------------------------------------------------------------
69 struct GtkLoader {
70 lib_t lib;
71 #ifdef CARLA_OS_WIN
72 lib_t glib;
73 lib_t golib;
74 #endif
75 gsym_timeout_add timeout_add;
76 gsym_signal_connect_data signal_connect_data;
77 gtksym_init init;
78 gtksym_main main;
79 gtksym_main_level main_level;
80 gtksym_main_quit main_quit;
81 gtksym_container_add container_add;
82 gtksym_widget_destroy widget_destroy;
83 gtksym_widget_hide widget_hide;
84 gtksym_widget_show_all widget_show_all;
85 gtksym_window_new window_new;
86 gtksym_window_get_position window_get_position;
87 gtksym_window_get_size window_get_size;
88 gtksym_window_resize window_resize;
89 gtksym_window_set_resizable window_set_resizable;
90 gtksym_window_set_title window_set_title;
91 bool ok;
93 #ifdef HAVE_X11
94 gtksym_widget_get_window widget_get_window;
95 # ifdef BRIDGE_GTK3
96 gdksym_window_get_display window_get_display;
97 gdksym_x11_display_get_xdisplay x11_display_get_xdisplay;
98 gdksym_x11_window_get_xid x11_window_get_xid;
99 # else
100 gdksym_x11_drawable_get_xdisplay x11_drawable_get_xdisplay;
101 gdksym_x11_drawable_get_xid x11_drawable_get_xid;
102 # endif
103 #endif
105 GtkLoader()
106 : lib(nullptr),
107 #ifdef CARLA_OS_WIN
108 glib(nullptr),
109 golib(nullptr),
110 #endif
111 timeout_add(nullptr),
112 signal_connect_data(nullptr),
113 init(nullptr),
114 main(nullptr),
115 main_level(nullptr),
116 main_quit(nullptr),
117 container_add(nullptr),
118 widget_destroy(nullptr),
119 widget_hide(nullptr),
120 widget_show_all(nullptr),
121 window_new(nullptr),
122 window_get_position(nullptr),
123 window_get_size(nullptr),
124 window_resize(nullptr),
125 window_set_resizable(nullptr),
126 window_set_title(nullptr),
127 ok(false)
128 #ifdef HAVE_X11
129 , widget_get_window(nullptr),
130 # ifdef BRIDGE_GTK3
131 window_get_display(nullptr),
132 x11_display_get_xdisplay(nullptr),
133 x11_window_get_xid(nullptr)
134 # else
135 x11_drawable_get_xdisplay(nullptr),
136 x11_drawable_get_xid(nullptr)
137 # endif
138 #endif
140 const char* filename;
141 const char* const filenames[] = {
142 #ifdef BRIDGE_GTK3
143 # if defined(CARLA_OS_MAC)
144 "libgtk-3.0.dylib",
145 # elif defined(CARLA_OS_WIN)
146 "libgtk-3-0.dll",
147 # else
148 "libgtk-3.so.0",
149 # endif
150 #else
151 # if defined(CARLA_OS_MAC)
152 "libgtk-quartz-2.0.dylib",
153 "libgtk-x11-2.0.dylib",
154 "/opt/homebrew/opt/gtk+/lib/libgtk-quartz-2.0.0.dylib",
155 "/opt/local/lib/libgtk-quartz-2.0.dylib",
156 "/opt/local/lib/libgtk-x11-2.0.dylib",
157 # elif defined(CARLA_OS_WIN)
158 "libgtk-win32-2.0-0.dll",
159 # ifdef CARLA_OS_WIN64
160 "C:\\msys64\\mingw64\\bin\\libgtk-win32-2.0-0.dll",
161 # else
162 "C:\\msys64\\mingw32\\bin\\libgtk-win32-2.0-0.dll",
163 # endif
164 # else
165 "libgtk-x11-2.0.so.0",
166 # endif
167 #endif
170 for (size_t i=0; i<sizeof(filenames)/sizeof(filenames[0]); ++i)
172 filename = filenames[i];
173 if ((lib = lib_open(filename, true)) != nullptr)
174 break;
177 if (lib == nullptr)
179 fprintf(stderr, "Failed to load Gtk, reason:\n%s\n", lib_error(filename));
180 return;
182 else
184 fprintf(stdout, "%s loaded successfully!\n", filename);
187 #ifdef CARLA_OS_WIN
188 const char* gfilename;
189 const char* const gfilenames[] = {
190 "libglib-2.0-0.dll",
191 # ifdef CARLA_OS_WIN64
192 "C:\\msys64\\mingw64\\bin\\libglib-2.0-0.dll",
193 # else
194 "C:\\msys64\\mingw32\\bin\\libglib-2.0-0.dll",
195 # endif
198 for (size_t i=0; i<sizeof(gfilenames)/sizeof(gfilenames[0]); ++i)
200 gfilename = gfilenames[i];
201 if ((glib = lib_open(gfilename, true)) != nullptr)
202 break;
205 if (glib == nullptr)
207 fprintf(stderr, "Failed to load glib, reason:\n%s\n", lib_error(gfilename));
208 return;
210 else
212 fprintf(stdout, "%s loaded successfully!\n", gfilename);
215 const char* gofilename;
216 const char* const gofilenames[] = {
217 "libgobject-2.0-0.dll",
218 # ifdef CARLA_OS_WIN64
219 "C:\\msys64\\mingw64\\bin\\libgobject-2.0-0.dll",
220 # else
221 "C:\\msys64\\mingw32\\bin\\libgobject-2.0-0.dll",
222 # endif
225 for (size_t i=0; i<sizeof(gofilenames)/sizeof(gofilenames[0]); ++i)
227 gofilename = gofilenames[i];
228 if ((golib = lib_open(gofilename, true)) != nullptr)
229 break;
232 if (golib == nullptr)
234 fprintf(stderr, "Failed to load gobject, reason:\n%s\n", lib_error(gofilename));
235 return;
237 else
239 fprintf(stdout, "%s loaded successfully!\n", gofilename);
242 #define G_LIB_SYMBOL(NAME) \
243 NAME = lib_symbol<gsym_##NAME>(glib, "g_" #NAME); \
244 CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
246 #define GO_LIB_SYMBOL(NAME) \
247 NAME = lib_symbol<gsym_##NAME>(golib, "g_" #NAME); \
248 CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
249 #else
250 #define G_LIB_SYMBOL(NAME) \
251 NAME = lib_symbol<gsym_##NAME>(lib, "g_" #NAME); \
252 CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
254 #define GO_LIB_SYMBOL G_LIB_SYMBOL
255 #endif
257 #define GTK_LIB_SYMBOL(NAME) \
258 NAME = lib_symbol<gtksym_##NAME>(lib, "gtk_" #NAME); \
259 CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
261 #define GDK_LIB_SYMBOL(NAME) \
262 NAME = lib_symbol<gdksym_##NAME>(lib, "gdk_" #NAME); \
263 CARLA_SAFE_ASSERT(NAME != nullptr);
265 G_LIB_SYMBOL(timeout_add)
266 GO_LIB_SYMBOL(signal_connect_data)
268 GTK_LIB_SYMBOL(init)
269 GTK_LIB_SYMBOL(main)
270 GTK_LIB_SYMBOL(main_level)
271 GTK_LIB_SYMBOL(main_quit)
272 GTK_LIB_SYMBOL(container_add)
273 GTK_LIB_SYMBOL(widget_destroy)
274 GTK_LIB_SYMBOL(widget_hide)
275 GTK_LIB_SYMBOL(widget_show_all)
276 GTK_LIB_SYMBOL(window_new)
277 GTK_LIB_SYMBOL(window_get_position)
278 GTK_LIB_SYMBOL(window_get_size)
279 GTK_LIB_SYMBOL(window_resize)
280 GTK_LIB_SYMBOL(window_set_resizable)
281 GTK_LIB_SYMBOL(window_set_title)
283 ok = true;
285 #ifdef HAVE_X11
286 GTK_LIB_SYMBOL(widget_get_window)
287 # ifdef BRIDGE_GTK3
288 GDK_LIB_SYMBOL(window_get_display)
289 GDK_LIB_SYMBOL(x11_display_get_xdisplay)
290 GDK_LIB_SYMBOL(x11_window_get_xid)
291 # else
292 GDK_LIB_SYMBOL(x11_drawable_get_xdisplay)
293 GDK_LIB_SYMBOL(x11_drawable_get_xid)
294 # endif
295 #endif
297 #undef G_LIB_SYMBOL
298 #undef GO_LIB_SYMBOL
299 #undef GDK_LIB_SYMBOL
300 #undef GTK_LIB_SYMBOL
303 ~GtkLoader()
305 if (lib != nullptr)
306 lib_close(lib);
307 #ifdef CARLA_OS_WIN
308 if (golib != nullptr)
309 lib_close(golib);
310 if (glib != nullptr)
311 lib_close(glib);
312 #endif
315 void main_quit_if_needed()
317 if (main_level() != 0)
318 main_quit();
321 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GtkLoader)
324 // -------------------------------------------------------------------------
326 static const bool gHideShowTesting = std::getenv("CARLA_UI_TESTING") != nullptr;
328 // -------------------------------------------------------------------------
330 class CarlaBridgeToolkitGtk : public CarlaBridgeToolkit
332 public:
333 CarlaBridgeToolkitGtk(CarlaBridgeFormat* const format)
334 : CarlaBridgeToolkit(format),
335 gtk(),
336 fNeedsShow(false),
337 fWindow(nullptr),
338 fLastX(0),
339 fLastY(0),
340 fLastWidth(0),
341 fLastHeight(0)
343 carla_debug("CarlaBridgeToolkitGtk::CarlaBridgeToolkitGtk(%p)", format);
346 ~CarlaBridgeToolkitGtk() override
348 CARLA_SAFE_ASSERT(fWindow == nullptr);
349 carla_debug("CarlaBridgeToolkitGtk::~CarlaBridgeToolkitGtk()");
352 bool init(const int /*argc*/, const char** /*argv[]*/) override
354 CARLA_SAFE_ASSERT_RETURN(fWindow == nullptr, false);
355 carla_debug("CarlaBridgeToolkitGtk::init()");
357 if (! gtk.ok)
358 return false;
360 static int gargc = 0;
361 static char** gargv = nullptr;
362 gtk.init(&gargc, &gargv);
364 fWindow = gtk.window_new(GTK_WINDOW_TOPLEVEL);
365 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr, false);
367 gtk.window_resize(fWindow, 30, 30);
368 gtk.widget_hide(fWindow);
370 return true;
373 void exec(const bool showUI) override
375 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
376 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
377 carla_debug("CarlaBridgeToolkitGtk::exec(%s)", bool2str(showUI));
379 const CarlaBridgeFormat::Options& options(fPlugin->getOptions());
381 GtkHandle* const widget((GtkHandle*)fPlugin->getWidget());
382 CARLA_SAFE_ASSERT_RETURN(widget != nullptr,);
384 gtk.container_add(fWindow, widget);
385 gtk.window_set_resizable(fWindow, options.isResizable);
386 gtk.window_set_title(fWindow, options.windowTitle.buffer());
388 if (showUI || fNeedsShow)
390 show();
391 fNeedsShow = false;
394 gtk.timeout_add(30, gtk_ui_timeout, this);
395 gtk.signal_connect_data(fWindow, "destroy", gtk_ui_destroy, this, nullptr, 0);
396 gtk.signal_connect_data(fWindow, "realize", gtk_ui_realize, this, nullptr, 0);
398 // First idle
399 handleTimeout();
401 // Main loop
402 gtk.main();
405 void quit() override
407 carla_debug("CarlaBridgeToolkitGtk::quit()");
409 if (fWindow != nullptr)
411 gtk.widget_destroy(fWindow);
412 fWindow = nullptr;
414 gtk.main_quit_if_needed();
418 void show() override
420 carla_debug("CarlaBridgeToolkitGtk::show()");
422 fNeedsShow = true;
424 if (fWindow != nullptr)
425 gtk.widget_show_all(fWindow);
428 void focus() override
430 carla_debug("CarlaBridgeToolkitGtk::focus()");
433 void hide() override
435 carla_debug("CarlaBridgeToolkitGtk::hide()");
437 fNeedsShow = false;
439 if (fWindow != nullptr)
440 gtk.widget_hide(fWindow);
443 void setChildWindow(void* const) override {}
445 void setSize(const uint width, const uint height) override
447 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
448 carla_debug("CarlaBridgeToolkitGtk::resize(%i, %i)", width, height);
450 gtk.window_resize(fWindow, static_cast<int>(width), static_cast<int>(height));
453 void setTitle(const char* const title) override
455 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
456 carla_debug("CarlaBridgeToolkitGtk::setTitle(\"%s\")", title);
458 gtk.window_set_title(fWindow, title);
461 // ---------------------------------------------------------------------
463 protected:
464 GtkLoader gtk;
465 bool fNeedsShow;
466 GtkHandle* fWindow;
468 int fLastX;
469 int fLastY;
470 int fLastWidth;
471 int fLastHeight;
473 void handleDestroy()
475 carla_debug("CarlaBridgeToolkitGtk::handleDestroy()");
477 fWindow = nullptr;
478 gtk.main_quit_if_needed();
481 void handleRealize()
483 carla_debug("CarlaBridgeToolkitGtk::handleRealize()");
485 #ifdef HAVE_X11
486 const CarlaBridgeFormat::Options& options(fPlugin->getOptions());
488 if (options.transientWindowId != 0)
489 setTransient(options.transientWindowId);
490 #endif
493 int handleTimeout()
495 if (fWindow != nullptr)
497 gtk.window_get_position(fWindow, &fLastX, &fLastY);
498 gtk.window_get_size(fWindow, &fLastWidth, &fLastHeight);
501 if (fPlugin->isPipeRunning())
502 fPlugin->idlePipe();
504 fPlugin->idleUI();
506 if (gHideShowTesting)
508 static int counter = 0;
509 ++counter;
511 if (counter == 100)
513 hide();
515 else if (counter == 200)
517 show();
518 counter = 0;
522 return 1;
525 #ifdef HAVE_X11
526 void setTransient(const uintptr_t winId)
528 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
529 carla_debug("CarlaBridgeToolkitGtk::setTransient(0x" P_UINTPTR ")", winId);
531 if (gtk.widget_get_window == nullptr)
532 return;
533 # ifdef BRIDGE_GTK3
534 if (gtk.window_get_display == nullptr)
535 return;
536 if (gtk.x11_display_get_xdisplay == nullptr)
537 return;
538 if (gtk.x11_window_get_xid == nullptr)
539 return;
540 # else
541 if (gtk.x11_drawable_get_xdisplay == nullptr)
542 return;
543 if (gtk.x11_drawable_get_xid == nullptr)
544 return;
545 # endif
547 GtkHandle* const gdkWindow = gtk.widget_get_window(fWindow);
548 CARLA_SAFE_ASSERT_RETURN(gdkWindow != nullptr,);
550 # ifdef BRIDGE_GTK3
551 GtkHandle* const gdkDisplay = gtk.window_get_display(gdkWindow);
552 CARLA_SAFE_ASSERT_RETURN(gdkDisplay != nullptr,);
554 ::Display* const display = gtk.x11_display_get_xdisplay(gdkDisplay);
555 CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
557 const ::XID xid = gtk.x11_window_get_xid(gdkWindow);
558 CARLA_SAFE_ASSERT_RETURN(xid != 0,);
559 # else
560 ::Display* const display = gtk.x11_drawable_get_xdisplay((GtkHandle*)gdkWindow);
561 CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
563 const ::XID xid = gtk.x11_drawable_get_xid((GtkHandle*)gdkWindow);
564 CARLA_SAFE_ASSERT_RETURN(xid != 0,);
565 # endif
567 XSetTransientForHint(display, xid, static_cast< ::Window>(winId));
569 #endif
571 // ---------------------------------------------------------------------
573 private:
574 static void gtk_ui_destroy(GtkHandle*, void* data)
576 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
578 ((CarlaBridgeToolkitGtk*)data)->handleDestroy();
581 static void gtk_ui_realize(GtkHandle*, void* data)
583 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
585 ((CarlaBridgeToolkitGtk*)data)->handleRealize();
588 static int gtk_ui_timeout(void* data)
590 CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
592 return ((CarlaBridgeToolkitGtk*)data)->handleTimeout();
595 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgeToolkitGtk)
598 // -------------------------------------------------------------------------
600 CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeFormat* const format)
602 return new CarlaBridgeToolkitGtk(format);
605 // -------------------------------------------------------------------------
607 CARLA_BRIDGE_UI_END_NAMESPACE