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"
23 # include <X11/Xlib.h>
32 typedef ulong (*gsym_signal_connect_data
)(void* instance
,
33 const char* detailed_signal
,
34 void (*c_handler
)(GtkHandle
*, void* data
),
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
);
54 typedef GtkHandle
* (*gtksym_widget_get_window
)(GtkHandle
* widget
);
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
);
60 typedef Display
* (*gdksym_x11_drawable_get_xdisplay
)(GtkHandle
* drawable
);
61 typedef XID (*gdksym_x11_drawable_get_xid
)(GtkHandle
* drawable
);
65 CARLA_BRIDGE_UI_START_NAMESPACE
67 // -------------------------------------------------------------------------
75 gsym_timeout_add timeout_add
;
76 gsym_signal_connect_data signal_connect_data
;
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
;
94 gtksym_widget_get_window widget_get_window
;
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
;
100 gdksym_x11_drawable_get_xdisplay x11_drawable_get_xdisplay
;
101 gdksym_x11_drawable_get_xid x11_drawable_get_xid
;
111 timeout_add(nullptr),
112 signal_connect_data(nullptr),
117 container_add(nullptr),
118 widget_destroy(nullptr),
119 widget_hide(nullptr),
120 widget_show_all(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),
129 , widget_get_window(nullptr),
131 window_get_display(nullptr),
132 x11_display_get_xdisplay(nullptr),
133 x11_window_get_xid(nullptr)
135 x11_drawable_get_xdisplay(nullptr),
136 x11_drawable_get_xid(nullptr)
140 const char* filename
;
141 const char* const filenames
[] = {
143 # if defined(CARLA_OS_MAC)
145 # elif defined(CARLA_OS_WIN)
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",
162 "C:\\msys64\\mingw32\\bin\\libgtk-win32-2.0-0.dll",
165 "libgtk-x11-2.0.so.0",
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)
179 fprintf(stderr
, "Failed to load Gtk, reason:\n%s\n", lib_error(filename
));
184 fprintf(stdout
, "%s loaded successfully!\n", filename
);
188 const char* gfilename
;
189 const char* const gfilenames
[] = {
191 # ifdef CARLA_OS_WIN64
192 "C:\\msys64\\mingw64\\bin\\libglib-2.0-0.dll",
194 "C:\\msys64\\mingw32\\bin\\libglib-2.0-0.dll",
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)
207 fprintf(stderr
, "Failed to load glib, reason:\n%s\n", lib_error(gfilename
));
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",
221 "C:\\msys64\\mingw32\\bin\\libgobject-2.0-0.dll",
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)
232 if (golib
== nullptr)
234 fprintf(stderr
, "Failed to load gobject, reason:\n%s\n", lib_error(gofilename
));
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,);
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
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
)
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
)
286 GTK_LIB_SYMBOL(widget_get_window
)
288 GDK_LIB_SYMBOL(window_get_display
)
289 GDK_LIB_SYMBOL(x11_display_get_xdisplay
)
290 GDK_LIB_SYMBOL(x11_window_get_xid
)
292 GDK_LIB_SYMBOL(x11_drawable_get_xdisplay
)
293 GDK_LIB_SYMBOL(x11_drawable_get_xid
)
299 #undef GDK_LIB_SYMBOL
300 #undef GTK_LIB_SYMBOL
308 if (golib
!= nullptr)
315 void main_quit_if_needed()
317 if (main_level() != 0)
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
333 CarlaBridgeToolkitGtk(CarlaBridgeFormat
* const format
)
334 : CarlaBridgeToolkit(format
),
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()");
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
);
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
)
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);
407 carla_debug("CarlaBridgeToolkitGtk::quit()");
409 if (fWindow
!= nullptr)
411 gtk
.widget_destroy(fWindow
);
414 gtk
.main_quit_if_needed();
420 carla_debug("CarlaBridgeToolkitGtk::show()");
424 if (fWindow
!= nullptr)
425 gtk
.widget_show_all(fWindow
);
428 void focus() override
430 carla_debug("CarlaBridgeToolkitGtk::focus()");
435 carla_debug("CarlaBridgeToolkitGtk::hide()");
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 // ---------------------------------------------------------------------
475 carla_debug("CarlaBridgeToolkitGtk::handleDestroy()");
478 gtk
.main_quit_if_needed();
483 carla_debug("CarlaBridgeToolkitGtk::handleRealize()");
486 const CarlaBridgeFormat::Options
& options(fPlugin
->getOptions());
488 if (options
.transientWindowId
!= 0)
489 setTransient(options
.transientWindowId
);
495 if (fWindow
!= nullptr)
497 gtk
.window_get_position(fWindow
, &fLastX
, &fLastY
);
498 gtk
.window_get_size(fWindow
, &fLastWidth
, &fLastHeight
);
501 if (fPlugin
->isPipeRunning())
506 if (gHideShowTesting
)
508 static int counter
= 0;
515 else if (counter
== 200)
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)
534 if (gtk
.window_get_display
== nullptr)
536 if (gtk
.x11_display_get_xdisplay
== nullptr)
538 if (gtk
.x11_window_get_xid
== nullptr)
541 if (gtk
.x11_drawable_get_xdisplay
== nullptr)
543 if (gtk
.x11_drawable_get_xid
== nullptr)
547 GtkHandle
* const gdkWindow
= gtk
.widget_get_window(fWindow
);
548 CARLA_SAFE_ASSERT_RETURN(gdkWindow
!= nullptr,);
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,);
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,);
567 XSetTransientForHint(display
, xid
, static_cast< ::Window
>(winId
));
571 // ---------------------------------------------------------------------
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