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.

1827 lines
46KB

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