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.

1352 lines
35KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
  17. #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
  18. #include "../Window.hpp"
  19. #include "ApplicationPrivateData.hpp"
  20. #include "pugl-upstream/pugl/pugl.h"
  21. #include <vector>
  22. #define FOR_EACH_WIDGET(it) \
  23. for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
  24. #define FOR_EACH_WIDGET_INV(rit) \
  25. for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
  26. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  27. # define DBG(msg) std::fprintf(stderr, "%s", msg);
  28. # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  29. # define DBGF std::fflush(stderr);
  30. #else
  31. # define DBG(msg)
  32. # define DBGp(...)
  33. # define DBGF
  34. #endif
  35. START_NAMESPACE_DGL
  36. // -----------------------------------------------------------------------
  37. struct Window::PrivateData {
  38. Application& fApp;
  39. Window* fSelf;
  40. PuglView* fView;
  41. // this one depends on build type
  42. // GraphicsContext fContext;
  43. // Fallback build-specific Window functions
  44. struct Fallback {
  45. static void onDisplayBefore();
  46. static void onDisplayAfter();
  47. static void onReshape(uint width, uint height);
  48. };
  49. PrivateData(Application& app, Window* const self)
  50. : fApp(app),
  51. fSelf(self)
  52. {
  53. }
  54. PrivateData(Application& app, Window* const self, Window& parent)
  55. : fApp(app),
  56. fSelf(self)
  57. {
  58. }
  59. PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable)
  60. : fApp(app),
  61. fSelf(self)
  62. {
  63. }
  64. DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)
  65. };
  66. // -----------------------------------------------------------------------
  67. END_NAMESPACE_DGL
  68. #endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
  69. #if 0
  70. // -----------------------------------------------------------------------
  71. // Window Private
  72. struct Window::PrivateData {
  73. PrivateData(Application& app, Window* const self)
  74. : fApp(app),
  75. fSelf(self),
  76. fView(puglInit()),
  77. fFirstInit(true),
  78. fVisible(false),
  79. fResizable(true),
  80. fUsingEmbed(false),
  81. fWidth(1),
  82. fHeight(1),
  83. fScaling(1.0),
  84. fAutoScaling(1.0),
  85. fTitle(nullptr),
  86. fWidgets(),
  87. fModal(),
  88. #if defined(DISTRHO_OS_HAIKU)
  89. bApplication(nullptr),
  90. bView(nullptr),
  91. bWindow(nullptr)
  92. #elif defined(DISTRHO_OS_MAC)
  93. fNeedsIdle(true),
  94. mView(nullptr),
  95. mWindow(nullptr),
  96. mParentWindow(nullptr)
  97. # ifndef DGL_FILE_BROWSER_DISABLED
  98. , fOpenFilePanel(nullptr),
  99. fFilePanelDelegate(nullptr)
  100. # endif
  101. #elif defined(DISTRHO_OS_WINDOWS)
  102. hwnd(nullptr),
  103. hwndParent(nullptr)
  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. bApplication(nullptr),
  129. bView(nullptr),
  130. bWindow(nullptr)
  131. #elif defined(DISTRHO_OS_MAC)
  132. fNeedsIdle(false),
  133. mView(nullptr),
  134. mWindow(nullptr),
  135. mParentWindow(nullptr)
  136. # ifndef DGL_FILE_BROWSER_DISABLED
  137. , fOpenFilePanel(nullptr),
  138. fFilePanelDelegate(nullptr)
  139. # endif
  140. #elif defined(DISTRHO_OS_WINDOWS)
  141. hwnd(nullptr),
  142. hwndParent(nullptr)
  143. #else
  144. xDisplay(nullptr),
  145. xWindow(0)
  146. #endif
  147. {
  148. DBG("Creating window with parent..."); DBGF;
  149. init();
  150. const PuglInternals* const parentImpl(parent.pData->fView->impl);
  151. // NOTE: almost a 1:1 copy of setTransientWinId()
  152. #if defined(DISTRHO_OS_HAIKU)
  153. // TODO
  154. #elif defined(DISTRHO_OS_MAC)
  155. mParentWindow = parentImpl->window;
  156. #elif defined(DISTRHO_OS_WINDOWS)
  157. hwndParent = parentImpl->hwnd;
  158. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent);
  159. #else
  160. XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
  161. #endif
  162. }
  163. PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable)
  164. : fApp(app),
  165. fSelf(self),
  166. fView(puglInit()),
  167. fFirstInit(true),
  168. fVisible(parentId != 0),
  169. fResizable(resizable),
  170. fUsingEmbed(parentId != 0),
  171. fWidth(1),
  172. fHeight(1),
  173. fScaling(scaling),
  174. fAutoScaling(1.0),
  175. fTitle(nullptr),
  176. fWidgets(),
  177. fModal(),
  178. #if defined(DISTRHO_OS_HAIKU)
  179. bApplication(nullptr),
  180. bView(nullptr),
  181. bWindow(nullptr)
  182. #elif defined(DISTRHO_OS_MAC)
  183. fNeedsIdle(parentId == 0),
  184. mView(nullptr),
  185. mWindow(nullptr),
  186. mParentWindow(nullptr)
  187. # ifndef DGL_FILE_BROWSER_DISABLED
  188. , fOpenFilePanel(nullptr),
  189. fFilePanelDelegate(nullptr)
  190. # endif
  191. #elif defined(DISTRHO_OS_WINDOWS)
  192. hwnd(nullptr),
  193. hwndParent(nullptr)
  194. #else
  195. xDisplay(nullptr),
  196. xWindow(0)
  197. #endif
  198. {
  199. if (fUsingEmbed)
  200. {
  201. DBG("Creating embedded window..."); DBGF;
  202. puglInitWindowParent(fView, parentId);
  203. }
  204. else
  205. {
  206. DBG("Creating window without parent..."); DBGF;
  207. }
  208. init();
  209. if (fUsingEmbed)
  210. {
  211. DBG("NOTE: Embed window is always visible and non-resizable\n");
  212. puglShowWindow(fView);
  213. fApp.pData->oneShown();
  214. fFirstInit = false;
  215. }
  216. }
  217. void init()
  218. {
  219. if (fSelf == nullptr || fView == nullptr)
  220. {
  221. DBG("Failed!\n");
  222. return;
  223. }
  224. puglInitUserResizable(fView, fResizable);
  225. puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
  226. puglSetHandle(fView, this);
  227. puglSetDisplayFunc(fView, onDisplayCallback);
  228. puglSetKeyboardFunc(fView, onKeyboardCallback);
  229. puglSetMotionFunc(fView, onMotionCallback);
  230. puglSetMouseFunc(fView, onMouseCallback);
  231. puglSetScrollFunc(fView, onScrollCallback);
  232. puglSetSpecialFunc(fView, onSpecialCallback);
  233. puglSetReshapeFunc(fView, onReshapeCallback);
  234. puglSetCloseFunc(fView, onCloseCallback);
  235. #ifndef DGL_FILE_BROWSER_DISABLED
  236. puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
  237. #endif
  238. puglCreateWindow(fView, nullptr);
  239. PuglInternals* impl = fView->impl;
  240. #if defined(DISTRHO_OS_HAIKU)
  241. bApplication = impl->app;
  242. bView = impl->view;
  243. bWindow = impl->window;
  244. #elif defined(DISTRHO_OS_MAC)
  245. mView = impl->view;
  246. mWindow = impl->window;
  247. DISTRHO_SAFE_ASSERT(mView != nullptr);
  248. if (fUsingEmbed) {
  249. DISTRHO_SAFE_ASSERT(mWindow == nullptr);
  250. } else {
  251. DISTRHO_SAFE_ASSERT(mWindow != nullptr);
  252. }
  253. #elif defined(DISTRHO_OS_WINDOWS)
  254. hwnd = impl->hwnd;
  255. DISTRHO_SAFE_ASSERT(hwnd != 0);
  256. #else
  257. xDisplay = impl->display;
  258. xWindow = impl->win;
  259. DISTRHO_SAFE_ASSERT(xWindow != 0);
  260. if (! fUsingEmbed)
  261. {
  262. const pid_t pid = getpid();
  263. const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False);
  264. XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  265. const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False);
  266. // Setting the window to both dialog and normal will produce a decorated floating dialog
  267. // Order is important: DIALOG needs to come before NORMAL
  268. const Atom _wts[2] = {
  269. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  270. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  271. };
  272. XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  273. }
  274. #endif
  275. puglEnterContext(fView);
  276. fApp.pData->windows.push_back(fSelf);
  277. DBG("Success!\n");
  278. }
  279. ~PrivateData()
  280. {
  281. DBG("Destroying window..."); DBGF;
  282. if (fModal.enabled)
  283. {
  284. exec_fini();
  285. close();
  286. }
  287. fWidgets.clear();
  288. if (fUsingEmbed)
  289. {
  290. puglHideWindow(fView);
  291. fApp.pData->oneHidden();
  292. }
  293. if (fSelf != nullptr)
  294. {
  295. fApp.pData->windows.remove(fSelf);
  296. fSelf = nullptr;
  297. }
  298. if (fView != nullptr)
  299. {
  300. puglDestroy(fView);
  301. fView = nullptr;
  302. }
  303. if (fTitle != nullptr)
  304. {
  305. std::free(fTitle);
  306. fTitle = nullptr;
  307. }
  308. #if defined(DISTRHO_OS_HAIKU)
  309. bApplication = nullptr;
  310. bView = nullptr;
  311. bWindow = nullptr;
  312. #elif defined(DISTRHO_OS_MAC)
  313. mView = nullptr;
  314. mWindow = nullptr;
  315. #elif defined(DISTRHO_OS_WINDOWS)
  316. hwnd = 0;
  317. #else
  318. xDisplay = nullptr;
  319. xWindow = 0;
  320. #endif
  321. #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
  322. if (fOpenFilePanel)
  323. {
  324. [fOpenFilePanel release];
  325. fOpenFilePanel = nullptr;
  326. }
  327. if (fFilePanelDelegate)
  328. {
  329. [fFilePanelDelegate release];
  330. fFilePanelDelegate = nullptr;
  331. }
  332. #endif
  333. DBG("Success!\n");
  334. }
  335. // -------------------------------------------------------------------
  336. void close()
  337. {
  338. DBG("Window close\n");
  339. if (fUsingEmbed)
  340. return;
  341. setVisible(false);
  342. if (! fFirstInit)
  343. {
  344. fApp.pData->oneHidden();
  345. fFirstInit = true;
  346. }
  347. }
  348. void exec(const bool lockWait)
  349. {
  350. DBG("Window exec\n");
  351. exec_init();
  352. if (lockWait)
  353. {
  354. for (; fVisible && fModal.enabled;)
  355. {
  356. idle();
  357. d_msleep(10);
  358. }
  359. exec_fini();
  360. }
  361. else
  362. {
  363. idle();
  364. }
  365. }
  366. // -------------------------------------------------------------------
  367. void exec_init()
  368. {
  369. DBG("Window modal loop starting..."); DBGF;
  370. DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true));
  371. fModal.enabled = true;
  372. fModal.parent->fModal.childFocus = this;
  373. fModal.parent->setVisible(true);
  374. setVisible(true);
  375. DBG("Ok\n");
  376. }
  377. void exec_fini()
  378. {
  379. DBG("Window modal loop stopping..."); DBGF;
  380. fModal.enabled = false;
  381. if (fModal.parent != nullptr)
  382. {
  383. fModal.parent->fModal.childFocus = nullptr;
  384. // the mouse position probably changed since the modal appeared,
  385. // so send a mouse motion event to the modal's parent window
  386. #if defined(DISTRHO_OS_HAIKU)
  387. // TODO
  388. #elif defined(DISTRHO_OS_MAC)
  389. // TODO
  390. #elif defined(DISTRHO_OS_WINDOWS)
  391. // TODO
  392. #else
  393. int i, wx, wy;
  394. uint u;
  395. ::Window w;
  396. if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
  397. fModal.parent->onPuglMotion(wx, wy);
  398. #endif
  399. }
  400. DBG("Ok\n");
  401. }
  402. // -------------------------------------------------------------------
  403. void focus()
  404. {
  405. DBG("Window focus\n");
  406. #if defined(DISTRHO_OS_HAIKU)
  407. if (bWindow != nullptr)
  408. {
  409. if (bWindow->LockLooper())
  410. {
  411. bWindow->Activate(true);
  412. bWindow->UnlockLooper();
  413. }
  414. }
  415. else
  416. {
  417. bView->MakeFocus(true);
  418. }
  419. #elif defined(DISTRHO_OS_MAC)
  420. if (mWindow != nullptr)
  421. [mWindow makeKeyWindow];
  422. #elif defined(DISTRHO_OS_WINDOWS)
  423. SetForegroundWindow(hwnd);
  424. SetActiveWindow(hwnd);
  425. SetFocus(hwnd);
  426. #else
  427. XRaiseWindow(xDisplay, xWindow);
  428. XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
  429. XFlush(xDisplay);
  430. #endif
  431. }
  432. // -------------------------------------------------------------------
  433. void setVisible(const bool yesNo)
  434. {
  435. if (fVisible == yesNo)
  436. {
  437. DBG("Window setVisible matches current state, ignoring request\n");
  438. return;
  439. }
  440. if (fUsingEmbed)
  441. {
  442. DBG("Window setVisible cannot be called when embedded\n");
  443. return;
  444. }
  445. DBG("Window setVisible called\n");
  446. fVisible = yesNo;
  447. if (yesNo && fFirstInit)
  448. setSize(fWidth, fHeight, true);
  449. #if defined(DISTRHO_OS_HAIKU)
  450. if (bWindow != nullptr)
  451. {
  452. if (bWindow->LockLooper())
  453. {
  454. if (yesNo)
  455. bWindow->Show();
  456. else
  457. bWindow->Hide();
  458. // TODO use flush?
  459. bWindow->Sync();
  460. bWindow->UnlockLooper();
  461. }
  462. }
  463. else
  464. {
  465. if (yesNo)
  466. bView->Show();
  467. else
  468. bView->Hide();
  469. }
  470. #elif defined(DISTRHO_OS_MAC)
  471. if (yesNo)
  472. {
  473. if (mWindow != nullptr)
  474. {
  475. if (mParentWindow != nullptr)
  476. [mParentWindow addChildWindow:mWindow
  477. ordered:NSWindowAbove];
  478. [mWindow setIsVisible:YES];
  479. }
  480. else
  481. {
  482. [mView setHidden:NO];
  483. }
  484. }
  485. else
  486. {
  487. if (mWindow != nullptr)
  488. {
  489. if (mParentWindow != nullptr)
  490. [mParentWindow removeChildWindow:mWindow];
  491. [mWindow setIsVisible:NO];
  492. }
  493. else
  494. {
  495. [mView setHidden:YES];
  496. }
  497. }
  498. #elif defined(DISTRHO_OS_WINDOWS)
  499. if (yesNo)
  500. {
  501. if (fFirstInit)
  502. {
  503. RECT rectChild, rectParent;
  504. if (hwndParent != nullptr &&
  505. GetWindowRect(hwnd, &rectChild) &&
  506. GetWindowRect(hwndParent, &rectParent))
  507. {
  508. SetWindowPos(hwnd, hwndParent,
  509. rectParent.left + (rectChild.right-rectChild.left)/2,
  510. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  511. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  512. }
  513. else
  514. {
  515. ShowWindow(hwnd, SW_SHOWNORMAL);
  516. }
  517. }
  518. else
  519. {
  520. ShowWindow(hwnd, SW_RESTORE);
  521. }
  522. }
  523. else
  524. {
  525. ShowWindow(hwnd, SW_HIDE);
  526. }
  527. UpdateWindow(hwnd);
  528. #else
  529. if (yesNo)
  530. XMapRaised(xDisplay, xWindow);
  531. else
  532. XUnmapWindow(xDisplay, xWindow);
  533. XFlush(xDisplay);
  534. #endif
  535. if (yesNo)
  536. {
  537. if (fFirstInit)
  538. {
  539. fApp.pData->oneShown();
  540. fFirstInit = false;
  541. }
  542. }
  543. else if (fModal.enabled)
  544. exec_fini();
  545. }
  546. // -------------------------------------------------------------------
  547. void setResizable(const bool yesNo)
  548. {
  549. if (fResizable == yesNo)
  550. {
  551. DBG("Window setResizable matches current state, ignoring request\n");
  552. return;
  553. }
  554. if (fUsingEmbed)
  555. {
  556. DBG("Window setResizable cannot be called when embedded\n");
  557. return;
  558. }
  559. DBG("Window setResizable called\n");
  560. fResizable = yesNo;
  561. fView->user_resizable = yesNo;
  562. #if defined(DISTRHO_OS_HAIKU)
  563. // TODO
  564. // B_NO_BORDER
  565. // B_TITLED_WINDOW_LOOK
  566. // bWindow->SetFlags();
  567. #elif defined(DISTRHO_OS_MAC)
  568. const uint flags = yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0;
  569. [mView setAutoresizingMask:flags];
  570. #elif defined(DISTRHO_OS_WINDOWS)
  571. const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
  572. : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  573. SetWindowLong(hwnd, GWL_STYLE, winFlags);
  574. #endif
  575. setSize(fWidth, fHeight, true);
  576. }
  577. // -------------------------------------------------------------------
  578. void setGeometryConstraints(uint width, uint height, bool aspect)
  579. {
  580. // Did you forget to set DISTRHO_UI_USER_RESIZABLE ?
  581. DISTRHO_SAFE_ASSERT_RETURN(fResizable,);
  582. fView->min_width = width;
  583. fView->min_height = height;
  584. puglUpdateGeometryConstraints(fView, width, height, aspect);
  585. }
  586. // -------------------------------------------------------------------
  587. void setSize(uint width, uint height, const bool forced = false)
  588. {
  589. if (width <= 1 || height <= 1)
  590. {
  591. DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
  592. return;
  593. }
  594. if (fWidth == width && fHeight == height && ! forced)
  595. {
  596. DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
  597. return;
  598. }
  599. fWidth = width;
  600. fHeight = height;
  601. DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
  602. #if defined(DISTRHO_OS_HAIKU)
  603. bView->ResizeTo(width, height);
  604. if (bWindow != nullptr && bWindow->LockLooper())
  605. {
  606. bWindow->MoveTo(50, 50);
  607. bWindow->ResizeTo(width, height);
  608. if (! forced)
  609. bWindow->Flush();
  610. bWindow->UnlockLooper();
  611. }
  612. // TODO resizable
  613. #elif defined(DISTRHO_OS_MAC)
  614. [mView setFrame:NSMakeRect(0, 0, width, height)];
  615. if (mWindow != nullptr)
  616. {
  617. const NSSize size = NSMakeSize(width, height);
  618. [mWindow setContentSize:size];
  619. if (fResizable)
  620. {
  621. [mWindow setContentMinSize:NSMakeSize(1, 1)];
  622. [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  623. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
  624. }
  625. else
  626. {
  627. [mWindow setContentMinSize:size];
  628. [mWindow setContentMaxSize:size];
  629. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  630. }
  631. }
  632. #elif defined(DISTRHO_OS_WINDOWS)
  633. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
  634. RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
  635. AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
  636. SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  637. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  638. if (! forced)
  639. UpdateWindow(hwnd);
  640. #else
  641. if (! fResizable)
  642. {
  643. XSizeHints sizeHints;
  644. memset(&sizeHints, 0, sizeof(sizeHints));
  645. sizeHints.flags = PSize|PMinSize|PMaxSize;
  646. sizeHints.width = static_cast<int>(width);
  647. sizeHints.height = static_cast<int>(height);
  648. sizeHints.min_width = static_cast<int>(width);
  649. sizeHints.min_height = static_cast<int>(height);
  650. sizeHints.max_width = static_cast<int>(width);
  651. sizeHints.max_height = static_cast<int>(height);
  652. XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
  653. }
  654. XResizeWindow(xDisplay, xWindow, width, height);
  655. if (! forced)
  656. XFlush(xDisplay);
  657. #endif
  658. puglPostRedisplay(fView);
  659. }
  660. // -------------------------------------------------------------------
  661. const char* getTitle() const noexcept
  662. {
  663. static const char* const kFallback = "";
  664. return fTitle != nullptr ? fTitle : kFallback;
  665. }
  666. void setTitle(const char* const title)
  667. {
  668. DBGp("Window setTitle \"%s\"\n", title);
  669. if (fTitle != nullptr)
  670. std::free(fTitle);
  671. fTitle = strdup(title);
  672. #if defined(DISTRHO_OS_HAIKU)
  673. if (bWindow != nullptr&& bWindow->LockLooper())
  674. {
  675. bWindow->SetTitle(title);
  676. bWindow->UnlockLooper();
  677. }
  678. #elif defined(DISTRHO_OS_MAC)
  679. if (mWindow != nullptr)
  680. {
  681. NSString* titleString = [[NSString alloc]
  682. initWithBytes:title
  683. length:strlen(title)
  684. encoding:NSUTF8StringEncoding];
  685. [mWindow setTitle:titleString];
  686. }
  687. #elif defined(DISTRHO_OS_WINDOWS)
  688. SetWindowTextA(hwnd, title);
  689. #else
  690. XStoreName(xDisplay, xWindow, title);
  691. Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False);
  692. Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False);
  693. XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title));
  694. #endif
  695. }
  696. void setTransientWinId(const uintptr_t winId)
  697. {
  698. DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
  699. #if defined(DISTRHO_OS_HAIKU)
  700. // TODO
  701. #elif defined(DISTRHO_OS_MAC)
  702. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  703. DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  704. [parentWindow addChildWindow:mWindow
  705. ordered:NSWindowAbove];
  706. #elif defined(DISTRHO_OS_WINDOWS)
  707. hwndParent = (HWND)winId;
  708. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId);
  709. #else
  710. XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
  711. #endif
  712. }
  713. // -------------------------------------------------------------------
  714. double getScaling() const noexcept
  715. {
  716. return fScaling;
  717. }
  718. void setAutoScaling(const double scaling) noexcept
  719. {
  720. DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,);
  721. fAutoScaling = scaling;
  722. }
  723. // -------------------------------------------------------------------
  724. bool getIgnoringKeyRepeat() const noexcept
  725. {
  726. return fView->ignoreKeyRepeat;
  727. }
  728. void setIgnoringKeyRepeat(bool ignore) noexcept
  729. {
  730. puglIgnoreKeyRepeat(fView, ignore);
  731. }
  732. // -------------------------------------------------------------------
  733. void addWidget(Widget* const widget)
  734. {
  735. fWidgets.push_back(widget);
  736. }
  737. void removeWidget(Widget* const widget)
  738. {
  739. fWidgets.remove(widget);
  740. }
  741. void idle()
  742. {
  743. puglProcessEvents(fView);
  744. #ifdef DISTRHO_OS_HAIKU
  745. if (bApplication != nullptr)
  746. {
  747. // bApplication->Lock();
  748. // bApplication->Loop();
  749. // bApplication->Unlock();
  750. }
  751. #endif
  752. #ifdef DISTRHO_OS_MAC
  753. if (fNeedsIdle)
  754. {
  755. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  756. NSEvent* event;
  757. for (;;)
  758. {
  759. event = [NSApp
  760. nextEventMatchingMask:NSAnyEventMask
  761. untilDate:[NSDate distantPast]
  762. inMode:NSDefaultRunLoopMode
  763. dequeue:YES];
  764. if (event == nil)
  765. break;
  766. [NSApp sendEvent: event];
  767. }
  768. [pool release];
  769. }
  770. #endif
  771. #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED)
  772. if (fSelectedFile.isNotEmpty())
  773. {
  774. char* const buffer = fSelectedFile.getAndReleaseBuffer();
  775. fView->fileSelectedFunc(fView, buffer);
  776. std::free(buffer);
  777. }
  778. #endif
  779. if (fModal.enabled && fModal.parent != nullptr)
  780. fModal.parent->idle();
  781. }
  782. // -------------------------------------------------------------------
  783. void onPuglDisplay()
  784. {
  785. fSelf->onDisplayBefore();
  786. FOR_EACH_WIDGET(it)
  787. {
  788. Widget* const widget(*it);
  789. widget->pData->display(fWidth, fHeight, fAutoScaling, false);
  790. }
  791. fSelf->onDisplayAfter();
  792. }
  793. int onPuglKeyboard(const bool press, const uint key)
  794. {
  795. DBGp("PUGL: onKeyboard : %i %i\n", press, key);
  796. if (fModal.childFocus != nullptr)
  797. {
  798. fModal.childFocus->focus();
  799. return 0;
  800. }
  801. Widget::KeyboardEvent ev;
  802. ev.press = press;
  803. ev.key = key;
  804. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  805. ev.time = puglGetEventTimestamp(fView);
  806. FOR_EACH_WIDGET_INV(rit)
  807. {
  808. Widget* const widget(*rit);
  809. if (widget->isVisible() && widget->onKeyboard(ev))
  810. return 0;
  811. }
  812. return 1;
  813. }
  814. int onPuglSpecial(const bool press, const Key key)
  815. {
  816. DBGp("PUGL: onSpecial : %i %i\n", press, key);
  817. if (fModal.childFocus != nullptr)
  818. {
  819. fModal.childFocus->focus();
  820. return 0;
  821. }
  822. Widget::SpecialEvent ev;
  823. ev.press = press;
  824. ev.key = key;
  825. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  826. ev.time = puglGetEventTimestamp(fView);
  827. FOR_EACH_WIDGET_INV(rit)
  828. {
  829. Widget* const widget(*rit);
  830. if (widget->isVisible() && widget->onSpecial(ev))
  831. return 0;
  832. }
  833. return 1;
  834. }
  835. void onPuglMouse(const int button, const bool press, int x, int y)
  836. {
  837. DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
  838. // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
  839. if (press && button == 0 && x == 0 && y == 0) return;
  840. if (fModal.childFocus != nullptr)
  841. return fModal.childFocus->focus();
  842. x /= fAutoScaling;
  843. y /= fAutoScaling;
  844. Widget::MouseEvent ev;
  845. ev.button = button;
  846. ev.press = press;
  847. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  848. ev.time = puglGetEventTimestamp(fView);
  849. FOR_EACH_WIDGET_INV(rit)
  850. {
  851. Widget* const widget(*rit);
  852. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  853. if (widget->isVisible() && widget->onMouse(ev))
  854. break;
  855. }
  856. }
  857. void onPuglMotion(int x, int y)
  858. {
  859. // DBGp("PUGL: onMotion : %i %i\n", x, y);
  860. if (fModal.childFocus != nullptr)
  861. return;
  862. x /= fAutoScaling;
  863. y /= fAutoScaling;
  864. Widget::MotionEvent ev;
  865. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  866. ev.time = puglGetEventTimestamp(fView);
  867. FOR_EACH_WIDGET_INV(rit)
  868. {
  869. Widget* const widget(*rit);
  870. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  871. if (widget->isVisible() && widget->onMotion(ev))
  872. break;
  873. }
  874. }
  875. void onPuglScroll(int x, int y, float dx, float dy)
  876. {
  877. DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
  878. if (fModal.childFocus != nullptr)
  879. return;
  880. x /= fAutoScaling;
  881. y /= fAutoScaling;
  882. dx /= fAutoScaling;
  883. dy /= fAutoScaling;
  884. Widget::ScrollEvent ev;
  885. ev.delta = Point<float>(dx, dy);
  886. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  887. ev.time = puglGetEventTimestamp(fView);
  888. FOR_EACH_WIDGET_INV(rit)
  889. {
  890. Widget* const widget(*rit);
  891. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  892. if (widget->isVisible() && widget->onScroll(ev))
  893. break;
  894. }
  895. }
  896. void onPuglReshape(const int width, const int height)
  897. {
  898. DBGp("PUGL: onReshape : %i %i\n", width, height);
  899. if (width <= 1 && height <= 1)
  900. return;
  901. fWidth = static_cast<uint>(width);
  902. fHeight = static_cast<uint>(height);
  903. fSelf->onReshape(fWidth, fHeight);
  904. FOR_EACH_WIDGET(it)
  905. {
  906. Widget* const widget(*it);
  907. if (widget->pData->needsFullViewport)
  908. widget->setSize(fWidth, fHeight);
  909. }
  910. }
  911. void onPuglClose()
  912. {
  913. DBG("PUGL: onClose\n");
  914. if (fModal.enabled)
  915. exec_fini();
  916. fSelf->onClose();
  917. if (fModal.childFocus != nullptr)
  918. fModal.childFocus->fSelf->onClose();
  919. close();
  920. }
  921. // -------------------------------------------------------------------
  922. bool handlePluginKeyboard(const bool press, const uint key)
  923. {
  924. DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
  925. if (fModal.childFocus != nullptr)
  926. {
  927. fModal.childFocus->focus();
  928. return true;
  929. }
  930. Widget::KeyboardEvent ev;
  931. ev.press = press;
  932. ev.key = key;
  933. ev.mod = static_cast<Modifier>(fView->mods);
  934. ev.time = 0;
  935. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  936. ev.key -= 'a' - 'A'; // a-z -> A-Z
  937. FOR_EACH_WIDGET_INV(rit)
  938. {
  939. Widget* const widget(*rit);
  940. if (widget->isVisible() && widget->onKeyboard(ev))
  941. return true;
  942. }
  943. return false;
  944. }
  945. bool handlePluginSpecial(const bool press, const Key key)
  946. {
  947. DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
  948. if (fModal.childFocus != nullptr)
  949. {
  950. fModal.childFocus->focus();
  951. return true;
  952. }
  953. int mods = 0x0;
  954. switch (key)
  955. {
  956. case kKeyShift:
  957. mods |= kModifierShift;
  958. break;
  959. case kKeyControl:
  960. mods |= kModifierControl;
  961. break;
  962. case kKeyAlt:
  963. mods |= kModifierAlt;
  964. break;
  965. default:
  966. break;
  967. }
  968. if (mods != 0x0)
  969. {
  970. if (press)
  971. fView->mods |= mods;
  972. else
  973. fView->mods &= ~(mods);
  974. }
  975. Widget::SpecialEvent ev;
  976. ev.press = press;
  977. ev.key = key;
  978. ev.mod = static_cast<Modifier>(fView->mods);
  979. ev.time = 0;
  980. FOR_EACH_WIDGET_INV(rit)
  981. {
  982. Widget* const widget(*rit);
  983. if (widget->isVisible() && widget->onSpecial(ev))
  984. return true;
  985. }
  986. return false;
  987. }
  988. #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
  989. static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData)
  990. {
  991. PrivateData* pData = (PrivateData*)userData;
  992. if (returnCode == NSOKButton)
  993. {
  994. NSArray* urls = [panel URLs];
  995. NSURL* fileUrl = nullptr;
  996. for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i)
  997. {
  998. NSURL* url = (NSURL*)[urls objectAtIndex:i];
  999. if ([url isFileURL])
  1000. fileUrl = url;
  1001. }
  1002. if (fileUrl)
  1003. {
  1004. PuglView* view = pData->fView;
  1005. if (view->fileSelectedFunc)
  1006. {
  1007. const char* fileName = [fileUrl.path UTF8String];
  1008. view->fileSelectedFunc(view, fileName);
  1009. }
  1010. }
  1011. }
  1012. [pData->fOpenFilePanel release];
  1013. pData->fOpenFilePanel = nullptr;
  1014. }
  1015. #endif
  1016. // -------------------------------------------------------------------
  1017. Application& fApp;
  1018. Window* fSelf;
  1019. GraphicsContext fContext;
  1020. PuglView* fView;
  1021. bool fFirstInit;
  1022. bool fVisible;
  1023. bool fResizable;
  1024. bool fUsingEmbed;
  1025. uint fWidth;
  1026. uint fHeight;
  1027. double fScaling;
  1028. double fAutoScaling;
  1029. char* fTitle;
  1030. std::list<Widget*> fWidgets;
  1031. struct Modal {
  1032. bool enabled;
  1033. PrivateData* parent;
  1034. PrivateData* childFocus;
  1035. Modal()
  1036. : enabled(false),
  1037. parent(nullptr),
  1038. childFocus(nullptr) {}
  1039. Modal(PrivateData* const p)
  1040. : enabled(false),
  1041. parent(p),
  1042. childFocus(nullptr) {}
  1043. ~Modal()
  1044. {
  1045. DISTRHO_SAFE_ASSERT(! enabled);
  1046. DISTRHO_SAFE_ASSERT(childFocus == nullptr);
  1047. }
  1048. DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
  1049. } fModal;
  1050. #if defined(DISTRHO_OS_HAIKU)
  1051. BApplication* bApplication;
  1052. BView* bView;
  1053. BWindow* bWindow;
  1054. #elif defined(DISTRHO_OS_MAC)
  1055. bool fNeedsIdle;
  1056. NSView<PuglGenericView>* mView;
  1057. id mWindow;
  1058. id mParentWindow;
  1059. # ifndef DGL_FILE_BROWSER_DISABLED
  1060. NSOpenPanel* fOpenFilePanel;
  1061. id fFilePanelDelegate;
  1062. # endif
  1063. #elif defined(DISTRHO_OS_WINDOWS)
  1064. HWND hwnd;
  1065. HWND hwndParent;
  1066. # ifndef DGL_FILE_BROWSER_DISABLED
  1067. String fSelectedFile;
  1068. # endif
  1069. #else
  1070. Display* xDisplay;
  1071. ::Window xWindow;
  1072. #endif
  1073. // -------------------------------------------------------------------
  1074. // Callbacks
  1075. #define handlePtr ((PrivateData*)puglGetHandle(view))
  1076. static void onDisplayCallback(PuglView* view)
  1077. {
  1078. handlePtr->onPuglDisplay();
  1079. }
  1080. static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
  1081. {
  1082. return handlePtr->onPuglKeyboard(press, key);
  1083. }
  1084. static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
  1085. {
  1086. return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
  1087. }
  1088. static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
  1089. {
  1090. handlePtr->onPuglMouse(button, press, x, y);
  1091. }
  1092. static void onMotionCallback(PuglView* view, int x, int y)
  1093. {
  1094. handlePtr->onPuglMotion(x, y);
  1095. }
  1096. static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
  1097. {
  1098. handlePtr->onPuglScroll(x, y, dx, dy);
  1099. }
  1100. static void onReshapeCallback(PuglView* view, int width, int height)
  1101. {
  1102. handlePtr->onPuglReshape(width, height);
  1103. }
  1104. static void onCloseCallback(PuglView* view)
  1105. {
  1106. handlePtr->onPuglClose();
  1107. }
  1108. #ifndef DGL_FILE_BROWSER_DISABLED
  1109. static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
  1110. {
  1111. handlePtr->fSelf->fileBrowserSelected(filename);
  1112. }
  1113. #endif
  1114. #undef handlePtr
  1115. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  1116. };
  1117. #endif