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.

365 lines
10KB

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