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.

504 lines
15KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2024 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. #include "DistrhoUI.hpp"
  17. #if defined(DISTRHO_OS_HAIKU)
  18. #elif defined(DISTRHO_OS_MAC)
  19. # import <Cocoa/Cocoa.h>
  20. #elif defined(DISTRHO_OS_WINDOWS)
  21. # define WIN32_CLASS_NAME "DPF-EmbedExternalExampleUI"
  22. # define WIN32_LEAN_AND_MEAN
  23. # include <windows.h>
  24. #else
  25. # include <sys/types.h>
  26. # include <X11/Xatom.h>
  27. # include <X11/Xlib.h>
  28. # include <X11/Xutil.h>
  29. # define X11Key_Escape 9
  30. #endif
  31. #if defined(DISTRHO_OS_MAC)
  32. # ifndef __MAC_10_12
  33. # define NSEventMaskAny NSAnyEventMask
  34. # define NSWindowStyleMaskClosable NSClosableWindowMask
  35. # define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
  36. # define NSWindowStyleMaskResizable NSResizableWindowMask
  37. # define NSWindowStyleMaskTitled NSTitledWindowMask
  38. # endif
  39. @interface NSExternalWindow : NSWindow
  40. @end
  41. @implementation NSExternalWindow {
  42. @public
  43. bool closed;
  44. bool standalone;
  45. }
  46. - (BOOL)canBecomeKeyWindow { return YES; }
  47. - (BOOL)canBecomeMainWindow { return standalone ? YES : NO; }
  48. - (BOOL)windowShouldClose:(id)_ { closed = true; return YES; }
  49. @end
  50. #endif
  51. START_NAMESPACE_DISTRHO
  52. // --------------------------------------------------------------------------------------------------------------------
  53. struct NativeWindow {
  54. struct Callbacks {
  55. virtual ~Callbacks() {}
  56. virtual void nativeHide() = 0;
  57. virtual void nativeResize(uint width, uint height) = 0;
  58. };
  59. Callbacks* const fCallbacks;
  60. const bool fIsStandalone;
  61. const bool fIsEmbed;
  62. bool fIsVisible;
  63. #if defined(DISTRHO_OS_HAIKU)
  64. #elif defined(DISTRHO_OS_MAC)
  65. NSView* fView;
  66. NSExternalWindow* fWindow;
  67. #elif defined(DISTRHO_OS_WINDOWS)
  68. ::HWND fWindow;
  69. #else
  70. ::Display* fDisplay;
  71. ::Window fWindow;
  72. #endif
  73. NativeWindow(Callbacks* callbacks,
  74. const char* title,
  75. uintptr_t parentWindowHandle,
  76. uint width,
  77. uint height,
  78. bool isStandalone);
  79. ~NativeWindow();
  80. void idle();
  81. void focus();
  82. void setSize(uint width, uint height);
  83. void setTitle(const char* title);
  84. void setTransientParentWindow(uintptr_t winId);
  85. void setVisible(bool visible);
  86. void hide()
  87. {
  88. fCallbacks->nativeHide();
  89. }
  90. uintptr_t getNativeWindowHandle() const noexcept
  91. {
  92. #if defined(DISTRHO_OS_HAIKU)
  93. #elif defined(DISTRHO_OS_MAC)
  94. return (uintptr_t)fView;
  95. #elif defined(DISTRHO_OS_WINDOWS)
  96. return (uintptr_t)fWindow;
  97. #else
  98. return (uintptr_t)fWindow;
  99. #endif
  100. }
  101. };
  102. // --------------------------------------------------------------------------------------------------------------------
  103. NativeWindow::NativeWindow(Callbacks* const callbacks,
  104. const char* const title,
  105. const uintptr_t parentWindowHandle,
  106. const uint width,
  107. const uint height,
  108. const bool isStandalone)
  109. : fCallbacks(callbacks),
  110. fIsStandalone(isStandalone),
  111. fIsEmbed(parentWindowHandle != 0),
  112. fIsVisible(parentWindowHandle != 0),
  113. #if defined(DISTRHO_OS_HAIKU)
  114. #elif defined(DISTRHO_OS_MAC)
  115. fView(nullptr),
  116. fWindow(nullptr)
  117. #elif defined(DISTRHO_OS_WINDOWS)
  118. fWindow(nullptr)
  119. #else
  120. fDisplay(nullptr),
  121. fWindow(0)
  122. #endif
  123. {
  124. #if defined(DISTRHO_OS_HAIKU)
  125. #elif defined(DISTRHO_OS_MAC)
  126. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  127. [NSApplication sharedApplication];
  128. if (standalone)
  129. {
  130. [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
  131. [NSApp activateIgnoringOtherApps:YES];
  132. }
  133. fView = [NSView new];
  134. DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
  135. [fView setFrame:NSMakeRect(0, 0, width, height)];
  136. [fView setAutoresizesSubviews:YES];
  137. [fView setWantsLayer:YES];
  138. [[fView layer] setBackgroundColor:[[NSColor blueColor] CGColor]];
  139. if (fIsEmbed)
  140. {
  141. [fView retain];
  142. [(NSView*)parentWindowHandle addSubview:fView];
  143. }
  144. else
  145. {
  146. const ulong styleMask = NSWindowStyleMaskClosable
  147. | NSWindowStyleMaskMiniaturizable
  148. | NSWindowStyleMaskResizable
  149. | NSWindowStyleMaskTitled;
  150. fWindow = [[[NSExternalWindow alloc]
  151. initWithContentRect:[fView frame]
  152. styleMask:styleMask
  153. backing:NSBackingStoreBuffered
  154. defer:NO] retain];
  155. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  156. fWindow->closed = false; // is this needed?
  157. fWindow->standalone = standalone;
  158. [fWindow setIsVisible:NO];
  159. if (NSString* const nsTitle = [[NSString alloc]
  160. initWithBytes:title
  161. length:std::strlen(title)
  162. encoding:NSUTF8StringEncoding])
  163. {
  164. [fWindow setTitle:nsTitle];
  165. [nsTitle release];
  166. }
  167. [fWindow setContentView:fView];
  168. [fWindow setContentSize:NSMakeSize(width, height)];
  169. [fWindow makeFirstResponder:fView];
  170. }
  171. [pool release];
  172. #elif defined(DISTRHO_OS_WINDOWS)
  173. WNDCLASS windowClass = {};
  174. windowClass.style = CS_OWNDC;
  175. windowClass.lpfnWndProc = DefWindowProc;
  176. windowClass.hInstance = nullptr;
  177. windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
  178. windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
  179. windowClass.lpszClassName = WIN32_CLASS_NAME;
  180. DISTRHO_SAFE_ASSERT_RETURN(RegisterClass(&windowClass),);
  181. const int winFlags = fIsEmbed
  182. ? WS_CHILD | WS_VISIBLE
  183. : WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX;
  184. RECT rect = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
  185. AdjustWindowRectEx(&rect, winFlags, FALSE, WS_EX_TOPMOST);
  186. fWindow = CreateWindowEx(WS_EX_TOPMOST,
  187. WIN32_CLASS_NAME,
  188. title,
  189. winFlags,
  190. CW_USEDEFAULT, CW_USEDEFAULT,
  191. rect.right - rect.left,
  192. rect.bottom - rect.top,
  193. (HWND)parentWindowHandle,
  194. nullptr, nullptr, nullptr);
  195. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  196. SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this);
  197. #else
  198. fDisplay = XOpenDisplay(nullptr);
  199. DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  200. const ::Window parent = fIsEmbed
  201. ? (::Window)parentWindowHandle
  202. : RootWindow(fDisplay, DefaultScreen(fDisplay));
  203. fWindow = XCreateSimpleWindow(fDisplay, parent, 0, 0, width, height, 0, 0, 0);
  204. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  205. XSizeHints sizeHints = {};
  206. sizeHints.flags = PMinSize;
  207. sizeHints.min_width = width;
  208. sizeHints.min_height = height;
  209. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  210. XStoreName(fDisplay, fWindow, title);
  211. if (fIsEmbed)
  212. {
  213. // start with window mapped, so host can access it
  214. XMapWindow(fDisplay, fWindow);
  215. }
  216. else
  217. {
  218. // grab Esc key for auto-close
  219. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  220. // destroy window on close
  221. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  222. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  223. // set pid WM hint
  224. const pid_t pid = getpid();
  225. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  226. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  227. // set the window to both dialog and normal to produce a decorated floating dialog
  228. // order is important: DIALOG needs to come before NORMAL
  229. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  230. const Atom _wts[2] = {
  231. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  232. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  233. };
  234. XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  235. }
  236. #endif
  237. }
  238. NativeWindow::~NativeWindow()
  239. {
  240. #if defined(DISTRHO_OS_HAIKU)
  241. #elif defined(DISTRHO_OS_MAC)
  242. if (fView != nullptr)
  243. {
  244. if (fWindow != nil)
  245. [fWindow close];
  246. [fView release];
  247. if (fWindow != nil)
  248. [fWindow release];
  249. }
  250. #elif defined(DISTRHO_OS_WINDOWS)
  251. if (fWindow != nullptr)
  252. DestroyWindow(fWindow);
  253. UnregisterClass(WIN32_CLASS_NAME, nullptr);
  254. #else
  255. if (fDisplay != nullptr)
  256. {
  257. if (fWindow != 0)
  258. XDestroyWindow(fDisplay, fWindow);
  259. XCloseDisplay(fDisplay);
  260. }
  261. #endif
  262. }
  263. void NativeWindow::idle()
  264. {
  265. #if defined(DISTRHO_OS_HAIKU)
  266. #elif defined(DISTRHO_OS_MAC)
  267. if (fIsStandalone)
  268. {
  269. NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init];
  270. NSDate* const date = [NSDate distantPast];
  271. for (NSEvent* event;;)
  272. {
  273. event = [NSApp
  274. nextEventMatchingMask:NSEventMaskAny
  275. untilDate:date
  276. inMode:NSDefaultRunLoopMode
  277. dequeue:YES];
  278. if (event == nil)
  279. break;
  280. [NSApp sendEvent:event];
  281. }
  282. if (fWindow->closed)
  283. {
  284. fWindow->closed = false;
  285. close();
  286. }
  287. [pool release];
  288. }
  289. #elif defined(DISTRHO_OS_WINDOWS)
  290. if (fIsStandalone && fWindow != nullptr)
  291. {
  292. MSG msg;
  293. if (::GetMessage(&msg, nullptr, 0, 0) > 0)
  294. {
  295. d_stdout("msg %u %u", msg.message, WM_SIZING);
  296. switch (msg.message)
  297. {
  298. case WM_SYSCOMMAND:
  299. if (msg.wParam != SC_CLOSE)
  300. break;
  301. // fall-through
  302. case WM_QUIT:
  303. hide();
  304. return;
  305. case WM_SIZING:
  306. break;
  307. }
  308. TranslateMessage(&msg);
  309. DispatchMessage(&msg);
  310. }
  311. }
  312. #else
  313. if (fDisplay != nullptr)
  314. {
  315. for (XEvent event; XPending(fDisplay) > 0;)
  316. {
  317. XNextEvent(fDisplay, &event);
  318. if (! fIsVisible)
  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. }
  336. #endif
  337. }
  338. void NativeWindow::focus()
  339. {
  340. d_stdout("focus");
  341. #if defined(DISTRHO_OS_HAIKU)
  342. #elif defined(DISTRHO_OS_MAC)
  343. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  344. [fWindow orderFrontRegardless];
  345. [fWindow makeKeyWindow];
  346. [fWindow makeFirstResponder:fView];
  347. #elif defined(DISTRHO_OS_WINDOWS)
  348. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  349. SetForegroundWindow(fWindow);
  350. SetActiveWindow(fWindow);
  351. SetFocus(fWindow);
  352. #else
  353. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  354. XRaiseWindow(fDisplay, fWindow);
  355. #endif
  356. }
  357. void NativeWindow::setSize(uint width, uint height)
  358. {
  359. #if defined(DISTRHO_OS_HAIKU)
  360. #elif defined(DISTRHO_OS_MAC)
  361. NSRect rect = [fView frame];
  362. rect.size = CGSizeMake((CGFloat)width, (CGFloat)height);
  363. [fView setFrame:rect];
  364. #elif defined(DISTRHO_OS_WINDOWS)
  365. if (fWindow != nullptr)
  366. SetWindowPos(fWindow,
  367. HWND_TOP,
  368. 0, 0,
  369. width, height,
  370. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  371. #else
  372. if (fWindow != 0)
  373. XResizeWindow(fDisplay, fWindow, width, height);
  374. #endif
  375. }
  376. void NativeWindow::setTitle(const char* const title)
  377. {
  378. #if defined(DISTRHO_OS_HAIKU)
  379. #elif defined(DISTRHO_OS_MAC)
  380. if (fWindow != nil)
  381. {
  382. if (NSString* const nsTitle = [[NSString alloc]
  383. initWithBytes:title
  384. length:strlen(title)
  385. encoding:NSUTF8StringEncoding])
  386. {
  387. [fWindow setTitle:nsTitle];
  388. [nsTitle release];
  389. }
  390. }
  391. #elif defined(DISTRHO_OS_WINDOWS)
  392. #else
  393. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  394. XStoreName(fDisplay, fWindow, title);
  395. #endif
  396. }
  397. void NativeWindow::setTransientParentWindow(const uintptr_t winId)
  398. {
  399. #if defined(DISTRHO_OS_HAIKU)
  400. #elif defined(DISTRHO_OS_MAC)
  401. #elif defined(DISTRHO_OS_WINDOWS)
  402. #else
  403. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  404. XSetTransientForHint(fDisplay, fWindow, (::Window)winId);
  405. #endif
  406. }
  407. void NativeWindow::setVisible(const bool visible)
  408. {
  409. fIsVisible = visible;
  410. #if defined(DISTRHO_OS_HAIKU)
  411. #elif defined(DISTRHO_OS_MAC)
  412. DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
  413. if (fWindow != nil)
  414. {
  415. [fWindow setIsVisible:(visible ? YES : NO)];
  416. if (fIsStandalone)
  417. [fWindow makeMainWindow];
  418. [fWindow makeKeyAndOrderFront:fWindow];
  419. }
  420. else
  421. {
  422. [fView setHidden:(visible ? NO : YES)];
  423. }
  424. #elif defined(DISTRHO_OS_WINDOWS)
  425. DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  426. ShowWindow(fWindow, visible ? SW_SHOWNORMAL : SW_HIDE);
  427. #else
  428. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  429. if (visible)
  430. XMapRaised(fDisplay, fWindow);
  431. else
  432. XUnmapWindow(fDisplay, fWindow);
  433. #endif
  434. }
  435. // --------------------------------------------------------------------------------------------------------------------
  436. END_NAMESPACE_DISTRHO