DISTRHO Plugin Framework
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.

325 lines
9.7KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. // needed for IDE
  17. #include "DistrhoPluginInfo.h"
  18. #include "DistrhoUI.hpp"
  19. #if defined(DISTRHO_OS_MAC)
  20. # import <Cocoa/Cocoa.h>
  21. #elif defined(DISTRHO_OS_WINDOWS)
  22. #else
  23. # include <sys/types.h>
  24. # include <X11/Xatom.h>
  25. # include <X11/Xlib.h>
  26. # include <X11/Xutil.h>
  27. # define X11Key_Escape 9
  28. #endif
  29. START_NAMESPACE_DISTRHO
  30. // -----------------------------------------------------------------------------------------------------------
  31. class EmbedExternalExampleUI : public UI
  32. {
  33. #if defined(DISTRHO_OS_MAC)
  34. NSView* fView;
  35. id fWindow;
  36. #elif defined(DISTRHO_OS_WINDOWS)
  37. #else
  38. ::Display* fDisplay;
  39. ::Window fWindow;
  40. #endif
  41. public:
  42. EmbedExternalExampleUI()
  43. : UI(512, 256),
  44. #if defined(DISTRHO_OS_MAC)
  45. fView(nullptr),
  46. fWindow(nil),
  47. #elif defined(DISTRHO_OS_WINDOWS)
  48. #else
  49. fDisplay(nullptr),
  50. fWindow(0),
  51. #endif
  52. fValue(0.0f)
  53. {
  54. #if defined(DISTRHO_OS_MAC)
  55. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  56. [NSApplication sharedApplication];
  57. if (isEmbed())
  58. {
  59. // [fView retain];
  60. // [(NSView*)getParentWindowHandle() fView];
  61. }
  62. else
  63. {
  64. fWindow = [[NSWindow new]retain];
  65. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nil,);
  66. [fWindow setIsVisible:NO];
  67. if (NSString* const nsTitle = [[NSString alloc]
  68. initWithBytes:getTitle()
  69. length:strlen(getTitle())
  70. encoding:NSUTF8StringEncoding])
  71. [fWindow setTitle:nsTitle];
  72. // [fWindow setContentView:impl->view];
  73. // [fWindow makeFirstResponder:impl->view];
  74. [fWindow makeKeyAndOrderFront:fWindow];
  75. }
  76. [pool release];
  77. #elif defined(DISTRHO_OS_WINDOWS)
  78. #else
  79. fDisplay = XOpenDisplay(nullptr);
  80. DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  81. const int screen = DefaultScreen(fDisplay);
  82. const ::Window root = RootWindow(fDisplay, screen);
  83. const ::Window parent = isEmbed() ? (::Window)getParentWindowHandle() : root;
  84. fWindow = XCreateSimpleWindow(fDisplay, parent, 0, 0, getWidth(), getHeight(), 0, 0, 0);
  85. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  86. XSizeHints sizeHints = {};
  87. sizeHints.flags = PMinSize;
  88. sizeHints.min_width = getWidth();
  89. sizeHints.min_height = getHeight();
  90. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  91. XStoreName(fDisplay, fWindow, getTitle());
  92. if (parent == root)
  93. {
  94. // grab Esc key for auto-close
  95. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  96. // destroy window on close
  97. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  98. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  99. // set pid WM hint
  100. const pid_t pid = getpid();
  101. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  102. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  103. // set the window to both dialog and normal to produce a decorated floating dialog
  104. // order is important: DIALOG needs to come before NORMAL
  105. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  106. const Atom _wts[2] = {
  107. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  108. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  109. };
  110. XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  111. }
  112. #endif
  113. }
  114. ~EmbedExternalExampleUI()
  115. {
  116. #if defined(DISTRHO_OS_MAC)
  117. if (fWindow != nil)
  118. [fWindow close];
  119. if (fView != nullptr)
  120. [fView release];
  121. if (fWindow != nil)
  122. [fWindow release];
  123. #elif defined(DISTRHO_OS_WINDOWS)
  124. #else
  125. if (fDisplay == nullptr)
  126. return;
  127. if (fWindow != 0)
  128. XDestroyWindow(fDisplay, fWindow);
  129. XCloseDisplay(fDisplay);
  130. #endif
  131. }
  132. protected:
  133. /* --------------------------------------------------------------------------------------------------------
  134. * DSP/Plugin Callbacks */
  135. /**
  136. A parameter has changed on the plugin side.
  137. This is called by the host to inform the UI about parameter changes.
  138. */
  139. void parameterChanged(uint32_t index, float value) override
  140. {
  141. if (index != 0)
  142. return;
  143. fValue = value;
  144. }
  145. /* --------------------------------------------------------------------------------------------------------
  146. * External Window overrides */
  147. uintptr_t getNativeWindowHandle() const noexcept override
  148. {
  149. #if defined(DISTRHO_OS_MAC)
  150. return (uintptr_t)fView;
  151. #elif defined(DISTRHO_OS_WINDOWS)
  152. #else
  153. return (uintptr_t)fWindow;
  154. #endif
  155. return 0;
  156. }
  157. void titleChanged(const char* const title) override
  158. {
  159. d_stdout("visibilityChanged %s", title);
  160. #if defined(DISTRHO_OS_MAC)
  161. if (fWindow != nil)
  162. {
  163. if (NSString* const nsTitle = [[NSString alloc]
  164. initWithBytes:title
  165. length:strlen(title)
  166. encoding:NSUTF8StringEncoding])
  167. {
  168. [fWindow setTitle:nsTitle];
  169. }
  170. }
  171. #elif defined(DISTRHO_OS_WINDOWS)
  172. #else
  173. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  174. XStoreName(fDisplay, fWindow, title);
  175. #endif
  176. }
  177. void transientParentWindowChanged(const uintptr_t winId) override
  178. {
  179. d_stdout("transientParentWindowChanged %lu", winId);
  180. #if defined(DISTRHO_OS_MAC)
  181. #elif defined(DISTRHO_OS_WINDOWS)
  182. #else
  183. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  184. XSetTransientForHint(fDisplay, fWindow, (::Window)winId);
  185. #endif
  186. }
  187. void visibilityChanged(const bool visible) override
  188. {
  189. d_stdout("visibilityChanged %d", visible);
  190. #if defined(DISTRHO_OS_MAC)
  191. if (fWindow != nil)
  192. [fWindow setIsVisible:(visible ? YES : NO)];
  193. else if (fView != nullptr)
  194. [fView setHidden:(visible ? NO : YES)];
  195. #elif defined(DISTRHO_OS_WINDOWS)
  196. #else
  197. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  198. if (visible)
  199. XMapRaised(fDisplay, fWindow);
  200. else
  201. XUnmapWindow(fDisplay, fWindow);
  202. #endif
  203. }
  204. void uiIdle() override
  205. {
  206. // d_stdout("uiIdle");
  207. #if defined(DISTRHO_OS_MAC)
  208. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  209. NSDate* const date = [NSDate distantPast];
  210. for (NSEvent* event;;)
  211. {
  212. event = [NSApp
  213. nextEventMatchingMask:NSAnyEventMask
  214. untilDate:date
  215. inMode:NSDefaultRunLoopMode
  216. dequeue:YES];
  217. if (event == nil)
  218. break;
  219. [NSApp sendEvent: event];
  220. }
  221. [pool release];
  222. #elif defined(DISTRHO_OS_WINDOWS)
  223. MSG msg;
  224. if (! ::PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE))
  225. return true;
  226. if (::GetMessage(&msg, nullptr, 0, 0) >= 0)
  227. {
  228. if (msg.message == WM_QUIT)
  229. return false;
  230. //TranslateMessage(&msg);
  231. DispatchMessage(&msg);
  232. }
  233. #else
  234. if (fDisplay == nullptr)
  235. return;
  236. for (XEvent event; XPending(fDisplay) > 0;)
  237. {
  238. XNextEvent(fDisplay, &event);
  239. if (! isVisible())
  240. continue;
  241. switch (event.type)
  242. {
  243. case ClientMessage:
  244. if (char* const type = XGetAtomName(fDisplay, event.xclient.message_type))
  245. {
  246. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  247. hide();
  248. }
  249. break;
  250. case KeyRelease:
  251. if (event.xkey.keycode == X11Key_Escape)
  252. hide();
  253. break;
  254. }
  255. }
  256. #endif
  257. }
  258. // -------------------------------------------------------------------------------------------------------
  259. private:
  260. // Current value, cached for when UI becomes visible
  261. float fValue;
  262. /**
  263. Set our UI class as non-copyable and add a leak detector just in case.
  264. */
  265. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EmbedExternalExampleUI)
  266. };
  267. /* ------------------------------------------------------------------------------------------------------------
  268. * UI entry point, called by DPF to create a new UI instance. */
  269. UI* createUI()
  270. {
  271. return new EmbedExternalExampleUI();
  272. }
  273. // -----------------------------------------------------------------------------------------------------------
  274. END_NAMESPACE_DISTRHO