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.

1755 lines
45KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
  4. * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru>
  5. * Copyright (C) 2019 Robin Gareus <robin@gareus.org>
  6. *
  7. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  8. * or without fee is hereby granted, provided that the above copyright notice and this
  9. * permission notice appear in all copies.
  10. *
  11. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  12. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  13. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  14. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  15. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  16. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. // we need this for now
  19. //#define PUGL_GRAB_FOCUS 1
  20. #include "../Base.hpp"
  21. #ifdef DGL_CAIRO
  22. # define PUGL_CAIRO
  23. # include "../Cairo.hpp"
  24. #endif
  25. #ifdef DGL_OPENGL
  26. # define PUGL_OPENGL
  27. # include "../OpenGL.hpp"
  28. #endif
  29. #include "pugl/pugl.h"
  30. #if defined(__GNUC__) && (__GNUC__ >= 7)
  31. # pragma GCC diagnostic push
  32. # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
  33. #endif
  34. #if defined(DISTRHO_OS_HAIKU)
  35. # include "pugl/pugl_haiku.cpp"
  36. #elif defined(DISTRHO_OS_MAC)
  37. # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE)
  38. # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE)
  39. # include "pugl/pugl_osx.m"
  40. #elif defined(DISTRHO_OS_WINDOWS)
  41. # include "pugl/pugl_win.cpp"
  42. # undef max
  43. # undef min
  44. #else
  45. # include <sys/types.h>
  46. # include <unistd.h>
  47. extern "C" {
  48. # include "pugl/pugl_x11.c"
  49. }
  50. #endif
  51. #if defined(__GNUC__) && (__GNUC__ >= 7)
  52. # pragma GCC diagnostic pop
  53. #endif
  54. #include "ApplicationPrivateData.hpp"
  55. #include "WidgetPrivateData.hpp"
  56. #include "../StandaloneWindow.hpp"
  57. #include "../../distrho/extra/String.hpp"
  58. #define FOR_EACH_WIDGET(it) \
  59. for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
  60. #define FOR_EACH_WIDGET_INV(rit) \
  61. for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
  62. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  63. # define DBG(msg) std::fprintf(stderr, "%s", msg);
  64. # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  65. # define DBGF std::fflush(stderr);
  66. #else
  67. # define DBG(msg)
  68. # define DBGp(...)
  69. # define DBGF
  70. #endif
  71. START_NAMESPACE_DGL
  72. // -----------------------------------------------------------------------
  73. // Window Private
  74. struct Window::PrivateData {
  75. PrivateData(Application& app, Window* const self)
  76. : fApp(app),
  77. fSelf(self),
  78. fView(puglInit()),
  79. fFirstInit(true),
  80. fVisible(false),
  81. fResizable(true),
  82. fUsingEmbed(false),
  83. fWidth(1),
  84. fHeight(1),
  85. fScaling(1.0),
  86. fAutoScaling(1.0),
  87. fTitle(nullptr),
  88. fWidgets(),
  89. fModal(),
  90. #if defined(DISTRHO_OS_HAIKU)
  91. bWindow(nullptr)
  92. #elif defined(DISTRHO_OS_MAC)
  93. fNeedsIdle(true),
  94. mView(nullptr),
  95. mWindow(nullptr),
  96. mParentWindow(nullptr)
  97. #elif defined(DISTRHO_OS_WINDOWS)
  98. hwnd(nullptr),
  99. hwndParent(nullptr)
  100. # ifndef DGL_FILE_BROWSER_DISABLED
  101. , fOpenFilePanel(nullptr),
  102. fFilePanelDelegate(nullptr)
  103. # endif
  104. #else
  105. xDisplay(nullptr),
  106. xWindow(0)
  107. #endif
  108. {
  109. DBG("Creating window without parent..."); DBGF;
  110. init();
  111. }
  112. PrivateData(Application& app, Window* const self, Window& parent)
  113. : fApp(app),
  114. fSelf(self),
  115. fView(puglInit()),
  116. fFirstInit(true),
  117. fVisible(false),
  118. fResizable(true),
  119. fUsingEmbed(false),
  120. fWidth(1),
  121. fHeight(1),
  122. fScaling(1.0),
  123. fAutoScaling(1.0),
  124. fTitle(nullptr),
  125. fWidgets(),
  126. fModal(parent.pData),
  127. #if defined(DISTRHO_OS_HAIKU)
  128. bWindow(nullptr)
  129. #elif defined(DISTRHO_OS_MAC)
  130. fNeedsIdle(false),
  131. mView(nullptr),
  132. mWindow(nullptr),
  133. mParentWindow(nullptr)
  134. #elif defined(DISTRHO_OS_WINDOWS)
  135. hwnd(nullptr),
  136. hwndParent(nullptr)
  137. # ifndef DGL_FILE_BROWSER_DISABLED
  138. , fOpenFilePanel(nullptr),
  139. fFilePanelDelegate(nullptr)
  140. # endif
  141. #else
  142. xDisplay(nullptr),
  143. xWindow(0)
  144. #endif
  145. {
  146. DBG("Creating window with parent..."); DBGF;
  147. init();
  148. const PuglInternals* const parentImpl(parent.pData->fView->impl);
  149. // NOTE: almost a 1:1 copy of setTransientWinId()
  150. #if defined(DISTRHO_OS_HAIKU)
  151. // TODO
  152. #elif defined(DISTRHO_OS_MAC)
  153. mParentWindow = parentImpl->window;
  154. #elif defined(DISTRHO_OS_WINDOWS)
  155. hwndParent = parentImpl->hwnd;
  156. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent);
  157. #else
  158. XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
  159. #endif
  160. }
  161. PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable)
  162. : fApp(app),
  163. fSelf(self),
  164. fView(puglInit()),
  165. fFirstInit(true),
  166. fVisible(parentId != 0),
  167. fResizable(resizable),
  168. fUsingEmbed(parentId != 0),
  169. fWidth(1),
  170. fHeight(1),
  171. fScaling(scaling),
  172. fAutoScaling(1.0),
  173. fTitle(nullptr),
  174. fWidgets(),
  175. fModal(),
  176. #if defined(DISTRHO_OS_HAIKU)
  177. bWindow(nullptr)
  178. #elif defined(DISTRHO_OS_MAC)
  179. fNeedsIdle(parentId == 0),
  180. mView(nullptr),
  181. mWindow(nullptr),
  182. mParentWindow(nullptr)
  183. #elif defined(DISTRHO_OS_WINDOWS)
  184. hwnd(nullptr),
  185. hwndParent(nullptr)
  186. # ifndef DGL_FILE_BROWSER_DISABLED
  187. , fOpenFilePanel(nullptr),
  188. fFilePanelDelegate(nullptr)
  189. # endif
  190. #else
  191. xDisplay(nullptr),
  192. xWindow(0)
  193. #endif
  194. {
  195. if (fUsingEmbed)
  196. {
  197. DBG("Creating embedded window..."); DBGF;
  198. puglInitWindowParent(fView, parentId);
  199. }
  200. else
  201. {
  202. DBG("Creating window without parent..."); DBGF;
  203. }
  204. init();
  205. if (fUsingEmbed)
  206. {
  207. DBG("NOTE: Embed window is always visible and non-resizable\n");
  208. puglShowWindow(fView);
  209. fApp.pData->oneShown();
  210. fFirstInit = false;
  211. }
  212. }
  213. void init()
  214. {
  215. if (fSelf == nullptr || fView == nullptr)
  216. {
  217. DBG("Failed!\n");
  218. return;
  219. }
  220. puglInitUserResizable(fView, fResizable);
  221. puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
  222. puglSetHandle(fView, this);
  223. puglSetDisplayFunc(fView, onDisplayCallback);
  224. puglSetKeyboardFunc(fView, onKeyboardCallback);
  225. puglSetMotionFunc(fView, onMotionCallback);
  226. puglSetMouseFunc(fView, onMouseCallback);
  227. puglSetScrollFunc(fView, onScrollCallback);
  228. puglSetSpecialFunc(fView, onSpecialCallback);
  229. puglSetReshapeFunc(fView, onReshapeCallback);
  230. puglSetCloseFunc(fView, onCloseCallback);
  231. #ifndef DGL_FILE_BROWSER_DISABLED
  232. puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
  233. #endif
  234. puglCreateWindow(fView, nullptr);
  235. PuglInternals* impl = fView->impl;
  236. #if defined(DISTRHO_OS_HAIKU)
  237. bWindow = impl->window;
  238. #elif defined(DISTRHO_OS_MAC)
  239. mView = impl->view;
  240. mWindow = impl->window;
  241. DISTRHO_SAFE_ASSERT(mView != nullptr);
  242. if (fUsingEmbed) {
  243. DISTRHO_SAFE_ASSERT(mWindow == nullptr);
  244. } else {
  245. DISTRHO_SAFE_ASSERT(mWindow != nullptr);
  246. }
  247. #elif defined(DISTRHO_OS_WINDOWS)
  248. hwnd = impl->hwnd;
  249. DISTRHO_SAFE_ASSERT(hwnd != 0);
  250. #else
  251. xDisplay = impl->display;
  252. xWindow = impl->win;
  253. DISTRHO_SAFE_ASSERT(xWindow != 0);
  254. if (! fUsingEmbed)
  255. {
  256. const pid_t pid = getpid();
  257. const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False);
  258. XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  259. const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False);
  260. // Setting the window to both dialog and normal will produce a decorated floating dialog
  261. // Order is important: DIALOG needs to come before NORMAL
  262. const Atom _wts[2] = {
  263. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  264. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  265. };
  266. XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  267. }
  268. #endif
  269. puglEnterContext(fView);
  270. fApp.pData->windows.push_back(fSelf);
  271. DBG("Success!\n");
  272. }
  273. ~PrivateData()
  274. {
  275. DBG("Destroying window..."); DBGF;
  276. if (fModal.enabled)
  277. {
  278. exec_fini();
  279. close();
  280. }
  281. fWidgets.clear();
  282. if (fUsingEmbed)
  283. {
  284. puglHideWindow(fView);
  285. fApp.pData->oneHidden();
  286. }
  287. if (fSelf != nullptr)
  288. {
  289. fApp.pData->windows.remove(fSelf);
  290. fSelf = nullptr;
  291. }
  292. if (fView != nullptr)
  293. {
  294. puglDestroy(fView);
  295. fView = nullptr;
  296. }
  297. if (fTitle != nullptr)
  298. {
  299. std::free(fTitle);
  300. fTitle = nullptr;
  301. }
  302. #if defined(DISTRHO_OS_HAIKU)
  303. #elif defined(DISTRHO_OS_MAC)
  304. mView = nullptr;
  305. mWindow = nullptr;
  306. #elif defined(DISTRHO_OS_WINDOWS)
  307. hwnd = 0;
  308. #else
  309. xDisplay = nullptr;
  310. xWindow = 0;
  311. #endif
  312. #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
  313. if (fOpenFilePanel)
  314. {
  315. [fOpenFilePanel release];
  316. fOpenFilePanel = nullptr;
  317. }
  318. if (fFilePanelDelegate)
  319. {
  320. [fFilePanelDelegate release];
  321. fFilePanelDelegate = nullptr;
  322. }
  323. #endif
  324. DBG("Success!\n");
  325. }
  326. // -------------------------------------------------------------------
  327. void close()
  328. {
  329. DBG("Window close\n");
  330. if (fUsingEmbed)
  331. return;
  332. setVisible(false);
  333. if (! fFirstInit)
  334. {
  335. fApp.pData->oneHidden();
  336. fFirstInit = true;
  337. }
  338. }
  339. void exec(const bool lockWait)
  340. {
  341. DBG("Window exec\n");
  342. exec_init();
  343. if (lockWait)
  344. {
  345. for (; fVisible && fModal.enabled;)
  346. {
  347. idle();
  348. d_msleep(10);
  349. }
  350. exec_fini();
  351. }
  352. else
  353. {
  354. idle();
  355. }
  356. }
  357. // -------------------------------------------------------------------
  358. void exec_init()
  359. {
  360. DBG("Window modal loop starting..."); DBGF;
  361. DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true));
  362. fModal.enabled = true;
  363. fModal.parent->fModal.childFocus = this;
  364. fModal.parent->setVisible(true);
  365. setVisible(true);
  366. DBG("Ok\n");
  367. }
  368. void exec_fini()
  369. {
  370. DBG("Window modal loop stopping..."); DBGF;
  371. fModal.enabled = false;
  372. if (fModal.parent != nullptr)
  373. {
  374. fModal.parent->fModal.childFocus = nullptr;
  375. // the mouse position probably changed since the modal appeared,
  376. // so send a mouse motion event to the modal's parent window
  377. #if defined(DISTRHO_OS_HAIKU)
  378. // TODO
  379. #elif defined(DISTRHO_OS_MAC)
  380. // TODO
  381. #elif defined(DISTRHO_OS_WINDOWS)
  382. // TODO
  383. #else
  384. int i, wx, wy;
  385. uint u;
  386. ::Window w;
  387. if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
  388. fModal.parent->onPuglMotion(wx, wy);
  389. #endif
  390. }
  391. DBG("Ok\n");
  392. }
  393. // -------------------------------------------------------------------
  394. void focus()
  395. {
  396. DBG("Window focus\n");
  397. #if defined(DISTRHO_OS_HAIKU)
  398. #elif defined(DISTRHO_OS_MAC)
  399. if (mWindow != nullptr)
  400. [mWindow makeKeyWindow];
  401. #elif defined(DISTRHO_OS_WINDOWS)
  402. SetForegroundWindow(hwnd);
  403. SetActiveWindow(hwnd);
  404. SetFocus(hwnd);
  405. #else
  406. XRaiseWindow(xDisplay, xWindow);
  407. XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
  408. XFlush(xDisplay);
  409. #endif
  410. }
  411. // -------------------------------------------------------------------
  412. void setVisible(const bool yesNo)
  413. {
  414. if (fVisible == yesNo)
  415. {
  416. DBG("Window setVisible matches current state, ignoring request\n");
  417. return;
  418. }
  419. if (fUsingEmbed)
  420. {
  421. DBG("Window setVisible cannot be called when embedded\n");
  422. return;
  423. }
  424. DBG("Window setVisible called\n");
  425. fVisible = yesNo;
  426. if (yesNo && fFirstInit)
  427. setSize(fWidth, fHeight, true);
  428. #if defined(DISTRHO_OS_HAIKU)
  429. // TODO
  430. #elif defined(DISTRHO_OS_MAC)
  431. if (yesNo)
  432. {
  433. if (mWindow != nullptr)
  434. {
  435. if (mParentWindow != nullptr)
  436. [mParentWindow addChildWindow:mWindow
  437. ordered:NSWindowAbove];
  438. [mWindow setIsVisible:YES];
  439. }
  440. else
  441. {
  442. [mView setHidden:NO];
  443. }
  444. }
  445. else
  446. {
  447. if (mWindow != nullptr)
  448. {
  449. if (mParentWindow != nullptr)
  450. [mParentWindow removeChildWindow:mWindow];
  451. [mWindow setIsVisible:NO];
  452. }
  453. else
  454. {
  455. [mView setHidden:YES];
  456. }
  457. }
  458. #elif defined(DISTRHO_OS_WINDOWS)
  459. if (yesNo)
  460. {
  461. if (fFirstInit)
  462. {
  463. RECT rectChild, rectParent;
  464. if (hwndParent != nullptr &&
  465. GetWindowRect(hwnd, &rectChild) &&
  466. GetWindowRect(hwndParent, &rectParent))
  467. {
  468. SetWindowPos(hwnd, hwndParent,
  469. rectParent.left + (rectChild.right-rectChild.left)/2,
  470. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  471. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  472. }
  473. else
  474. {
  475. ShowWindow(hwnd, SW_SHOWNORMAL);
  476. }
  477. }
  478. else
  479. {
  480. ShowWindow(hwnd, SW_RESTORE);
  481. }
  482. }
  483. else
  484. {
  485. ShowWindow(hwnd, SW_HIDE);
  486. }
  487. UpdateWindow(hwnd);
  488. #else
  489. if (yesNo)
  490. XMapRaised(xDisplay, xWindow);
  491. else
  492. XUnmapWindow(xDisplay, xWindow);
  493. XFlush(xDisplay);
  494. #endif
  495. if (yesNo)
  496. {
  497. if (fFirstInit)
  498. {
  499. fApp.pData->oneShown();
  500. fFirstInit = false;
  501. }
  502. }
  503. else if (fModal.enabled)
  504. exec_fini();
  505. }
  506. // -------------------------------------------------------------------
  507. void setResizable(const bool yesNo)
  508. {
  509. if (fResizable == yesNo)
  510. {
  511. DBG("Window setResizable matches current state, ignoring request\n");
  512. return;
  513. }
  514. if (fUsingEmbed)
  515. {
  516. DBG("Window setResizable cannot be called when embedded\n");
  517. return;
  518. }
  519. DBG("Window setResizable called\n");
  520. fResizable = yesNo;
  521. fView->user_resizable = yesNo;
  522. #if defined(DISTRHO_OS_HAIKU)
  523. // TODO
  524. #elif defined(DISTRHO_OS_MAC)
  525. const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0);
  526. [mView setAutoresizingMask:flags];
  527. #elif defined(DISTRHO_OS_WINDOWS)
  528. const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
  529. : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  530. SetWindowLong(hwnd, GWL_STYLE, winFlags);
  531. #endif
  532. setSize(fWidth, fHeight, true);
  533. }
  534. // -------------------------------------------------------------------
  535. void setGeometryConstraints(uint width, uint height, bool aspect)
  536. {
  537. // Did you forget to set DISTRHO_UI_USER_RESIZABLE ?
  538. DISTRHO_SAFE_ASSERT_RETURN(fResizable,);
  539. fView->min_width = width;
  540. fView->min_height = height;
  541. puglUpdateGeometryConstraints(fView, width, height, aspect);
  542. }
  543. // -------------------------------------------------------------------
  544. void setSize(uint width, uint height, const bool forced = false)
  545. {
  546. if (width <= 1 || height <= 1)
  547. {
  548. DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
  549. return;
  550. }
  551. if (fWidth == width && fHeight == height && ! forced)
  552. {
  553. DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
  554. return;
  555. }
  556. fWidth = width;
  557. fHeight = height;
  558. DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
  559. #if defined(DISTRHO_OS_HAIKU)
  560. // TODO
  561. #elif defined(DISTRHO_OS_MAC)
  562. [mView setFrame:NSMakeRect(0, 0, width, height)];
  563. if (mWindow != nullptr)
  564. {
  565. const NSSize size = NSMakeSize(width, height);
  566. [mWindow setContentSize:size];
  567. if (fResizable)
  568. {
  569. [mWindow setContentMinSize:NSMakeSize(1, 1)];
  570. [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  571. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
  572. }
  573. else
  574. {
  575. [mWindow setContentMinSize:size];
  576. [mWindow setContentMaxSize:size];
  577. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  578. }
  579. }
  580. #elif defined(DISTRHO_OS_WINDOWS)
  581. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
  582. RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
  583. AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
  584. SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  585. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  586. if (! forced)
  587. UpdateWindow(hwnd);
  588. #else
  589. if (! fResizable)
  590. {
  591. XSizeHints sizeHints;
  592. memset(&sizeHints, 0, sizeof(sizeHints));
  593. sizeHints.flags = PSize|PMinSize|PMaxSize;
  594. sizeHints.width = static_cast<int>(width);
  595. sizeHints.height = static_cast<int>(height);
  596. sizeHints.min_width = static_cast<int>(width);
  597. sizeHints.min_height = static_cast<int>(height);
  598. sizeHints.max_width = static_cast<int>(width);
  599. sizeHints.max_height = static_cast<int>(height);
  600. XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
  601. }
  602. XResizeWindow(xDisplay, xWindow, width, height);
  603. if (! forced)
  604. XFlush(xDisplay);
  605. #endif
  606. puglPostRedisplay(fView);
  607. }
  608. // -------------------------------------------------------------------
  609. const char* getTitle() const noexcept
  610. {
  611. static const char* const kFallback = "";
  612. return fTitle != nullptr ? fTitle : kFallback;
  613. }
  614. void setTitle(const char* const title)
  615. {
  616. DBGp("Window setTitle \"%s\"\n", title);
  617. if (fTitle != nullptr)
  618. std::free(fTitle);
  619. fTitle = strdup(title);
  620. #if defined(DISTRHO_OS_HAIKU)
  621. if (bWindow != nullptr)
  622. bWindow->SetTitle(title);
  623. #elif defined(DISTRHO_OS_MAC)
  624. if (mWindow != nullptr)
  625. {
  626. NSString* titleString = [[NSString alloc]
  627. initWithBytes:title
  628. length:strlen(title)
  629. encoding:NSUTF8StringEncoding];
  630. [mWindow setTitle:titleString];
  631. }
  632. #elif defined(DISTRHO_OS_WINDOWS)
  633. SetWindowTextA(hwnd, title);
  634. #else
  635. XStoreName(xDisplay, xWindow, title);
  636. Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False);
  637. Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False);
  638. XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title));
  639. #endif
  640. }
  641. void setTransientWinId(const uintptr_t winId)
  642. {
  643. DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
  644. #if defined(DISTRHO_OS_HAIKU)
  645. // TODO
  646. #elif defined(DISTRHO_OS_MAC)
  647. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  648. DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  649. [parentWindow addChildWindow:mWindow
  650. ordered:NSWindowAbove];
  651. #elif defined(DISTRHO_OS_WINDOWS)
  652. hwndParent = (HWND)winId;
  653. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId);
  654. #else
  655. XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
  656. #endif
  657. }
  658. // -------------------------------------------------------------------
  659. double getScaling() const noexcept
  660. {
  661. return fScaling;
  662. }
  663. void setAutoScaling(const double scaling) noexcept
  664. {
  665. DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,);
  666. fAutoScaling = scaling;
  667. }
  668. // -------------------------------------------------------------------
  669. bool getIgnoringKeyRepeat() const noexcept
  670. {
  671. return fView->ignoreKeyRepeat;
  672. }
  673. void setIgnoringKeyRepeat(bool ignore) noexcept
  674. {
  675. puglIgnoreKeyRepeat(fView, ignore);
  676. }
  677. // -------------------------------------------------------------------
  678. void addWidget(Widget* const widget)
  679. {
  680. fWidgets.push_back(widget);
  681. }
  682. void removeWidget(Widget* const widget)
  683. {
  684. fWidgets.remove(widget);
  685. }
  686. void idle()
  687. {
  688. puglProcessEvents(fView);
  689. #ifdef DISTRHO_OS_MAC
  690. if (fNeedsIdle)
  691. {
  692. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  693. NSEvent* event;
  694. for (;;)
  695. {
  696. event = [NSApp
  697. nextEventMatchingMask:NSAnyEventMask
  698. untilDate:[NSDate distantPast]
  699. inMode:NSDefaultRunLoopMode
  700. dequeue:YES];
  701. if (event == nil)
  702. break;
  703. [NSApp sendEvent: event];
  704. }
  705. [pool release];
  706. }
  707. #endif
  708. #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED)
  709. if (fSelectedFile.isNotEmpty())
  710. {
  711. char* const buffer = fSelectedFile.getAndReleaseBuffer();
  712. fView->fileSelectedFunc(fView, buffer);
  713. std::free(buffer);
  714. }
  715. #endif
  716. if (fModal.enabled && fModal.parent != nullptr)
  717. fModal.parent->idle();
  718. }
  719. // -------------------------------------------------------------------
  720. void onPuglDisplay()
  721. {
  722. fSelf->onDisplayBefore();
  723. FOR_EACH_WIDGET(it)
  724. {
  725. Widget* const widget(*it);
  726. widget->pData->display(fWidth, fHeight, fAutoScaling, false);
  727. }
  728. fSelf->onDisplayAfter();
  729. }
  730. int onPuglKeyboard(const bool press, const uint key)
  731. {
  732. DBGp("PUGL: onKeyboard : %i %i\n", press, key);
  733. if (fModal.childFocus != nullptr)
  734. {
  735. fModal.childFocus->focus();
  736. return 0;
  737. }
  738. Widget::KeyboardEvent ev;
  739. ev.press = press;
  740. ev.key = key;
  741. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  742. ev.time = puglGetEventTimestamp(fView);
  743. FOR_EACH_WIDGET_INV(rit)
  744. {
  745. Widget* const widget(*rit);
  746. if (widget->isVisible() && widget->onKeyboard(ev))
  747. return 0;
  748. }
  749. return 1;
  750. }
  751. int onPuglSpecial(const bool press, const Key key)
  752. {
  753. DBGp("PUGL: onSpecial : %i %i\n", press, key);
  754. if (fModal.childFocus != nullptr)
  755. {
  756. fModal.childFocus->focus();
  757. return 0;
  758. }
  759. Widget::SpecialEvent ev;
  760. ev.press = press;
  761. ev.key = key;
  762. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  763. ev.time = puglGetEventTimestamp(fView);
  764. FOR_EACH_WIDGET_INV(rit)
  765. {
  766. Widget* const widget(*rit);
  767. if (widget->isVisible() && widget->onSpecial(ev))
  768. return 0;
  769. }
  770. return 1;
  771. }
  772. void onPuglMouse(const int button, const bool press, int x, int y)
  773. {
  774. DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
  775. // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
  776. if (press && button == 0 && x == 0 && y == 0) return;
  777. if (fModal.childFocus != nullptr)
  778. return fModal.childFocus->focus();
  779. x /= fAutoScaling;
  780. y /= fAutoScaling;
  781. Widget::MouseEvent ev;
  782. ev.button = button;
  783. ev.press = press;
  784. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  785. ev.time = puglGetEventTimestamp(fView);
  786. FOR_EACH_WIDGET_INV(rit)
  787. {
  788. Widget* const widget(*rit);
  789. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  790. if (widget->isVisible() && widget->onMouse(ev))
  791. break;
  792. }
  793. }
  794. void onPuglMotion(int x, int y)
  795. {
  796. // DBGp("PUGL: onMotion : %i %i\n", x, y);
  797. if (fModal.childFocus != nullptr)
  798. return;
  799. x /= fAutoScaling;
  800. y /= fAutoScaling;
  801. Widget::MotionEvent ev;
  802. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  803. ev.time = puglGetEventTimestamp(fView);
  804. FOR_EACH_WIDGET_INV(rit)
  805. {
  806. Widget* const widget(*rit);
  807. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  808. if (widget->isVisible() && widget->onMotion(ev))
  809. break;
  810. }
  811. }
  812. void onPuglScroll(int x, int y, float dx, float dy)
  813. {
  814. DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
  815. if (fModal.childFocus != nullptr)
  816. return;
  817. x /= fAutoScaling;
  818. y /= fAutoScaling;
  819. dx /= fAutoScaling;
  820. dy /= fAutoScaling;
  821. Widget::ScrollEvent ev;
  822. ev.delta = Point<float>(dx, dy);
  823. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  824. ev.time = puglGetEventTimestamp(fView);
  825. FOR_EACH_WIDGET_INV(rit)
  826. {
  827. Widget* const widget(*rit);
  828. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  829. if (widget->isVisible() && widget->onScroll(ev))
  830. break;
  831. }
  832. }
  833. void onPuglReshape(const int width, const int height)
  834. {
  835. DBGp("PUGL: onReshape : %i %i\n", width, height);
  836. if (width <= 1 && height <= 1)
  837. return;
  838. fWidth = static_cast<uint>(width);
  839. fHeight = static_cast<uint>(height);
  840. fSelf->onReshape(fWidth, fHeight);
  841. FOR_EACH_WIDGET(it)
  842. {
  843. Widget* const widget(*it);
  844. if (widget->pData->needsFullViewport)
  845. widget->setSize(fWidth, fHeight);
  846. }
  847. }
  848. void onPuglClose()
  849. {
  850. DBG("PUGL: onClose\n");
  851. if (fModal.enabled)
  852. exec_fini();
  853. fSelf->onClose();
  854. if (fModal.childFocus != nullptr)
  855. fModal.childFocus->fSelf->onClose();
  856. close();
  857. }
  858. // -------------------------------------------------------------------
  859. bool handlePluginKeyboard(const bool press, const uint key)
  860. {
  861. DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
  862. if (fModal.childFocus != nullptr)
  863. {
  864. fModal.childFocus->focus();
  865. return true;
  866. }
  867. Widget::KeyboardEvent ev;
  868. ev.press = press;
  869. ev.key = key;
  870. ev.mod = static_cast<Modifier>(fView->mods);
  871. ev.time = 0;
  872. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  873. ev.key -= 'a' - 'A'; // a-z -> A-Z
  874. FOR_EACH_WIDGET_INV(rit)
  875. {
  876. Widget* const widget(*rit);
  877. if (widget->isVisible() && widget->onKeyboard(ev))
  878. return true;
  879. }
  880. return false;
  881. }
  882. bool handlePluginSpecial(const bool press, const Key key)
  883. {
  884. DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
  885. if (fModal.childFocus != nullptr)
  886. {
  887. fModal.childFocus->focus();
  888. return true;
  889. }
  890. int mods = 0x0;
  891. switch (key)
  892. {
  893. case kKeyShift:
  894. mods |= kModifierShift;
  895. break;
  896. case kKeyControl:
  897. mods |= kModifierControl;
  898. break;
  899. case kKeyAlt:
  900. mods |= kModifierAlt;
  901. break;
  902. default:
  903. break;
  904. }
  905. if (mods != 0x0)
  906. {
  907. if (press)
  908. fView->mods |= mods;
  909. else
  910. fView->mods &= ~(mods);
  911. }
  912. Widget::SpecialEvent ev;
  913. ev.press = press;
  914. ev.key = key;
  915. ev.mod = static_cast<Modifier>(fView->mods);
  916. ev.time = 0;
  917. FOR_EACH_WIDGET_INV(rit)
  918. {
  919. Widget* const widget(*rit);
  920. if (widget->isVisible() && widget->onSpecial(ev))
  921. return true;
  922. }
  923. return false;
  924. }
  925. #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
  926. static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData)
  927. {
  928. PrivateData* pData = (PrivateData*)userData;
  929. if (returnCode == NSOKButton)
  930. {
  931. NSArray* urls = [panel URLs];
  932. NSURL* fileUrl = nullptr;
  933. for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i)
  934. {
  935. NSURL* url = (NSURL*)[urls objectAtIndex:i];
  936. if ([url isFileURL])
  937. fileUrl = url;
  938. }
  939. if (fileUrl)
  940. {
  941. PuglView* view = pData->fView;
  942. if (view->fileSelectedFunc)
  943. {
  944. const char* fileName = [fileUrl.path UTF8String];
  945. view->fileSelectedFunc(view, fileName);
  946. }
  947. }
  948. }
  949. [pData->fOpenFilePanel release];
  950. pData->fOpenFilePanel = nullptr;
  951. }
  952. #endif
  953. // -------------------------------------------------------------------
  954. Application& fApp;
  955. Window* fSelf;
  956. GraphicsContext fContext;
  957. PuglView* fView;
  958. bool fFirstInit;
  959. bool fVisible;
  960. bool fResizable;
  961. bool fUsingEmbed;
  962. uint fWidth;
  963. uint fHeight;
  964. double fScaling;
  965. double fAutoScaling;
  966. char* fTitle;
  967. std::list<Widget*> fWidgets;
  968. struct Modal {
  969. bool enabled;
  970. PrivateData* parent;
  971. PrivateData* childFocus;
  972. Modal()
  973. : enabled(false),
  974. parent(nullptr),
  975. childFocus(nullptr) {}
  976. Modal(PrivateData* const p)
  977. : enabled(false),
  978. parent(p),
  979. childFocus(nullptr) {}
  980. ~Modal()
  981. {
  982. DISTRHO_SAFE_ASSERT(! enabled);
  983. DISTRHO_SAFE_ASSERT(childFocus == nullptr);
  984. }
  985. DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
  986. } fModal;
  987. #if defined(DISTRHO_OS_HAIKU)
  988. BWindow* bWindow;
  989. #elif defined(DISTRHO_OS_MAC)
  990. bool fNeedsIdle;
  991. NSView<PuglGenericView>* mView;
  992. id mWindow;
  993. id mParentWindow;
  994. # ifndef DGL_FILE_BROWSER_DISABLED
  995. NSOpenPanel* fOpenFilePanel;
  996. id fFilePanelDelegate;
  997. # endif
  998. #elif defined(DISTRHO_OS_WINDOWS)
  999. HWND hwnd;
  1000. HWND hwndParent;
  1001. # ifndef DGL_FILE_BROWSER_DISABLED
  1002. String fSelectedFile;
  1003. # endif
  1004. #else
  1005. Display* xDisplay;
  1006. ::Window xWindow;
  1007. #endif
  1008. // -------------------------------------------------------------------
  1009. // Callbacks
  1010. #define handlePtr ((PrivateData*)puglGetHandle(view))
  1011. static void onDisplayCallback(PuglView* view)
  1012. {
  1013. handlePtr->onPuglDisplay();
  1014. }
  1015. static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
  1016. {
  1017. return handlePtr->onPuglKeyboard(press, key);
  1018. }
  1019. static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
  1020. {
  1021. return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
  1022. }
  1023. static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
  1024. {
  1025. handlePtr->onPuglMouse(button, press, x, y);
  1026. }
  1027. static void onMotionCallback(PuglView* view, int x, int y)
  1028. {
  1029. handlePtr->onPuglMotion(x, y);
  1030. }
  1031. static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
  1032. {
  1033. handlePtr->onPuglScroll(x, y, dx, dy);
  1034. }
  1035. static void onReshapeCallback(PuglView* view, int width, int height)
  1036. {
  1037. handlePtr->onPuglReshape(width, height);
  1038. }
  1039. static void onCloseCallback(PuglView* view)
  1040. {
  1041. handlePtr->onPuglClose();
  1042. }
  1043. #ifndef DGL_FILE_BROWSER_DISABLED
  1044. static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
  1045. {
  1046. handlePtr->fSelf->fileBrowserSelected(filename);
  1047. }
  1048. #endif
  1049. #undef handlePtr
  1050. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  1051. };
  1052. // -----------------------------------------------------------------------
  1053. // Window
  1054. Window::Window(Application& app)
  1055. : pData(new PrivateData(app, this)) {}
  1056. Window::Window(Application& app, Window& parent)
  1057. : pData(new PrivateData(app, this, parent)) {}
  1058. Window::Window(Application& app, intptr_t parentId, double scaling, bool resizable)
  1059. : pData(new PrivateData(app, this, parentId, scaling, resizable)) {}
  1060. Window::~Window()
  1061. {
  1062. delete pData;
  1063. }
  1064. void Window::show()
  1065. {
  1066. pData->setVisible(true);
  1067. }
  1068. void Window::hide()
  1069. {
  1070. pData->setVisible(false);
  1071. }
  1072. void Window::close()
  1073. {
  1074. pData->close();
  1075. }
  1076. void Window::exec(bool lockWait)
  1077. {
  1078. pData->exec(lockWait);
  1079. }
  1080. void Window::focus()
  1081. {
  1082. pData->focus();
  1083. }
  1084. void Window::repaint() noexcept
  1085. {
  1086. puglPostRedisplay(pData->fView);
  1087. }
  1088. // static int fib_filter_filename_filter(const char* const name)
  1089. // {
  1090. // return 1;
  1091. // (void)name;
  1092. // }
  1093. #ifndef DGL_FILE_BROWSER_DISABLED
  1094. #ifdef DISTRHO_OS_MAC
  1095. END_NAMESPACE_DGL
  1096. @interface FilePanelDelegate : NSObject
  1097. {
  1098. void (*fCallback)(NSOpenPanel*, int, void*);
  1099. void* fUserData;
  1100. }
  1101. -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData;
  1102. -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
  1103. @end
  1104. START_NAMESPACE_DGL
  1105. #endif
  1106. bool Window::openFileBrowser(const FileBrowserOptions& options)
  1107. {
  1108. # ifdef SOFD_HAVE_X11
  1109. using DISTRHO_NAMESPACE::String;
  1110. // --------------------------------------------------------------------------
  1111. // configure start dir
  1112. // TODO: get abspath if needed
  1113. // TODO: cross-platform
  1114. String startDir(options.startDir);
  1115. # ifdef DISTRHO_OS_LINUX
  1116. if (startDir.isEmpty())
  1117. {
  1118. if (char* const dir_name = get_current_dir_name())
  1119. {
  1120. startDir = dir_name;
  1121. std::free(dir_name);
  1122. }
  1123. }
  1124. # endif
  1125. DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
  1126. if (! startDir.endsWith('/'))
  1127. startDir += "/";
  1128. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
  1129. // --------------------------------------------------------------------------
  1130. // configure title
  1131. String title(options.title);
  1132. if (title.isEmpty())
  1133. {
  1134. title = pData->getTitle();
  1135. if (title.isEmpty())
  1136. title = "FileBrowser";
  1137. }
  1138. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
  1139. // --------------------------------------------------------------------------
  1140. // configure filters
  1141. x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
  1142. // --------------------------------------------------------------------------
  1143. // configure buttons
  1144. x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
  1145. x_fib_cfg_buttons(1, options.buttons.showHidden-1);
  1146. x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
  1147. // --------------------------------------------------------------------------
  1148. // show
  1149. return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
  1150. # elif defined(DISTRHO_OS_WINDOWS)
  1151. // the old and compatible dialog API
  1152. OPENFILENAMEW ofn;
  1153. memset(&ofn, 0, sizeof(ofn));
  1154. ofn.lStructSize = sizeof(ofn);
  1155. ofn.hwndOwner = pData->hwnd;
  1156. // set initial directory in UTF-16 coding
  1157. std::vector<WCHAR> startDirW;
  1158. if (options.startDir)
  1159. {
  1160. startDirW.resize(strlen(options.startDir) + 1);
  1161. if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size()))
  1162. ofn.lpstrInitialDir = startDirW.data();
  1163. }
  1164. // set title in UTF-16 coding
  1165. std::vector<WCHAR> titleW;
  1166. if (options.title)
  1167. {
  1168. titleW.resize(strlen(options.title) + 1);
  1169. if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size()))
  1170. ofn.lpstrTitle = titleW.data();
  1171. }
  1172. // prepare a buffer to receive the result
  1173. std::vector<WCHAR> fileNameW(32768); // the Unicode maximum
  1174. ofn.lpstrFile = fileNameW.data();
  1175. ofn.nMaxFile = (DWORD)fileNameW.size();
  1176. // TODO synchronous only, can't do better with WinAPI native dialogs.
  1177. // threading might work, if someone is motivated to risk it.
  1178. if (GetOpenFileNameW(&ofn))
  1179. {
  1180. // back to UTF-8
  1181. std::vector<char> fileNameA(4 * 32768);
  1182. if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr))
  1183. {
  1184. // handle it during the next idle cycle (fake async)
  1185. pData->fSelectedFile = fileNameA.data();
  1186. }
  1187. }
  1188. return true;
  1189. # elif defined(DISTRHO_OS_MAC)
  1190. if (pData->fOpenFilePanel) // permit one dialog at most
  1191. {
  1192. [pData->fOpenFilePanel makeKeyAndOrderFront:nil];
  1193. return false;
  1194. }
  1195. NSOpenPanel* panel = [NSOpenPanel openPanel];
  1196. pData->fOpenFilePanel = [panel retain];
  1197. [panel setCanChooseFiles:YES];
  1198. [panel setCanChooseDirectories:NO];
  1199. [panel setAllowsMultipleSelection:NO];
  1200. if (options.startDir)
  1201. [panel setDirectory:[NSString stringWithUTF8String:options.startDir]];
  1202. if (options.title)
  1203. {
  1204. NSString* titleString = [[NSString alloc]
  1205. initWithBytes:options.title
  1206. length:strlen(options.title)
  1207. encoding:NSUTF8StringEncoding];
  1208. [panel setTitle:titleString];
  1209. }
  1210. id delegate = pData->fFilePanelDelegate;
  1211. if (!delegate)
  1212. {
  1213. delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd
  1214. userData:pData];
  1215. pData->fFilePanelDelegate = [delegate retain];
  1216. }
  1217. [panel beginSheetForDirectory:nullptr
  1218. file:nullptr
  1219. modalForWindow:nullptr
  1220. modalDelegate:delegate
  1221. didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
  1222. contextInfo:nullptr];
  1223. return true;
  1224. # else
  1225. // not implemented
  1226. return false;
  1227. // unused
  1228. (void)options;
  1229. # endif
  1230. }
  1231. #ifdef DISTRHO_OS_MAC
  1232. END_NAMESPACE_DGL
  1233. @implementation FilePanelDelegate
  1234. -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData
  1235. {
  1236. [super init];
  1237. self->fCallback = callback;
  1238. self->fUserData = userData;
  1239. return self;
  1240. }
  1241. -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
  1242. {
  1243. self->fCallback(sheet, returnCode, self->fUserData);
  1244. (void)contextInfo;
  1245. }
  1246. @end
  1247. START_NAMESPACE_DGL
  1248. #endif
  1249. #endif // !defined(DGL_FILE_BROWSER_DISABLED)
  1250. bool Window::isEmbed() const noexcept
  1251. {
  1252. return pData->fUsingEmbed;
  1253. }
  1254. bool Window::isVisible() const noexcept
  1255. {
  1256. return pData->fVisible;
  1257. }
  1258. void Window::setVisible(bool yesNo)
  1259. {
  1260. pData->setVisible(yesNo);
  1261. }
  1262. bool Window::isResizable() const noexcept
  1263. {
  1264. return pData->fResizable;
  1265. }
  1266. void Window::setResizable(bool yesNo)
  1267. {
  1268. pData->setResizable(yesNo);
  1269. }
  1270. void Window::setGeometryConstraints(uint width, uint height, bool aspect)
  1271. {
  1272. pData->setGeometryConstraints(width, height, aspect);
  1273. }
  1274. uint Window::getWidth() const noexcept
  1275. {
  1276. return pData->fWidth;
  1277. }
  1278. uint Window::getHeight() const noexcept
  1279. {
  1280. return pData->fHeight;
  1281. }
  1282. Size<uint> Window::getSize() const noexcept
  1283. {
  1284. return Size<uint>(pData->fWidth, pData->fHeight);
  1285. }
  1286. void Window::setSize(uint width, uint height)
  1287. {
  1288. pData->setSize(width, height);
  1289. }
  1290. void Window::setSize(Size<uint> size)
  1291. {
  1292. pData->setSize(size.getWidth(), size.getHeight());
  1293. }
  1294. const char* Window::getTitle() const noexcept
  1295. {
  1296. return pData->getTitle();
  1297. }
  1298. void Window::setTitle(const char* title)
  1299. {
  1300. pData->setTitle(title);
  1301. }
  1302. void Window::setTransientWinId(uintptr_t winId)
  1303. {
  1304. pData->setTransientWinId(winId);
  1305. }
  1306. double Window::getScaling() const noexcept
  1307. {
  1308. return pData->getScaling();
  1309. }
  1310. bool Window::getIgnoringKeyRepeat() const noexcept
  1311. {
  1312. return pData->getIgnoringKeyRepeat();
  1313. }
  1314. void Window::setIgnoringKeyRepeat(bool ignore) noexcept
  1315. {
  1316. pData->setIgnoringKeyRepeat(ignore);
  1317. }
  1318. Application& Window::getApp() const noexcept
  1319. {
  1320. return pData->fApp;
  1321. }
  1322. intptr_t Window::getWindowId() const noexcept
  1323. {
  1324. return puglGetNativeWindow(pData->fView);
  1325. }
  1326. const GraphicsContext& Window::getGraphicsContext() const noexcept
  1327. {
  1328. GraphicsContext& context = pData->fContext;
  1329. #ifdef DGL_CAIRO
  1330. context.cairo = (cairo_t*)puglGetContext(pData->fView);
  1331. #endif
  1332. return context;
  1333. }
  1334. void Window::_setAutoScaling(double scaling) noexcept
  1335. {
  1336. pData->setAutoScaling(scaling);
  1337. }
  1338. void Window::_addWidget(Widget* const widget)
  1339. {
  1340. pData->addWidget(widget);
  1341. }
  1342. void Window::_removeWidget(Widget* const widget)
  1343. {
  1344. pData->removeWidget(widget);
  1345. }
  1346. void Window::_idle()
  1347. {
  1348. pData->idle();
  1349. }
  1350. // -----------------------------------------------------------------------
  1351. void Window::addIdleCallback(IdleCallback* const callback)
  1352. {
  1353. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1354. pData->fApp.pData->idleCallbacks.push_back(callback);
  1355. }
  1356. void Window::removeIdleCallback(IdleCallback* const callback)
  1357. {
  1358. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1359. pData->fApp.pData->idleCallbacks.remove(callback);
  1360. }
  1361. // -----------------------------------------------------------------------
  1362. void Window::onDisplayBefore()
  1363. {
  1364. #ifdef DGL_OPENGL
  1365. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1366. glLoadIdentity();
  1367. #endif
  1368. }
  1369. void Window::onDisplayAfter()
  1370. {
  1371. }
  1372. void Window::onReshape(uint width, uint height)
  1373. {
  1374. #ifdef DGL_OPENGL
  1375. glEnable(GL_BLEND);
  1376. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1377. glMatrixMode(GL_PROJECTION);
  1378. glLoadIdentity();
  1379. glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
  1380. glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
  1381. glMatrixMode(GL_MODELVIEW);
  1382. glLoadIdentity();
  1383. #endif
  1384. }
  1385. void Window::onClose()
  1386. {
  1387. }
  1388. #ifndef DGL_FILE_BROWSER_DISABLED
  1389. void Window::fileBrowserSelected(const char*)
  1390. {
  1391. }
  1392. #endif
  1393. bool Window::handlePluginKeyboard(const bool press, const uint key)
  1394. {
  1395. return pData->handlePluginKeyboard(press, key);
  1396. }
  1397. bool Window::handlePluginSpecial(const bool press, const Key key)
  1398. {
  1399. return pData->handlePluginSpecial(press, key);
  1400. }
  1401. // -----------------------------------------------------------------------
  1402. StandaloneWindow::StandaloneWindow()
  1403. : Application(),
  1404. Window((Application&)*this),
  1405. fWidget(nullptr) {}
  1406. void StandaloneWindow::exec()
  1407. {
  1408. Window::show();
  1409. Application::exec();
  1410. }
  1411. void StandaloneWindow::onReshape(uint width, uint height)
  1412. {
  1413. if (fWidget != nullptr)
  1414. fWidget->setSize(width, height);
  1415. Window::onReshape(width, height);
  1416. }
  1417. void StandaloneWindow::_addWidget(Widget* widget)
  1418. {
  1419. if (fWidget == nullptr)
  1420. {
  1421. fWidget = widget;
  1422. fWidget->pData->needsFullViewport = true;
  1423. }
  1424. Window::_addWidget(widget);
  1425. }
  1426. void StandaloneWindow::_removeWidget(Widget* widget)
  1427. {
  1428. if (fWidget == widget)
  1429. {
  1430. fWidget->pData->needsFullViewport = false;
  1431. fWidget = nullptr;
  1432. }
  1433. Window::_removeWidget(widget);
  1434. }
  1435. // -----------------------------------------------------------------------
  1436. END_NAMESPACE_DGL
  1437. #undef DBG
  1438. #undef DBGF