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.

415 lines
13KB

  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. #if defined(DISTRHO_OS_MAC)
  30. # ifndef __MAC_10_12
  31. # define NSEventMaskAny NSAnyEventMask
  32. # define NSWindowStyleMaskClosable NSClosableWindowMask
  33. # define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
  34. # define NSWindowStyleMaskResizable NSResizableWindowMask
  35. # define NSWindowStyleMaskTitled NSTitledWindowMask
  36. # endif
  37. @interface NSExternalWindow : NSWindow
  38. @end
  39. @implementation NSExternalWindow {
  40. @public
  41. bool closed;
  42. bool standalone;
  43. }
  44. - (BOOL)canBecomeKeyWindow { return YES; }
  45. - (BOOL)canBecomeMainWindow { return standalone ? YES : NO; }
  46. - (BOOL)windowShouldClose:(id)_ { closed = true; return YES; }
  47. @end
  48. #endif
  49. START_NAMESPACE_DISTRHO
  50. // -----------------------------------------------------------------------------------------------------------
  51. class EmbedExternalExampleUI : public UI
  52. {
  53. #if defined(DISTRHO_OS_MAC)
  54. NSView* fView;
  55. NSExternalWindow* fWindow;
  56. #elif defined(DISTRHO_OS_WINDOWS)
  57. #else
  58. ::Display* fDisplay;
  59. ::Window fWindow;
  60. #endif
  61. public:
  62. EmbedExternalExampleUI()
  63. : UI(512, 256),
  64. #if defined(DISTRHO_OS_MAC)
  65. fView(nullptr),
  66. fWindow(nullptr),
  67. #elif defined(DISTRHO_OS_WINDOWS)
  68. #else
  69. fDisplay(nullptr),
  70. fWindow(0),
  71. #endif
  72. fValue(0.0f)
  73. {
  74. const bool standalone = isStandalone();
  75. d_stdout("isStandalone %d", (int)standalone);
  76. #if defined(DISTRHO_OS_MAC)
  77. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc]init];
  78. [NSApplication sharedApplication];
  79. if (standalone)
  80. {
  81. [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
  82. [NSApp activateIgnoringOtherApps:YES];
  83. }
  84. fView = [NSView new];
  85. DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
  86. [fView initWithFrame:NSMakeRect(0, 0, getWidth(), getHeight())];
  87. [fView setAutoresizesSubviews:YES];
  88. [fView setFrame:NSMakeRect(0, 0, getWidth(), getHeight())];
  89. [fView setHidden:NO];
  90. [fView setNeedsDisplay:YES];
  91. if (isEmbed())
  92. {
  93. [fView retain];
  94. [(NSView*)getParentWindowHandle() addSubview:fView];
  95. }
  96. else
  97. {
  98. const ulong styleMask = NSWindowStyleMaskClosable
  99. | NSWindowStyleMaskMiniaturizable
  100. | NSWindowStyleMaskResizable
  101. | NSWindowStyleMaskTitled;
  102. fWindow = [[[NSExternalWindow alloc]
  103. initWithContentRect:[fView frame]
  104. styleMask:styleMask
  105. backing:NSBackingStoreBuffered
  106. defer:NO]retain];
  107. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  108. fWindow->closed = false; // is this needed?
  109. fWindow->standalone = standalone;
  110. [fWindow setIsVisible:NO];
  111. if (NSString* const nsTitle = [[NSString alloc]
  112. initWithBytes:getTitle()
  113. length:strlen(getTitle())
  114. encoding:NSUTF8StringEncoding])
  115. [fWindow setTitle:nsTitle];
  116. [fWindow setContentView:fView];
  117. [fWindow setContentSize:NSMakeSize(getWidth(), getHeight())];
  118. [fWindow makeFirstResponder:fView];
  119. }
  120. [pool release];
  121. #elif defined(DISTRHO_OS_WINDOWS)
  122. #else
  123. fDisplay = XOpenDisplay(nullptr);
  124. DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  125. const ::Window parent = isEmbed()
  126. ? (::Window)getParentWindowHandle()
  127. : RootWindow(fDisplay, DefaultScreen(fDisplay));
  128. fWindow = XCreateSimpleWindow(fDisplay, parent, 0, 0, getWidth(), getHeight(), 0, 0, 0);
  129. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  130. XSizeHints sizeHints = {};
  131. sizeHints.flags = PMinSize;
  132. sizeHints.min_width = getWidth();
  133. sizeHints.min_height = getHeight();
  134. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  135. XStoreName(fDisplay, fWindow, getTitle());
  136. if (isEmbed())
  137. {
  138. // start with window mapped, so host can access it
  139. XMapWindow(fDisplay, fWindow);
  140. }
  141. else
  142. {
  143. // grab Esc key for auto-close
  144. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  145. // destroy window on close
  146. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  147. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  148. // set pid WM hint
  149. const pid_t pid = getpid();
  150. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  151. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  152. // set the window to both dialog and normal to produce a decorated floating dialog
  153. // order is important: DIALOG needs to come before NORMAL
  154. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  155. const Atom _wts[2] = {
  156. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  157. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  158. };
  159. XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  160. }
  161. #endif
  162. d_stdout("created external window with size %u %u", getWidth(), getHeight());
  163. }
  164. ~EmbedExternalExampleUI()
  165. {
  166. #if defined(DISTRHO_OS_MAC)
  167. if (fView == nullptr)
  168. return;
  169. if (fWindow != nil)
  170. [fWindow close];
  171. [fView release];
  172. if (fWindow != nil)
  173. [fWindow release];
  174. #elif defined(DISTRHO_OS_WINDOWS)
  175. #else
  176. if (fDisplay == nullptr)
  177. return;
  178. if (fWindow != 0)
  179. XDestroyWindow(fDisplay, fWindow);
  180. XCloseDisplay(fDisplay);
  181. #endif
  182. }
  183. protected:
  184. /* --------------------------------------------------------------------------------------------------------
  185. * DSP/Plugin Callbacks */
  186. /**
  187. A parameter has changed on the plugin side.
  188. This is called by the host to inform the UI about parameter changes.
  189. */
  190. void parameterChanged(uint32_t index, float value) override
  191. {
  192. if (index != 0)
  193. return;
  194. fValue = value;
  195. }
  196. /* --------------------------------------------------------------------------------------------------------
  197. * External Window overrides */
  198. void focus() override
  199. {
  200. d_stdout("focus");
  201. #if defined(DISTRHO_OS_MAC)
  202. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nil,);
  203. [fWindow orderFrontRegardless];
  204. [fWindow makeKeyWindow];
  205. [fWindow makeFirstResponder:fView];
  206. #elif defined(DISTRHO_OS_WINDOWS)
  207. #else
  208. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  209. XRaiseWindow(fDisplay, fWindow);
  210. #endif
  211. }
  212. uintptr_t getNativeWindowHandle() const noexcept override
  213. {
  214. #if defined(DISTRHO_OS_MAC)
  215. return (uintptr_t)fView;
  216. #elif defined(DISTRHO_OS_WINDOWS)
  217. #else
  218. return (uintptr_t)fWindow;
  219. #endif
  220. return 0;
  221. }
  222. void titleChanged(const char* const title) override
  223. {
  224. d_stdout("titleChanged %s", title);
  225. #if defined(DISTRHO_OS_MAC)
  226. if (fWindow != nil)
  227. {
  228. if (NSString* const nsTitle = [[NSString alloc]
  229. initWithBytes:title
  230. length:strlen(title)
  231. encoding:NSUTF8StringEncoding])
  232. {
  233. [fWindow setTitle:nsTitle];
  234. [nsTitle release];
  235. }
  236. }
  237. #elif defined(DISTRHO_OS_WINDOWS)
  238. #else
  239. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  240. XStoreName(fDisplay, fWindow, title);
  241. #endif
  242. }
  243. void transientParentWindowChanged(const uintptr_t winId) override
  244. {
  245. d_stdout("transientParentWindowChanged %lu", winId);
  246. #if defined(DISTRHO_OS_MAC)
  247. #elif defined(DISTRHO_OS_WINDOWS)
  248. #else
  249. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  250. XSetTransientForHint(fDisplay, fWindow, (::Window)winId);
  251. #endif
  252. }
  253. void visibilityChanged(const bool visible) override
  254. {
  255. d_stdout("visibilityChanged %d", visible);
  256. #if defined(DISTRHO_OS_MAC)
  257. DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
  258. if (fWindow != nil)
  259. {
  260. [fWindow setIsVisible:(visible ? YES : NO)];
  261. if (isStandalone())
  262. [fWindow makeMainWindow];
  263. [fWindow makeKeyAndOrderFront:fWindow];
  264. }
  265. else
  266. {
  267. [fView setHidden:(visible ? NO : YES)];
  268. }
  269. #elif defined(DISTRHO_OS_WINDOWS)
  270. #else
  271. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  272. if (visible)
  273. XMapRaised(fDisplay, fWindow);
  274. else
  275. XUnmapWindow(fDisplay, fWindow);
  276. #endif
  277. }
  278. void uiIdle() override
  279. {
  280. // d_stdout("uiIdle");
  281. #if defined(DISTRHO_OS_MAC)
  282. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  283. NSDate* const date = [NSDate distantPast];
  284. for (NSEvent* event;;)
  285. {
  286. event = [NSApp
  287. nextEventMatchingMask:NSEventMaskAny
  288. untilDate:date
  289. inMode:NSDefaultRunLoopMode
  290. dequeue:YES];
  291. if (event == nil)
  292. break;
  293. [NSApp sendEvent:event];
  294. }
  295. if (fWindow->closed)
  296. {
  297. fWindow->closed = false;
  298. close();
  299. }
  300. [pool release];
  301. #elif defined(DISTRHO_OS_WINDOWS)
  302. /*MSG msg;
  303. if (! ::PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE))
  304. return true;
  305. if (::GetMessage(&msg, nullptr, 0, 0) >= 0)
  306. {
  307. if (msg.message == WM_QUIT)
  308. return false;
  309. //TranslateMessage(&msg);
  310. DispatchMessage(&msg);
  311. }*/
  312. #else
  313. if (fDisplay == nullptr)
  314. return;
  315. for (XEvent event; XPending(fDisplay) > 0;)
  316. {
  317. XNextEvent(fDisplay, &event);
  318. if (! isVisible())
  319. continue;
  320. switch (event.type)
  321. {
  322. case ClientMessage:
  323. if (char* const type = XGetAtomName(fDisplay, event.xclient.message_type))
  324. {
  325. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  326. hide();
  327. }
  328. break;
  329. case KeyRelease:
  330. if (event.xkey.keycode == X11Key_Escape)
  331. hide();
  332. break;
  333. }
  334. }
  335. #endif
  336. }
  337. // -------------------------------------------------------------------------------------------------------
  338. private:
  339. // Current value, cached for when UI becomes visible
  340. float fValue;
  341. /**
  342. Set our UI class as non-copyable and add a leak detector just in case.
  343. */
  344. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EmbedExternalExampleUI)
  345. };
  346. /* ------------------------------------------------------------------------------------------------------------
  347. * UI entry point, called by DPF to create a new UI instance. */
  348. UI* createUI()
  349. {
  350. return new EmbedExternalExampleUI();
  351. }
  352. // -----------------------------------------------------------------------------------------------------------
  353. END_NAMESPACE_DISTRHO