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.

575 lines
18KB

  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. #include "pugl.hpp"
  17. /* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
  18. #include <cassert>
  19. #include <cmath>
  20. #include <cstdlib>
  21. #include <cstring>
  22. #include <ctime>
  23. #if defined(DISTRHO_OS_HAIKU)
  24. #elif defined(DISTRHO_OS_MAC)
  25. # import <Cocoa/Cocoa.h>
  26. # include <dlfcn.h>
  27. # include <mach/mach_time.h>
  28. # ifdef DGL_CAIRO
  29. # include <cairo.h>
  30. # include <cairo-quartz.h>
  31. # endif
  32. # ifdef DGL_OPENGL
  33. # include <OpenGL/gl.h>
  34. # endif
  35. # ifdef DGL_VULKAN
  36. # import <QuartzCore/CAMetalLayer.h>
  37. # include <vulkan/vulkan_core.h>
  38. # include <vulkan/vulkan_macos.h>
  39. # endif
  40. #elif defined(DISTRHO_OS_WINDOWS)
  41. # include <wctype.h>
  42. # include <windows.h>
  43. # include <windowsx.h>
  44. # ifdef DGL_CAIRO
  45. # include <cairo.h>
  46. # include <cairo-win32.h>
  47. # endif
  48. # ifdef DGL_OPENGL
  49. # include <GL/gl.h>
  50. # endif
  51. # ifdef DGL_VULKAN
  52. # include <vulkan/vulkan.h>
  53. # include <vulkan/vulkan_win32.h>
  54. # endif
  55. #else
  56. # include <dlfcn.h>
  57. # include <sys/select.h>
  58. # include <sys/time.h>
  59. # include <X11/X.h>
  60. # include <X11/Xatom.h>
  61. # include <X11/Xlib.h>
  62. # include <X11/Xutil.h>
  63. # include <X11/keysym.h>
  64. # ifdef HAVE_XCURSOR
  65. # include <X11/Xcursor/Xcursor.h>
  66. # include <X11/cursorfont.h>
  67. # endif
  68. # ifdef HAVE_XRANDR
  69. # include <X11/extensions/Xrandr.h>
  70. # endif
  71. # ifdef HAVE_XSYNC
  72. # include <X11/extensions/sync.h>
  73. # include <X11/extensions/syncconst.h>
  74. # endif
  75. # ifdef DGL_CAIRO
  76. # include <cairo.h>
  77. # include <cairo-xlib.h>
  78. # endif
  79. # ifdef DGL_OPENGL
  80. # include <GL/gl.h>
  81. # include <GL/glx.h>
  82. # endif
  83. # ifdef DGL_VULKAN
  84. # include <vulkan/vulkan_core.h>
  85. # include <vulkan/vulkan_xlib.h>
  86. # endif
  87. #endif
  88. #ifdef HAVE_X11
  89. # define DBLCLKTME 400
  90. # include "sofd/libsofd.h"
  91. # include "sofd/libsofd.c"
  92. #endif
  93. #ifndef DISTRHO_OS_MAC
  94. START_NAMESPACE_DGL
  95. #endif
  96. // --------------------------------------------------------------------------------------------------------------------
  97. #if defined(DISTRHO_OS_HAIKU)
  98. #elif defined(DISTRHO_OS_MAC)
  99. # ifndef DISTRHO_MACOS_NAMESPACE_MACRO
  100. # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE
  101. # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE)
  102. # define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView)
  103. # define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView)
  104. # define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow)
  105. # endif
  106. # ifndef __MAC_10_9
  107. # define NSModalResponseOK NSOKButton
  108. typedef NSUInteger NSEventSubtype;
  109. # endif
  110. # pragma clang diagnostic push
  111. # pragma clang diagnostic ignored "-Wdeprecated-declarations"
  112. # import "pugl-upstream/src/mac.m"
  113. # import "pugl-upstream/src/mac_stub.m"
  114. # ifdef DGL_CAIRO
  115. # import "pugl-upstream/src/mac_cairo.m"
  116. # endif
  117. # ifdef DGL_OPENGL
  118. # import "pugl-upstream/src/mac_gl.m"
  119. # endif
  120. # ifdef DGL_VULKAN
  121. # import "pugl-upstream/src/mac_vulkan.m"
  122. # endif
  123. # pragma clang diagnostic pop
  124. #elif defined(DISTRHO_OS_WINDOWS)
  125. # include "pugl-upstream/src/win.c"
  126. # include "pugl-upstream/src/win_stub.c"
  127. # ifdef DGL_CAIRO
  128. # include "pugl-upstream/src/win_cairo.c"
  129. # endif
  130. # ifdef DGL_OPENGL
  131. # include "pugl-upstream/src/win_gl.c"
  132. # endif
  133. # ifdef DGL_VULKAN
  134. # include "pugl-upstream/src/win_vulkan.c"
  135. # endif
  136. #else
  137. # include "pugl-upstream/src/x11.c"
  138. # include "pugl-upstream/src/x11_stub.c"
  139. # ifdef DGL_CAIRO
  140. # include "pugl-upstream/src/x11_cairo.c"
  141. # endif
  142. # ifdef DGL_OPENGL
  143. # include "pugl-upstream/src/x11_gl.c"
  144. # endif
  145. # ifdef DGL_VULKAN
  146. # include "pugl-upstream/src/x11_vulkan.c"
  147. # endif
  148. #endif
  149. #include "pugl-upstream/src/implementation.c"
  150. // --------------------------------------------------------------------------------------------------------------------
  151. // expose backend enter
  152. void puglBackendEnter(PuglView* const view)
  153. {
  154. view->backend->enter(view, NULL);
  155. }
  156. // --------------------------------------------------------------------------------------------------------------------
  157. // expose backend leave
  158. void puglBackendLeave(PuglView* const view)
  159. {
  160. view->backend->leave(view, NULL);
  161. }
  162. // --------------------------------------------------------------------------------------------------------------------
  163. // clear minimum size to 0
  164. void puglClearMinSize(PuglView* const view)
  165. {
  166. view->minWidth = 0;
  167. view->minHeight = 0;
  168. }
  169. // --------------------------------------------------------------------------------------------------------------------
  170. // missing in pugl, directly returns transient parent
  171. PuglNativeView puglGetTransientParent(const PuglView* const view)
  172. {
  173. return view->transientParent;
  174. }
  175. // --------------------------------------------------------------------------------------------------------------------
  176. // missing in pugl, directly returns title char* pointer
  177. const char* puglGetWindowTitle(const PuglView* const view)
  178. {
  179. return view->title;
  180. }
  181. // --------------------------------------------------------------------------------------------------------------------
  182. // get global scale factor
  183. double puglGetDesktopScaleFactor(const PuglView* const view)
  184. {
  185. #if defined(DISTRHO_OS_MAC)
  186. return (view->impl->window ? [view->impl->window screen]
  187. : [NSScreen mainScreen]).backingScaleFactor;
  188. #else
  189. return 1.0;
  190. // unused
  191. (void)view;
  192. #endif
  193. }
  194. // --------------------------------------------------------------------------------------------------------------------
  195. // bring view window into the foreground, aka "raise" window
  196. void puglRaiseWindow(PuglView* const view)
  197. {
  198. #if defined(DISTRHO_OS_HAIKU)
  199. // nothing here yet
  200. #elif defined(DISTRHO_OS_MAC)
  201. if (view->impl->window)
  202. [view->impl->window orderFrontRegardless];
  203. #elif defined(DISTRHO_OS_WINDOWS)
  204. SetForegroundWindow(view->impl->hwnd);
  205. SetActiveWindow(view->impl->hwnd);
  206. #else
  207. XRaiseWindow(view->impl->display, view->impl->win);
  208. #endif
  209. }
  210. // --------------------------------------------------------------------------------------------------------------------
  211. // set backend that matches current build
  212. void puglSetMatchingBackendForCurrentBuild(PuglView* const view)
  213. {
  214. #ifdef DGL_CAIRO
  215. puglSetBackend(view, puglCairoBackend());
  216. #endif
  217. #ifdef DGL_OPENGL
  218. puglSetBackend(view, puglGlBackend());
  219. #endif
  220. #ifdef DGL_VULKAN
  221. puglSetBackend(view, puglVulkanBackend());
  222. #endif
  223. if (view->backend == nullptr)
  224. puglSetBackend(view, puglStubBackend());
  225. }
  226. // --------------------------------------------------------------------------------------------------------------------
  227. // Combine puglSetMinSize and puglSetAspectRatio
  228. PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect)
  229. {
  230. view->minWidth = (int)width;
  231. view->minHeight = (int)height;
  232. if (aspect) {
  233. view->minAspectX = (int)width;
  234. view->minAspectY = (int)height;
  235. view->maxAspectX = (int)width;
  236. view->maxAspectY = (int)height;
  237. }
  238. #if defined(DISTRHO_OS_HAIKU)
  239. // nothing?
  240. #elif defined(DISTRHO_OS_MAC)
  241. /*
  242. if (view->impl->window)
  243. {
  244. [view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)];
  245. if (aspect)
  246. [view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)];
  247. }
  248. */
  249. puglSetMinSize(view, width, height);
  250. puglSetAspectRatio(view, width, height, width, height);
  251. #elif defined(DISTRHO_OS_WINDOWS)
  252. // nothing
  253. #else
  254. if (const PuglStatus status = updateSizeHints(view))
  255. return status;
  256. XFlush(view->impl->display);
  257. #endif
  258. return PUGL_SUCCESS;
  259. }
  260. // --------------------------------------------------------------------------------------------------------------------
  261. // set window size with default size and without changing frame x/y position
  262. PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height)
  263. {
  264. view->defaultWidth = width;
  265. view->defaultHeight = height;
  266. #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC)
  267. // keep upstream behaviour
  268. const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height };
  269. return puglSetFrame(view, frame);
  270. #elif defined(DISTRHO_OS_WINDOWS)
  271. // matches upstream pugl, except we add SWP_NOMOVE flag
  272. if (view->impl->hwnd)
  273. {
  274. const PuglRect frame = view->frame;
  275. RECT rect = { (long)frame.x,
  276. (long)frame.y,
  277. (long)frame.x + (long)frame.width,
  278. (long)frame.y + (long)frame.height };
  279. AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view));
  280. if (! SetWindowPos(view->impl->hwnd,
  281. HWND_TOP,
  282. rect.left,
  283. rect.top,
  284. rect.right - rect.left,
  285. rect.bottom - rect.top,
  286. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER))
  287. return PUGL_UNKNOWN_ERROR;
  288. }
  289. #else
  290. // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow
  291. if (view->impl->win)
  292. {
  293. Display* const display = view->impl->display;
  294. if (! XResizeWindow(display, view->impl->win, width, height))
  295. return PUGL_UNKNOWN_ERROR;
  296. if (const PuglStatus status = updateSizeHints(view))
  297. return status;
  298. XFlush(display);
  299. }
  300. #endif
  301. view->frame.width = width;
  302. view->frame.height = height;
  303. return PUGL_SUCCESS;
  304. }
  305. // --------------------------------------------------------------------------------------------------------------------
  306. // DGL specific, build-specific drawing prepare
  307. void puglOnDisplayPrepare(PuglView*)
  308. {
  309. #ifdef DGL_OPENGL
  310. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  311. glLoadIdentity();
  312. #endif
  313. }
  314. // --------------------------------------------------------------------------------------------------------------------
  315. // DGL specific, build-specific fallback resize
  316. void puglFallbackOnResize(PuglView* const view)
  317. {
  318. #ifdef DGL_OPENGL
  319. glEnable(GL_BLEND);
  320. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  321. glMatrixMode(GL_PROJECTION);
  322. glLoadIdentity();
  323. glOrtho(0.0, static_cast<GLdouble>(view->frame.width), static_cast<GLdouble>(view->frame.height), 0.0, 0.0, 1.0);
  324. glViewport(0, 0, static_cast<GLsizei>(view->frame.width), static_cast<GLsizei>(view->frame.height));
  325. glMatrixMode(GL_MODELVIEW);
  326. glLoadIdentity();
  327. #endif
  328. }
  329. #ifdef DISTRHO_OS_MAC
  330. // --------------------------------------------------------------------------------------------------------------------
  331. // macOS specific, setup file browser dialog
  332. bool puglMacOSFilePanelOpen(PuglView* const view,
  333. const char* const startDir, const char* const title, const uint flags,
  334. openPanelCallback callback)
  335. {
  336. PuglInternals* impl = view->impl;
  337. NSOpenPanel* const panel = [NSOpenPanel openPanel];
  338. // TODO flags
  339. [panel setCanChooseFiles:YES];
  340. [panel setCanChooseDirectories:NO];
  341. [panel setAllowsMultipleSelection:NO];
  342. [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]];
  343. NSString* titleString = [[NSString alloc]
  344. initWithBytes:title
  345. length:strlen(title)
  346. encoding:NSUTF8StringEncoding];
  347. [panel setTitle:titleString];
  348. [panel beginSheetModalForWindow:(impl->window ? impl->window : [view->impl->wrapperView window])
  349. completionHandler:^(NSInteger result)
  350. {
  351. if (result == NSModalResponseOK && [[panel URL] isFileURL])
  352. {
  353. NSString* const path = [[panel URL] path];
  354. callback(view, [path UTF8String]);
  355. }
  356. else
  357. {
  358. callback(view, nullptr);
  359. }
  360. }];
  361. return true;
  362. }
  363. // --------------------------------------------------------------------------------------------------------------------
  364. // macOS specific, allow standalone window to gain focus
  365. void puglMacOSActivateApp()
  366. {
  367. [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
  368. [NSApp activateIgnoringOtherApps:YES];
  369. }
  370. #endif
  371. #ifdef DISTRHO_OS_WINDOWS
  372. // --------------------------------------------------------------------------------------------------------------------
  373. // win32 specific, call ShowWindow with SW_RESTORE
  374. void puglWin32RestoreWindow(PuglView* const view)
  375. {
  376. PuglInternals* impl = view->impl;
  377. DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);
  378. ShowWindow(impl->hwnd, SW_RESTORE);
  379. SetFocus(impl->hwnd);
  380. }
  381. // --------------------------------------------------------------------------------------------------------------------
  382. // win32 specific, center view based on parent coordinates (if there is one)
  383. void puglWin32ShowWindowCentered(PuglView* const view)
  384. {
  385. PuglInternals* impl = view->impl;
  386. DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);
  387. RECT rectChild, rectParent;
  388. if (view->transientParent != 0 &&
  389. GetWindowRect(impl->hwnd, &rectChild) &&
  390. GetWindowRect((HWND)view->transientParent, &rectParent))
  391. {
  392. SetWindowPos(impl->hwnd, (HWND)view->transientParent,
  393. rectParent.left + (rectChild.right-rectChild.left)/2,
  394. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  395. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  396. }
  397. else
  398. {
  399. ShowWindow(impl->hwnd, SW_SHOWNORMAL);
  400. }
  401. SetFocus(impl->hwnd);
  402. }
  403. // --------------------------------------------------------------------------------------------------------------------
  404. // win32 specific, set or unset WS_SIZEBOX style flag
  405. void puglWin32SetWindowResizable(PuglView* const view, const bool resizable)
  406. {
  407. PuglInternals* impl = view->impl;
  408. DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);
  409. const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX
  410. : GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  411. SetWindowLong(impl->hwnd, GWL_STYLE, winFlags);
  412. }
  413. // --------------------------------------------------------------------------------------------------------------------
  414. #endif
  415. #ifdef HAVE_X11
  416. // --------------------------------------------------------------------------------------------------------------------
  417. // X11 specific, setup event loop filter for sofd file dialog
  418. static bool sofd_has_action;
  419. static char* sofd_filename;
  420. static bool sofd_event_filter(Display* const display, XEvent* const xevent)
  421. {
  422. if (x_fib_handle_events(display, xevent) == 0)
  423. return false;
  424. if (sofd_filename != nullptr)
  425. std::free(sofd_filename);
  426. if (x_fib_status() > 0)
  427. sofd_filename = x_fib_filename();
  428. else
  429. sofd_filename = nullptr;
  430. x_fib_close(display);
  431. sofd_has_action = true;
  432. return true;
  433. }
  434. void sofdFileDialogSetup(PuglWorld* const world)
  435. {
  436. puglX11SetEventFilter(world, sofd_event_filter);
  437. }
  438. // --------------------------------------------------------------------------------------------------------------------
  439. // X11 specific, show file dialog via sofd
  440. bool sofdFileDialogShow(PuglView* const view,
  441. const char* const startDir, const char* const title,
  442. const uint flags, const uint width, const uint height)
  443. {
  444. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
  445. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
  446. /*
  447. x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
  448. x_fib_cfg_buttons(1, options.buttons.showHidden-1);
  449. x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
  450. */
  451. PuglInternals* const impl = view->impl;
  452. return (x_fib_show(impl->display, impl->win, width, height) == 0);
  453. }
  454. // --------------------------------------------------------------------------------------------------------------------
  455. // X11 specific, close sofd file dialog
  456. void sofdFileDialogClose(PuglView* const view)
  457. {
  458. PuglInternals* const impl = view->impl;
  459. x_fib_close(impl->display);
  460. }
  461. // --------------------------------------------------------------------------------------------------------------------
  462. // X11 specific, get path chosen via sofd file dialog
  463. bool sofdFileDialogGetPath(char** path)
  464. {
  465. if (! sofd_has_action)
  466. return false;
  467. sofd_has_action = false;
  468. *path = sofd_filename;
  469. return true;
  470. }
  471. // --------------------------------------------------------------------------------------------------------------------
  472. // X11 specific, free path of sofd file dialog, no longer needed
  473. void sofdFileDialogFree(char* const path)
  474. {
  475. DISTRHO_SAFE_ASSERT_RETURN(path == nullptr || path == sofd_filename,);
  476. std::free(sofd_filename);
  477. sofd_filename = nullptr;
  478. }
  479. // --------------------------------------------------------------------------------------------------------------------
  480. #endif
  481. // --------------------------------------------------------------------------------------------------------------------
  482. #ifndef DISTRHO_OS_MAC
  483. END_NAMESPACE_DGL
  484. #endif