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.

349 lines
9.6KB

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