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.

364 lines
10KB

  1. /*
  2. * DISTRHO Ildaeil Plugin
  3. * Copyright (C) 2021-2024 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 LICENSE file.
  16. */
  17. #include "src/DistrhoDefines.h"
  18. #if defined(DISTRHO_OS_HAIKU)
  19. #elif defined(DISTRHO_OS_MAC)
  20. # import <Cocoa/Cocoa.h>
  21. #elif defined(DISTRHO_OS_WASM)
  22. #elif defined(DISTRHO_OS_WINDOWS)
  23. # define WIN32_LEAN_AND_MEAN
  24. # include <windows.h>
  25. #else
  26. # define ILDAEIL_X11
  27. # include <X11/Xlib.h>
  28. # include <X11/Xutil.h>
  29. # include <pthread.h>
  30. #endif
  31. #include "PluginHostWindow.hpp"
  32. START_NAMESPACE_DGL
  33. #if defined(DISTRHO_OS_HAIKU)
  34. #elif defined(DISTRHO_OS_MAC)
  35. #elif defined(DISTRHO_OS_WASM)
  36. #elif defined(DISTRHO_OS_WINDOWS)
  37. #else
  38. static pthread_mutex_t gErrorMutex = PTHREAD_MUTEX_INITIALIZER;
  39. static bool gErrorTriggered = false;
  40. static int ildaeilErrorHandler(Display*, XErrorEvent*)
  41. {
  42. gErrorTriggered = true;
  43. return 0;
  44. }
  45. #endif
  46. struct PluginHostWindow::PrivateData
  47. {
  48. void* const windowHandle;
  49. Callbacks* const pluginWindowCallbacks;
  50. #if defined(DISTRHO_OS_HAIKU)
  51. #elif defined(DISTRHO_OS_MAC)
  52. NSView* pluginView;
  53. #elif defined(DISTRHO_OS_WASM)
  54. #elif defined(DISTRHO_OS_WINDOWS)
  55. ::HWND pluginWindow;
  56. #else
  57. ::Display* display;
  58. ::Window pluginWindow;
  59. #endif
  60. uint xOffset, yOffset;
  61. bool lookingForChildren;
  62. PrivateData(void* const wh, Callbacks* const cbs)
  63. : windowHandle(wh),
  64. pluginWindowCallbacks(cbs),
  65. #if defined(DISTRHO_OS_HAIKU)
  66. #elif defined(DISTRHO_OS_MAC)
  67. pluginView(nullptr),
  68. #elif defined(DISTRHO_OS_WASM)
  69. #elif defined(DISTRHO_OS_WINDOWS)
  70. pluginWindow(nullptr),
  71. #else
  72. display(nullptr),
  73. pluginWindow(0),
  74. #endif
  75. xOffset(0),
  76. yOffset(0),
  77. lookingForChildren(false)
  78. {
  79. #if defined(DISTRHO_OS_HAIKU)
  80. #elif defined(DISTRHO_OS_MAC)
  81. #elif defined(DISTRHO_OS_WASM)
  82. #elif defined(DISTRHO_OS_WINDOWS)
  83. #else
  84. display = XOpenDisplay(nullptr);
  85. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr,)
  86. #endif
  87. }
  88. ~PrivateData()
  89. {
  90. #if defined(DISTRHO_OS_HAIKU)
  91. #elif defined(DISTRHO_OS_MAC)
  92. #elif defined(DISTRHO_OS_WASM)
  93. #elif defined(DISTRHO_OS_WINDOWS)
  94. #else
  95. if (display != nullptr)
  96. XCloseDisplay(display);
  97. #endif
  98. }
  99. void restart()
  100. {
  101. lookingForChildren = true;
  102. #if defined(DISTRHO_OS_HAIKU)
  103. #elif defined(DISTRHO_OS_MAC)
  104. pluginView = nullptr;
  105. #elif defined(DISTRHO_OS_WASM)
  106. #elif defined(DISTRHO_OS_WINDOWS)
  107. pluginWindow = nullptr;
  108. #else
  109. pluginWindow = 0;
  110. #endif
  111. }
  112. bool hide()
  113. {
  114. #if defined(DISTRHO_OS_HAIKU)
  115. #elif defined(DISTRHO_OS_MAC)
  116. if (pluginView != nullptr)
  117. {
  118. [pluginView setHidden:YES];
  119. pluginView = nullptr;
  120. [NSOpenGLContext clearCurrentContext];
  121. return true;
  122. }
  123. #elif defined(DISTRHO_OS_WASM)
  124. #elif defined(DISTRHO_OS_WINDOWS)
  125. if (pluginWindow != nullptr)
  126. {
  127. ShowWindow(pluginWindow, SW_HIDE);
  128. pluginWindow = nullptr;
  129. return true;
  130. }
  131. #else
  132. if (pluginWindow != 0)
  133. {
  134. XUnmapWindow(display, pluginWindow);
  135. XSync(display, True);
  136. pluginWindow = 0;
  137. return true;
  138. }
  139. #endif
  140. return false;
  141. }
  142. void idle()
  143. {
  144. if (lookingForChildren)
  145. {
  146. #if defined(DISTRHO_OS_HAIKU)
  147. #elif defined(DISTRHO_OS_MAC)
  148. if (pluginView == nullptr)
  149. {
  150. bool first = true;
  151. for (NSView* view in [(NSView*)windowHandle subviews])
  152. {
  153. if (first)
  154. {
  155. first = false;
  156. continue;
  157. }
  158. pluginView = view;
  159. break;
  160. }
  161. }
  162. #elif defined(DISTRHO_OS_WASM)
  163. #elif defined(DISTRHO_OS_WINDOWS)
  164. if (pluginWindow == nullptr)
  165. pluginWindow = FindWindowExA((::HWND)windowHandle, nullptr, nullptr, nullptr);
  166. #else
  167. if (display == nullptr)
  168. return;
  169. if (pluginWindow == 0)
  170. {
  171. ::Window rootWindow, parentWindow;
  172. ::Window* childWindows = nullptr;
  173. uint numChildren = 0;
  174. XQueryTree(display, (::Window)windowHandle, &rootWindow, &parentWindow, &childWindows, &numChildren);
  175. if (numChildren > 0 && childWindows != nullptr)
  176. {
  177. // pick last child, needed for NTK based UIs which do not delete/remove previous windows.
  178. //sadly this breaks ildaeil-within-ildaeil recursion.. :(
  179. pluginWindow = childWindows[numChildren - 1];
  180. XFree(childWindows);
  181. }
  182. }
  183. #endif
  184. }
  185. #if defined(DISTRHO_OS_HAIKU)
  186. #elif defined(DISTRHO_OS_MAC)
  187. if (pluginView != nullptr)
  188. {
  189. const double scaleFactor = [[[pluginView window] screen] backingScaleFactor];
  190. const NSSize size = [pluginView frame].size;
  191. const double width = size.width;
  192. const double height = size.height;
  193. if (lookingForChildren)
  194. d_stdout("child window bounds %f %f | offset %u %u", width, height, xOffset, yOffset);
  195. if (width > 1.0 && height > 1.0)
  196. {
  197. lookingForChildren = false;
  198. [pluginView setFrameOrigin:NSMakePoint(xOffset / scaleFactor, yOffset / scaleFactor)];
  199. [pluginView setNeedsDisplay:YES];
  200. pluginWindowCallbacks->pluginWindowResized(width * scaleFactor, height * scaleFactor);
  201. }
  202. }
  203. #elif defined(DISTRHO_OS_WASM)
  204. #elif defined(DISTRHO_OS_WINDOWS)
  205. if (pluginWindow != nullptr)
  206. {
  207. int width = 0;
  208. int height = 0;
  209. RECT rect;
  210. if (GetWindowRect(pluginWindow, &rect))
  211. {
  212. width = rect.right - rect.left;
  213. height = rect.bottom - rect.top;
  214. }
  215. if (lookingForChildren)
  216. d_stdout("child window bounds %i %i | offset %u %u", width, height, xOffset, yOffset);
  217. if (width > 1 && height > 1)
  218. {
  219. lookingForChildren = false;
  220. SetWindowPos(pluginWindow, 0, xOffset, yOffset, 0, 0,
  221. SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  222. pluginWindowCallbacks->pluginWindowResized(width, height);
  223. }
  224. }
  225. #else
  226. for (XEvent event; XPending(display) > 0;)
  227. XNextEvent(display, &event);
  228. if (pluginWindow != 0)
  229. {
  230. int width = 0;
  231. int height = 0;
  232. XWindowAttributes attrs;
  233. memset(&attrs, 0, sizeof(attrs));
  234. pthread_mutex_lock(&gErrorMutex);
  235. const XErrorHandler oldErrorHandler = XSetErrorHandler(ildaeilErrorHandler);
  236. gErrorTriggered = false;
  237. if (XGetWindowAttributes(display, pluginWindow, &attrs) && ! gErrorTriggered)
  238. {
  239. width = attrs.width;
  240. height = attrs.height;
  241. }
  242. XSetErrorHandler(oldErrorHandler);
  243. pthread_mutex_unlock(&gErrorMutex);
  244. if (width == 0 && height == 0)
  245. {
  246. XSizeHints sizeHints;
  247. memset(&sizeHints, 0, sizeof(sizeHints));
  248. if (XGetNormalHints(display, pluginWindow, &sizeHints))
  249. {
  250. if (sizeHints.flags & PSize)
  251. {
  252. width = sizeHints.width;
  253. height = sizeHints.height;
  254. }
  255. else if (sizeHints.flags & PBaseSize)
  256. {
  257. width = sizeHints.base_width;
  258. height = sizeHints.base_height;
  259. }
  260. }
  261. }
  262. if (lookingForChildren)
  263. d_stdout("child window bounds %i %i | offset %u %u", width, height, xOffset, yOffset);
  264. if (width > 1 && height > 1)
  265. {
  266. lookingForChildren = false;
  267. XMoveWindow(display, pluginWindow, xOffset, yOffset);
  268. XSync(display, True);
  269. pluginWindowCallbacks->pluginWindowResized(width, height);
  270. }
  271. }
  272. #endif
  273. }
  274. void setOffset(const uint x, const uint y)
  275. {
  276. xOffset = x;
  277. yOffset = y;
  278. }
  279. void setSize(const uint width, const uint height)
  280. {
  281. #if defined(DISTRHO_OS_HAIKU)
  282. #elif defined(DISTRHO_OS_MAC)
  283. if (pluginView != nullptr)
  284. [pluginView setFrameSize:NSMakeSize(width, height)];
  285. #elif defined(DISTRHO_OS_WASM)
  286. #elif defined(DISTRHO_OS_WINDOWS)
  287. if (pluginWindow != nullptr)
  288. SetWindowPos(pluginWindow, 0, 0, 0, width, height,
  289. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  290. #else
  291. if (pluginWindow != 0)
  292. XResizeWindow(display, pluginWindow, width, height);
  293. #endif
  294. }
  295. };
  296. PluginHostWindow::PluginHostWindow(void* const windowHandle, Callbacks* const cbs)
  297. : pData(new PrivateData(windowHandle, cbs)) {}
  298. PluginHostWindow::~PluginHostWindow()
  299. {
  300. delete pData;
  301. }
  302. void PluginHostWindow::restart()
  303. {
  304. pData->restart();
  305. }
  306. bool PluginHostWindow::hide()
  307. {
  308. return pData->hide();
  309. }
  310. void PluginHostWindow::idle()
  311. {
  312. pData->idle();
  313. }
  314. void PluginHostWindow::setOffset(const uint x, const uint y)
  315. {
  316. pData->setOffset(x, y);
  317. }
  318. void PluginHostWindow::setSize(const uint width, const uint height)
  319. {
  320. pData->setSize(width, height);
  321. }
  322. END_NAMESPACE_DGL