Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
7.2KB

  1. /*
  2. * Carla Bridge Toolkit, Gtk version
  3. * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com>
  4. *
  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.
  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. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaBridgeClient.hpp"
  18. #include "CarlaBridgeToolkit.hpp"
  19. #include <gtk/gtk.h>
  20. #ifdef CARLA_OS_LINUX
  21. # include <gdk/gdkx.h>
  22. #endif
  23. CARLA_BRIDGE_START_NAMESPACE
  24. // -------------------------------------------------------------------------
  25. static int gargc = 0;
  26. static char** gargv = nullptr;
  27. // -------------------------------------------------------------------------
  28. class CarlaBridgeToolkitGtk : public CarlaBridgeToolkit
  29. {
  30. public:
  31. CarlaBridgeToolkitGtk(CarlaBridgeClient* const client, const char* const windowTitle)
  32. : CarlaBridgeToolkit(client, windowTitle),
  33. fNeedsShow(false),
  34. fWindow(nullptr),
  35. fLastX(0),
  36. fLastY(0),
  37. fLastWidth(0),
  38. fLastHeight(0),
  39. leakDetector_CarlaBridgeToolkitGtk()
  40. {
  41. carla_debug("CarlaBridgeToolkitGtk::CarlaBridgeToolkitGtk(%p, \"%s\")", client, windowTitle);
  42. }
  43. ~CarlaBridgeToolkitGtk() override
  44. {
  45. CARLA_SAFE_ASSERT(fWindow == nullptr);
  46. carla_debug("CarlaBridgeToolkitGtk::~CarlaBridgeToolkitGtk()");
  47. }
  48. void init() override
  49. {
  50. CARLA_SAFE_ASSERT_RETURN(fWindow == nullptr,);
  51. carla_debug("CarlaBridgeToolkitGtk::init()");
  52. gtk_init(&gargc, &gargv);
  53. fWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  54. gtk_window_resize(GTK_WINDOW(fWindow), 30, 30);
  55. gtk_widget_hide(fWindow);
  56. }
  57. void exec(const bool showUI) override
  58. {
  59. CARLA_SAFE_ASSERT_RETURN(kClient != nullptr,);
  60. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  61. carla_debug("CarlaBridgeToolkitGtk::exec(%s)", bool2str(showUI));
  62. GtkWindow* const gtkWindow(GTK_WINDOW(fWindow));
  63. CARLA_SAFE_ASSERT_RETURN(gtkWindow != nullptr,);
  64. GtkWidget* const widget((GtkWidget*)kClient->getWidget());
  65. gtk_container_add(GTK_CONTAINER(fWindow), widget);
  66. gtk_window_set_resizable(gtkWindow, kClient->isResizable());
  67. gtk_window_set_title(gtkWindow, kWindowTitle);
  68. if (showUI || fNeedsShow)
  69. {
  70. show();
  71. fNeedsShow = false;
  72. }
  73. g_timeout_add(30, gtk_ui_timeout, this);
  74. g_signal_connect(fWindow, "destroy", G_CALLBACK(gtk_ui_destroy), this);
  75. g_signal_connect(fWindow, "realize", G_CALLBACK(gtk_ui_realize), this);
  76. // First idle
  77. handleTimeout();
  78. // Main loop
  79. gtk_main();
  80. }
  81. void quit() override
  82. {
  83. carla_debug("CarlaBridgeToolkitGtk::quit()");
  84. if (fWindow != nullptr)
  85. {
  86. gtk_widget_destroy(fWindow);
  87. fWindow = nullptr;
  88. gtk_main_quit_if_needed();
  89. }
  90. }
  91. void show() override
  92. {
  93. carla_debug("CarlaBridgeToolkitGtk::show()");
  94. fNeedsShow = true;
  95. if (fWindow != nullptr)
  96. gtk_widget_show_all(fWindow);
  97. }
  98. void hide() override
  99. {
  100. carla_debug("CarlaBridgeToolkitGtk::hide()");
  101. fNeedsShow = false;
  102. if (fWindow != nullptr)
  103. gtk_widget_hide(fWindow);
  104. }
  105. void resize(int width, int height) override
  106. {
  107. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  108. carla_debug("CarlaBridgeToolkitGtk::resize(%i, %i)", width, height);
  109. gtk_window_resize(GTK_WINDOW(fWindow), width, height);
  110. }
  111. // ---------------------------------------------------------------------
  112. protected:
  113. bool fNeedsShow;
  114. GtkWidget* fWindow;
  115. gint fLastX;
  116. gint fLastY;
  117. gint fLastWidth;
  118. gint fLastHeight;
  119. void handleDestroy()
  120. {
  121. carla_debug("CarlaBridgeToolkitGtk::handleDestroy()");
  122. fWindow = nullptr;
  123. }
  124. void handleRealize()
  125. {
  126. carla_debug("CarlaBridgeToolkitGtk::handleRealize()");
  127. #ifdef CARLA_OS_LINUX
  128. if (const char* const winIdStr = std::getenv("ENGINE_OPTION_FRONTEND_WIN_ID"))
  129. if (const long long winId = std::strtoll(winIdStr, nullptr, 16))
  130. setTransient(winId);
  131. #endif
  132. }
  133. gboolean handleTimeout()
  134. {
  135. if (fWindow != nullptr)
  136. {
  137. gtk_window_get_position(GTK_WINDOW(fWindow), &fLastX, &fLastY);
  138. gtk_window_get_size(GTK_WINDOW(fWindow), &fLastWidth, &fLastHeight);
  139. }
  140. kClient->uiIdle();
  141. if (! kClient->oscIdle())
  142. {
  143. gtk_main_quit_if_needed();
  144. return false;
  145. }
  146. return true;
  147. }
  148. #ifdef CARLA_OS_LINUX
  149. void setTransient(const uintptr_t winId)
  150. {
  151. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  152. carla_debug("CarlaBridgeToolkitGtk::setTransient(0x" P_UINTPTR ")", winId);
  153. GdkWindow* const gdkWindow(gtk_widget_get_window(fWindow));
  154. CARLA_SAFE_ASSERT_RETURN(gdkWindow != nullptr,);
  155. # ifdef BRIDGE_GTK3
  156. GdkDisplay* const gdkDisplay(gdk_window_get_display(gdkWindow));
  157. CARLA_SAFE_ASSERT_RETURN(gdkDisplay != nullptr,);
  158. ::Display* const display(gdk_x11_display_get_xdisplay(gdkDisplay));
  159. CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
  160. const ::XID xid(gdk_x11_window_get_xid(gdkWindow));
  161. CARLA_SAFE_ASSERT_RETURN(xid != 0,);
  162. # else
  163. ::Display* const display(gdk_x11_drawable_get_xdisplay(gdkWindow));
  164. CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
  165. const ::XID xid(gdk_x11_drawable_get_xid(gdkWindow));
  166. CARLA_SAFE_ASSERT_RETURN(xid != 0,);
  167. # endif
  168. XSetTransientForHint(display, xid, static_cast<::Window>(winId));
  169. }
  170. #endif
  171. // ---------------------------------------------------------------------
  172. private:
  173. static void gtk_main_quit_if_needed()
  174. {
  175. if (gtk_main_level() != 0)
  176. gtk_main_quit();
  177. }
  178. static void gtk_ui_destroy(GtkWidget*, gpointer data)
  179. {
  180. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  181. ((CarlaBridgeToolkitGtk*)data)->handleDestroy();
  182. gtk_main_quit_if_needed();
  183. }
  184. static void gtk_ui_realize(GtkWidget*, gpointer data)
  185. {
  186. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  187. ((CarlaBridgeToolkitGtk*)data)->handleRealize();
  188. }
  189. static gboolean gtk_ui_timeout(gpointer data)
  190. {
  191. CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
  192. return ((CarlaBridgeToolkitGtk*)data)->handleTimeout();
  193. }
  194. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgeToolkitGtk)
  195. };
  196. // -------------------------------------------------------------------------
  197. CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const windowTitle)
  198. {
  199. return new CarlaBridgeToolkitGtk(client, windowTitle);
  200. }
  201. // -------------------------------------------------------------------------
  202. CARLA_BRIDGE_END_NAMESPACE