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.

1515 lines
37KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2019 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. // we need this for now
  17. //#define PUGL_GRAB_FOCUS 1
  18. #include "../Base.hpp"
  19. #undef PUGL_HAVE_CAIRO
  20. #undef PUGL_HAVE_GL
  21. #ifdef HAVE_DGL
  22. #define PUGL_HAVE_GL 1
  23. #endif
  24. #ifdef HAVE_DCAIRO
  25. #define PUGL_HAVE_CAIRO 1
  26. #endif
  27. #include "pugl/pugl.h"
  28. #if defined(__GNUC__) && (__GNUC__ >= 7)
  29. # pragma GCC diagnostic push
  30. # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
  31. #endif
  32. #if defined(DISTRHO_OS_WINDOWS)
  33. # include "pugl/pugl_win.cpp"
  34. # undef max
  35. # undef min
  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. #else
  41. # include <sys/types.h>
  42. # include <unistd.h>
  43. extern "C" {
  44. # include "pugl/pugl_x11.c"
  45. }
  46. #endif
  47. #if defined(__GNUC__) && (__GNUC__ >= 7)
  48. # pragma GCC diagnostic pop
  49. #endif
  50. #include "ApplicationPrivateData.hpp"
  51. #include "WidgetPrivateData.hpp"
  52. #include "../StandaloneWindow.hpp"
  53. #include "../../distrho/extra/String.hpp"
  54. #define FOR_EACH_WIDGET(it) \
  55. for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
  56. #define FOR_EACH_WIDGET_INV(rit) \
  57. for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
  58. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  59. # define DBG(msg) std::fprintf(stderr, "%s", msg);
  60. # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  61. # define DBGF std::fflush(stderr);
  62. #else
  63. # define DBG(msg)
  64. # define DBGp(...)
  65. # define DBGF
  66. #endif
  67. START_NAMESPACE_DGL
  68. // -----------------------------------------------------------------------
  69. // Window Private
  70. struct Window::PrivateData {
  71. PrivateData(Application& app, Window* const self)
  72. : fApp(app),
  73. fSelf(self),
  74. fView(puglInit()),
  75. fFirstInit(true),
  76. fVisible(false),
  77. fResizable(true),
  78. fUsingEmbed(false),
  79. fWidth(1),
  80. fHeight(1),
  81. fScaling(1.0),
  82. fTitle(nullptr),
  83. fWidgets(),
  84. fModal(),
  85. #if defined(DISTRHO_OS_WINDOWS)
  86. hwnd(nullptr),
  87. hwndParent(nullptr)
  88. #elif defined(DISTRHO_OS_MAC)
  89. fNeedsIdle(true),
  90. mView(nullptr),
  91. mWindow(nullptr),
  92. mParentWindow(nullptr)
  93. #else
  94. xDisplay(nullptr),
  95. xWindow(0)
  96. #endif
  97. {
  98. DBG("Creating window without parent..."); DBGF;
  99. init();
  100. }
  101. PrivateData(Application& app, Window* const self, Window& parent)
  102. : fApp(app),
  103. fSelf(self),
  104. fView(puglInit()),
  105. fFirstInit(true),
  106. fVisible(false),
  107. fResizable(true),
  108. fUsingEmbed(false),
  109. fWidth(1),
  110. fHeight(1),
  111. fScaling(1.0),
  112. fTitle(nullptr),
  113. fWidgets(),
  114. fModal(parent.pData),
  115. #if defined(DISTRHO_OS_WINDOWS)
  116. hwnd(nullptr),
  117. hwndParent(nullptr)
  118. #elif defined(DISTRHO_OS_MAC)
  119. fNeedsIdle(false),
  120. mView(nullptr),
  121. mWindow(nullptr),
  122. mParentWindow(nullptr)
  123. #else
  124. xDisplay(nullptr),
  125. xWindow(0)
  126. #endif
  127. {
  128. DBG("Creating window with parent..."); DBGF;
  129. init();
  130. const PuglInternals* const parentImpl(parent.pData->fView->impl);
  131. // NOTE: almost a 1:1 copy of setTransientWinId()
  132. #if defined(DISTRHO_OS_WINDOWS)
  133. hwndParent = parentImpl->hwnd;
  134. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent);
  135. #elif defined(DISTRHO_OS_MAC)
  136. mParentWindow = parentImpl->window;
  137. #else
  138. XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
  139. #endif
  140. }
  141. PrivateData(Application& app, Window* const self, const intptr_t parentId)
  142. : fApp(app),
  143. fSelf(self),
  144. fView(puglInit()),
  145. fFirstInit(true),
  146. fVisible(parentId != 0),
  147. fResizable(parentId == 0),
  148. fUsingEmbed(parentId != 0),
  149. fWidth(1),
  150. fHeight(1),
  151. fScaling(1.0),
  152. fTitle(nullptr),
  153. fWidgets(),
  154. fModal(),
  155. #if defined(DISTRHO_OS_WINDOWS)
  156. hwnd(nullptr),
  157. hwndParent(nullptr)
  158. #elif defined(DISTRHO_OS_MAC)
  159. fNeedsIdle(parentId == 0),
  160. mView(nullptr),
  161. mWindow(nullptr),
  162. mParentWindow(nullptr)
  163. #else
  164. xDisplay(nullptr),
  165. xWindow(0)
  166. #endif
  167. {
  168. if (fUsingEmbed)
  169. {
  170. DBG("Creating embedded window..."); DBGF;
  171. puglInitWindowParent(fView, parentId);
  172. }
  173. else
  174. {
  175. DBG("Creating window without parent..."); DBGF;
  176. }
  177. init();
  178. if (fUsingEmbed)
  179. {
  180. DBG("NOTE: Embed window is always visible and non-resizable\n");
  181. puglShowWindow(fView);
  182. fApp.pData->oneShown();
  183. fFirstInit = false;
  184. }
  185. }
  186. void init()
  187. {
  188. if (fSelf == nullptr || fView == nullptr)
  189. {
  190. DBG("Failed!\n");
  191. return;
  192. }
  193. puglInitUserResizable(fView, fResizable);
  194. puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
  195. puglSetHandle(fView, this);
  196. puglSetDisplayFunc(fView, onDisplayCallback);
  197. puglSetKeyboardFunc(fView, onKeyboardCallback);
  198. puglSetMotionFunc(fView, onMotionCallback);
  199. puglSetMouseFunc(fView, onMouseCallback);
  200. puglSetScrollFunc(fView, onScrollCallback);
  201. puglSetSpecialFunc(fView, onSpecialCallback);
  202. puglSetReshapeFunc(fView, onReshapeCallback);
  203. puglSetCloseFunc(fView, onCloseCallback);
  204. #ifndef DGL_FILE_BROWSER_DISABLED
  205. puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
  206. #endif
  207. puglCreateWindow(fView, nullptr);
  208. PuglInternals* impl = fView->impl;
  209. #if defined(DISTRHO_OS_WINDOWS)
  210. hwnd = impl->hwnd;
  211. DISTRHO_SAFE_ASSERT(hwnd != 0);
  212. #elif defined(DISTRHO_OS_MAC)
  213. mView = impl->view;
  214. mWindow = impl->window;
  215. DISTRHO_SAFE_ASSERT(mView != nullptr);
  216. if (fUsingEmbed) {
  217. DISTRHO_SAFE_ASSERT(mWindow == nullptr);
  218. } else {
  219. DISTRHO_SAFE_ASSERT(mWindow != nullptr);
  220. }
  221. #else
  222. xDisplay = impl->display;
  223. xWindow = impl->win;
  224. DISTRHO_SAFE_ASSERT(xWindow != 0);
  225. if (! fUsingEmbed)
  226. {
  227. const pid_t pid = getpid();
  228. const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False);
  229. XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  230. const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False);
  231. // Setting the window to both dialog and normal will produce a decorated floating dialog
  232. // Order is important: DIALOG needs to come before NORMAL
  233. const Atom _wts[2] = {
  234. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  235. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  236. };
  237. XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  238. }
  239. #endif
  240. puglEnterContext(fView);
  241. fApp.pData->windows.push_back(fSelf);
  242. DBG("Success!\n");
  243. }
  244. ~PrivateData()
  245. {
  246. DBG("Destroying window..."); DBGF;
  247. if (fModal.enabled)
  248. {
  249. exec_fini();
  250. close();
  251. }
  252. fWidgets.clear();
  253. if (fUsingEmbed)
  254. {
  255. puglHideWindow(fView);
  256. fApp.pData->oneHidden();
  257. }
  258. if (fSelf != nullptr)
  259. {
  260. fApp.pData->windows.remove(fSelf);
  261. fSelf = nullptr;
  262. }
  263. if (fView != nullptr)
  264. {
  265. puglDestroy(fView);
  266. fView = nullptr;
  267. }
  268. if (fTitle != nullptr)
  269. {
  270. std::free(fTitle);
  271. fTitle = nullptr;
  272. }
  273. #if defined(DISTRHO_OS_WINDOWS)
  274. hwnd = 0;
  275. #elif defined(DISTRHO_OS_MAC)
  276. mView = nullptr;
  277. mWindow = nullptr;
  278. #else
  279. xDisplay = nullptr;
  280. xWindow = 0;
  281. #endif
  282. DBG("Success!\n");
  283. }
  284. // -------------------------------------------------------------------
  285. void close()
  286. {
  287. DBG("Window close\n");
  288. if (fUsingEmbed)
  289. return;
  290. setVisible(false);
  291. if (! fFirstInit)
  292. {
  293. fApp.pData->oneHidden();
  294. fFirstInit = true;
  295. }
  296. }
  297. void exec(const bool lockWait)
  298. {
  299. DBG("Window exec\n");
  300. exec_init();
  301. if (lockWait)
  302. {
  303. for (; fVisible && fModal.enabled;)
  304. {
  305. idle();
  306. d_msleep(10);
  307. }
  308. exec_fini();
  309. }
  310. else
  311. {
  312. idle();
  313. }
  314. }
  315. // -------------------------------------------------------------------
  316. void exec_init()
  317. {
  318. DBG("Window modal loop starting..."); DBGF;
  319. DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true));
  320. fModal.enabled = true;
  321. fModal.parent->fModal.childFocus = this;
  322. fModal.parent->setVisible(true);
  323. setVisible(true);
  324. DBG("Ok\n");
  325. }
  326. void exec_fini()
  327. {
  328. DBG("Window modal loop stopping..."); DBGF;
  329. fModal.enabled = false;
  330. if (fModal.parent != nullptr)
  331. {
  332. fModal.parent->fModal.childFocus = nullptr;
  333. // the mouse position probably changed since the modal appeared,
  334. // so send a mouse motion event to the modal's parent window
  335. #if defined(DISTRHO_OS_WINDOWS)
  336. // TODO
  337. #elif defined(DISTRHO_OS_MAC)
  338. // TODO
  339. #else
  340. int i, wx, wy;
  341. uint u;
  342. ::Window w;
  343. if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
  344. fModal.parent->onPuglMotion(wx, wy);
  345. #endif
  346. }
  347. DBG("Ok\n");
  348. }
  349. // -------------------------------------------------------------------
  350. void focus()
  351. {
  352. DBG("Window focus\n");
  353. #if defined(DISTRHO_OS_WINDOWS)
  354. SetForegroundWindow(hwnd);
  355. SetActiveWindow(hwnd);
  356. SetFocus(hwnd);
  357. #elif defined(DISTRHO_OS_MAC)
  358. if (mWindow != nullptr)
  359. [mWindow makeKeyWindow];
  360. #else
  361. XRaiseWindow(xDisplay, xWindow);
  362. XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
  363. XFlush(xDisplay);
  364. #endif
  365. }
  366. // -------------------------------------------------------------------
  367. void setVisible(const bool yesNo)
  368. {
  369. if (fVisible == yesNo)
  370. {
  371. DBG("Window setVisible matches current state, ignoring request\n");
  372. return;
  373. }
  374. if (fUsingEmbed)
  375. {
  376. DBG("Window setVisible cannot be called when embedded\n");
  377. return;
  378. }
  379. DBG("Window setVisible called\n");
  380. fVisible = yesNo;
  381. if (yesNo && fFirstInit)
  382. setSize(fWidth, fHeight, true);
  383. #if defined(DISTRHO_OS_WINDOWS)
  384. if (yesNo)
  385. {
  386. if (fFirstInit)
  387. {
  388. RECT rectChild, rectParent;
  389. if (hwndParent != nullptr &&
  390. GetWindowRect(hwnd, &rectChild) &&
  391. GetWindowRect(hwndParent, &rectParent))
  392. {
  393. SetWindowPos(hwnd, hwndParent,
  394. rectParent.left + (rectChild.right-rectChild.left)/2,
  395. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  396. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  397. }
  398. else
  399. {
  400. ShowWindow(hwnd, SW_SHOWNORMAL);
  401. }
  402. }
  403. else
  404. {
  405. ShowWindow(hwnd, SW_RESTORE);
  406. }
  407. }
  408. else
  409. {
  410. ShowWindow(hwnd, SW_HIDE);
  411. }
  412. UpdateWindow(hwnd);
  413. #elif defined(DISTRHO_OS_MAC)
  414. if (yesNo)
  415. {
  416. if (mWindow != nullptr)
  417. {
  418. if (mParentWindow != nullptr)
  419. [mParentWindow addChildWindow:mWindow
  420. ordered:NSWindowAbove];
  421. [mWindow setIsVisible:YES];
  422. }
  423. else
  424. {
  425. [mView setHidden:NO];
  426. }
  427. }
  428. else
  429. {
  430. if (mWindow != nullptr)
  431. {
  432. if (mParentWindow != nullptr)
  433. [mParentWindow removeChildWindow:mWindow];
  434. [mWindow setIsVisible:NO];
  435. }
  436. else
  437. {
  438. [mView setHidden:YES];
  439. }
  440. }
  441. #else
  442. if (yesNo)
  443. XMapRaised(xDisplay, xWindow);
  444. else
  445. XUnmapWindow(xDisplay, xWindow);
  446. XFlush(xDisplay);
  447. #endif
  448. if (yesNo)
  449. {
  450. if (fFirstInit)
  451. {
  452. fApp.pData->oneShown();
  453. fFirstInit = false;
  454. }
  455. }
  456. else if (fModal.enabled)
  457. exec_fini();
  458. }
  459. // -------------------------------------------------------------------
  460. void setResizable(const bool yesNo)
  461. {
  462. if (fResizable == yesNo)
  463. {
  464. DBG("Window setResizable matches current state, ignoring request\n");
  465. return;
  466. }
  467. if (fUsingEmbed)
  468. {
  469. DBG("Window setResizable cannot be called when embedded\n");
  470. return;
  471. }
  472. DBG("Window setResizable called\n");
  473. fResizable = yesNo;
  474. fView->user_resizable = yesNo;
  475. #if defined(DISTRHO_OS_WINDOWS)
  476. const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
  477. : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  478. SetWindowLong(hwnd, GWL_STYLE, winFlags);
  479. #elif defined(DISTRHO_OS_MAC)
  480. const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0);
  481. [mView setAutoresizingMask:flags];
  482. #endif
  483. setSize(fWidth, fHeight, true);
  484. }
  485. // -------------------------------------------------------------------
  486. void setGeometryConstraints(uint width, uint height, bool aspect)
  487. {
  488. DISTRHO_SAFE_ASSERT_RETURN(fResizable,);
  489. fView->min_width = width;
  490. fView->min_height = height;
  491. puglUpdateGeometryConstraints(fView, width, height, aspect);
  492. }
  493. // -------------------------------------------------------------------
  494. void setSize(uint width, uint height, const bool forced = false)
  495. {
  496. if (width <= 1 || height <= 1)
  497. {
  498. DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
  499. return;
  500. }
  501. if (fWidth == width && fHeight == height && ! forced)
  502. {
  503. DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
  504. return;
  505. }
  506. fWidth = width;
  507. fHeight = height;
  508. DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
  509. #if defined(DISTRHO_OS_WINDOWS)
  510. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
  511. RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
  512. AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
  513. SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  514. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  515. if (! forced)
  516. UpdateWindow(hwnd);
  517. #elif defined(DISTRHO_OS_MAC)
  518. [mView setFrame:NSMakeRect(0, 0, width, height)];
  519. if (mWindow != nullptr)
  520. {
  521. const NSSize size = NSMakeSize(width, height);
  522. [mWindow setContentSize:size];
  523. if (fResizable)
  524. {
  525. [mWindow setContentMinSize:NSMakeSize(1, 1)];
  526. [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  527. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
  528. }
  529. else
  530. {
  531. [mWindow setContentMinSize:size];
  532. [mWindow setContentMaxSize:size];
  533. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  534. }
  535. }
  536. #else
  537. if (! fResizable)
  538. {
  539. XSizeHints sizeHints;
  540. memset(&sizeHints, 0, sizeof(sizeHints));
  541. sizeHints.flags = PSize|PMinSize|PMaxSize;
  542. sizeHints.width = static_cast<int>(width);
  543. sizeHints.height = static_cast<int>(height);
  544. sizeHints.min_width = static_cast<int>(width);
  545. sizeHints.min_height = static_cast<int>(height);
  546. sizeHints.max_width = static_cast<int>(width);
  547. sizeHints.max_height = static_cast<int>(height);
  548. XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
  549. }
  550. XResizeWindow(xDisplay, xWindow, width, height);
  551. if (! forced)
  552. XFlush(xDisplay);
  553. #endif
  554. puglPostRedisplay(fView);
  555. }
  556. // -------------------------------------------------------------------
  557. const char* getTitle() const noexcept
  558. {
  559. static const char* const kFallback = "";
  560. return fTitle != nullptr ? fTitle : kFallback;
  561. }
  562. void setTitle(const char* const title)
  563. {
  564. DBGp("Window setTitle \"%s\"\n", title);
  565. if (fTitle != nullptr)
  566. std::free(fTitle);
  567. fTitle = strdup(title);
  568. #if defined(DISTRHO_OS_WINDOWS)
  569. SetWindowTextA(hwnd, title);
  570. #elif defined(DISTRHO_OS_MAC)
  571. if (mWindow != nullptr)
  572. {
  573. NSString* titleString = [[NSString alloc]
  574. initWithBytes:title
  575. length:strlen(title)
  576. encoding:NSUTF8StringEncoding];
  577. [mWindow setTitle:titleString];
  578. }
  579. #else
  580. XStoreName(xDisplay, xWindow, title);
  581. #endif
  582. }
  583. void setTransientWinId(const uintptr_t winId)
  584. {
  585. DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
  586. #if defined(DISTRHO_OS_WINDOWS)
  587. hwndParent = (HWND)winId;
  588. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId);
  589. #elif defined(DISTRHO_OS_MAC)
  590. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  591. DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  592. [parentWindow addChildWindow:mWindow
  593. ordered:NSWindowAbove];
  594. #else
  595. XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
  596. #endif
  597. }
  598. // -------------------------------------------------------------------
  599. double getScaling() const noexcept
  600. {
  601. return fScaling;
  602. }
  603. void setScaling(double scaling) noexcept
  604. {
  605. DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,);
  606. fScaling = scaling;
  607. }
  608. // -------------------------------------------------------------------
  609. bool getIgnoringKeyRepeat() const noexcept
  610. {
  611. return fView->ignoreKeyRepeat;
  612. }
  613. void setIgnoringKeyRepeat(bool ignore) noexcept
  614. {
  615. puglIgnoreKeyRepeat(fView, ignore);
  616. }
  617. // -------------------------------------------------------------------
  618. void addWidget(Widget* const widget)
  619. {
  620. fWidgets.push_back(widget);
  621. }
  622. void removeWidget(Widget* const widget)
  623. {
  624. fWidgets.remove(widget);
  625. }
  626. void idle()
  627. {
  628. puglProcessEvents(fView);
  629. #ifdef DISTRHO_OS_MAC
  630. if (fNeedsIdle)
  631. {
  632. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  633. NSEvent* event;
  634. for (;;)
  635. {
  636. event = [NSApp
  637. nextEventMatchingMask:NSAnyEventMask
  638. untilDate:[NSDate distantPast]
  639. inMode:NSDefaultRunLoopMode
  640. dequeue:YES];
  641. if (event == nil)
  642. break;
  643. [NSApp sendEvent: event];
  644. }
  645. [pool release];
  646. }
  647. #endif
  648. if (fModal.enabled && fModal.parent != nullptr)
  649. fModal.parent->idle();
  650. }
  651. // -------------------------------------------------------------------
  652. void onPuglDisplay()
  653. {
  654. fSelf->onDisplayBefore();
  655. FOR_EACH_WIDGET(it)
  656. {
  657. Widget* const widget(*it);
  658. widget->pData->display(fWidth, fHeight, fScaling, false);
  659. }
  660. fSelf->onDisplayAfter();
  661. }
  662. int onPuglKeyboard(const bool press, const uint key)
  663. {
  664. DBGp("PUGL: onKeyboard : %i %i\n", press, key);
  665. if (fModal.childFocus != nullptr)
  666. {
  667. fModal.childFocus->focus();
  668. return 0;
  669. }
  670. Widget::KeyboardEvent ev;
  671. ev.press = press;
  672. ev.key = key;
  673. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  674. ev.time = puglGetEventTimestamp(fView);
  675. FOR_EACH_WIDGET_INV(rit)
  676. {
  677. Widget* const widget(*rit);
  678. if (widget->isVisible() && widget->onKeyboard(ev))
  679. return 0;
  680. }
  681. return 1;
  682. }
  683. int onPuglSpecial(const bool press, const Key key)
  684. {
  685. DBGp("PUGL: onSpecial : %i %i\n", press, key);
  686. if (fModal.childFocus != nullptr)
  687. {
  688. fModal.childFocus->focus();
  689. return 0;
  690. }
  691. Widget::SpecialEvent ev;
  692. ev.press = press;
  693. ev.key = key;
  694. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  695. ev.time = puglGetEventTimestamp(fView);
  696. FOR_EACH_WIDGET_INV(rit)
  697. {
  698. Widget* const widget(*rit);
  699. if (widget->isVisible() && widget->onSpecial(ev))
  700. return 0;
  701. }
  702. return 1;
  703. }
  704. void onPuglMouse(const int button, const bool press, int x, int y)
  705. {
  706. DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
  707. // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
  708. if (press && button == 0 && x == 0 && y == 0) return;
  709. if (fModal.childFocus != nullptr)
  710. return fModal.childFocus->focus();
  711. x /= fScaling;
  712. y /= fScaling;
  713. Widget::MouseEvent ev;
  714. ev.button = button;
  715. ev.press = press;
  716. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  717. ev.time = puglGetEventTimestamp(fView);
  718. FOR_EACH_WIDGET_INV(rit)
  719. {
  720. Widget* const widget(*rit);
  721. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  722. if (widget->isVisible() && widget->onMouse(ev))
  723. break;
  724. }
  725. }
  726. void onPuglMotion(int x, int y)
  727. {
  728. // DBGp("PUGL: onMotion : %i %i\n", x, y);
  729. if (fModal.childFocus != nullptr)
  730. return;
  731. x /= fScaling;
  732. y /= fScaling;
  733. Widget::MotionEvent ev;
  734. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  735. ev.time = puglGetEventTimestamp(fView);
  736. FOR_EACH_WIDGET_INV(rit)
  737. {
  738. Widget* const widget(*rit);
  739. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  740. if (widget->isVisible() && widget->onMotion(ev))
  741. break;
  742. }
  743. }
  744. void onPuglScroll(int x, int y, float dx, float dy)
  745. {
  746. DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
  747. if (fModal.childFocus != nullptr)
  748. return;
  749. x /= fScaling;
  750. y /= fScaling;
  751. dx /= fScaling;
  752. dy /= fScaling;
  753. Widget::ScrollEvent ev;
  754. ev.delta = Point<float>(dx, dy);
  755. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  756. ev.time = puglGetEventTimestamp(fView);
  757. FOR_EACH_WIDGET_INV(rit)
  758. {
  759. Widget* const widget(*rit);
  760. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  761. if (widget->isVisible() && widget->onScroll(ev))
  762. break;
  763. }
  764. }
  765. void onPuglReshape(const int width, const int height)
  766. {
  767. DBGp("PUGL: onReshape : %i %i\n", width, height);
  768. if (width <= 1 && height <= 1)
  769. return;
  770. fWidth = static_cast<uint>(width);
  771. fHeight = static_cast<uint>(height);
  772. fSelf->onReshape(fWidth, fHeight);
  773. FOR_EACH_WIDGET(it)
  774. {
  775. Widget* const widget(*it);
  776. if (widget->pData->needsFullViewport)
  777. widget->setSize(fWidth, fHeight);
  778. }
  779. }
  780. void onPuglClose()
  781. {
  782. DBG("PUGL: onClose\n");
  783. if (fModal.enabled)
  784. exec_fini();
  785. fSelf->onClose();
  786. if (fModal.childFocus != nullptr)
  787. fModal.childFocus->fSelf->onClose();
  788. close();
  789. }
  790. // -------------------------------------------------------------------
  791. bool handlePluginKeyboard(const bool press, const uint key)
  792. {
  793. DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
  794. if (fModal.childFocus != nullptr)
  795. {
  796. fModal.childFocus->focus();
  797. return true;
  798. }
  799. Widget::KeyboardEvent ev;
  800. ev.press = press;
  801. ev.key = key;
  802. ev.mod = static_cast<Modifier>(fView->mods);
  803. ev.time = 0;
  804. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  805. ev.key -= 'a' - 'A'; // a-z -> A-Z
  806. FOR_EACH_WIDGET_INV(rit)
  807. {
  808. Widget* const widget(*rit);
  809. if (widget->isVisible() && widget->onKeyboard(ev))
  810. return true;
  811. }
  812. return false;
  813. }
  814. bool handlePluginSpecial(const bool press, const Key key)
  815. {
  816. DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
  817. if (fModal.childFocus != nullptr)
  818. {
  819. fModal.childFocus->focus();
  820. return true;
  821. }
  822. int mods = 0x0;
  823. switch (key)
  824. {
  825. case kKeyShift:
  826. mods |= kModifierShift;
  827. break;
  828. case kKeyControl:
  829. mods |= kModifierControl;
  830. break;
  831. case kKeyAlt:
  832. mods |= kModifierAlt;
  833. break;
  834. default:
  835. break;
  836. }
  837. if (mods != 0x0)
  838. {
  839. if (press)
  840. fView->mods |= mods;
  841. else
  842. fView->mods &= ~(mods);
  843. }
  844. Widget::SpecialEvent ev;
  845. ev.press = press;
  846. ev.key = key;
  847. ev.mod = static_cast<Modifier>(fView->mods);
  848. ev.time = 0;
  849. FOR_EACH_WIDGET_INV(rit)
  850. {
  851. Widget* const widget(*rit);
  852. if (widget->isVisible() && widget->onSpecial(ev))
  853. return true;
  854. }
  855. return false;
  856. }
  857. // -------------------------------------------------------------------
  858. Application& fApp;
  859. Window* fSelf;
  860. Context fContext;
  861. PuglView* fView;
  862. bool fFirstInit;
  863. bool fVisible;
  864. bool fResizable;
  865. bool fUsingEmbed;
  866. uint fWidth;
  867. uint fHeight;
  868. double fScaling;
  869. char* fTitle;
  870. std::list<Widget*> fWidgets;
  871. struct Modal {
  872. bool enabled;
  873. PrivateData* parent;
  874. PrivateData* childFocus;
  875. Modal()
  876. : enabled(false),
  877. parent(nullptr),
  878. childFocus(nullptr) {}
  879. Modal(PrivateData* const p)
  880. : enabled(false),
  881. parent(p),
  882. childFocus(nullptr) {}
  883. ~Modal()
  884. {
  885. DISTRHO_SAFE_ASSERT(! enabled);
  886. DISTRHO_SAFE_ASSERT(childFocus == nullptr);
  887. }
  888. DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
  889. } fModal;
  890. #if defined(DISTRHO_OS_WINDOWS)
  891. HWND hwnd;
  892. HWND hwndParent;
  893. #elif defined(DISTRHO_OS_MAC)
  894. bool fNeedsIdle;
  895. NSView<PuglGenericView>* mView;
  896. id mWindow;
  897. id mParentWindow;
  898. #else
  899. Display* xDisplay;
  900. ::Window xWindow;
  901. #endif
  902. // -------------------------------------------------------------------
  903. // Callbacks
  904. #define handlePtr ((PrivateData*)puglGetHandle(view))
  905. static void onDisplayCallback(PuglView* view)
  906. {
  907. handlePtr->onPuglDisplay();
  908. }
  909. static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
  910. {
  911. return handlePtr->onPuglKeyboard(press, key);
  912. }
  913. static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
  914. {
  915. return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
  916. }
  917. static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
  918. {
  919. handlePtr->onPuglMouse(button, press, x, y);
  920. }
  921. static void onMotionCallback(PuglView* view, int x, int y)
  922. {
  923. handlePtr->onPuglMotion(x, y);
  924. }
  925. static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
  926. {
  927. handlePtr->onPuglScroll(x, y, dx, dy);
  928. }
  929. static void onReshapeCallback(PuglView* view, int width, int height)
  930. {
  931. handlePtr->onPuglReshape(width, height);
  932. }
  933. static void onCloseCallback(PuglView* view)
  934. {
  935. handlePtr->onPuglClose();
  936. }
  937. #ifndef DGL_FILE_BROWSER_DISABLED
  938. static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
  939. {
  940. handlePtr->fSelf->fileBrowserSelected(filename);
  941. }
  942. #endif
  943. #undef handlePtr
  944. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  945. };
  946. // -----------------------------------------------------------------------
  947. // Window
  948. Window::Window(Application& app)
  949. : pData(new PrivateData(app, this)) {}
  950. Window::Window(Application& app, Window& parent)
  951. : pData(new PrivateData(app, this, parent)) {}
  952. Window::Window(Application& app, intptr_t parentId)
  953. : pData(new PrivateData(app, this, parentId)) {}
  954. Window::~Window()
  955. {
  956. delete pData;
  957. }
  958. void Window::show()
  959. {
  960. pData->setVisible(true);
  961. }
  962. void Window::hide()
  963. {
  964. pData->setVisible(false);
  965. }
  966. void Window::close()
  967. {
  968. pData->close();
  969. }
  970. void Window::exec(bool lockWait)
  971. {
  972. pData->exec(lockWait);
  973. }
  974. void Window::focus()
  975. {
  976. pData->focus();
  977. }
  978. void Window::repaint() noexcept
  979. {
  980. puglPostRedisplay(pData->fView);
  981. }
  982. // static int fib_filter_filename_filter(const char* const name)
  983. // {
  984. // return 1;
  985. // (void)name;
  986. // }
  987. #ifndef DGL_FILE_BROWSER_DISABLED
  988. bool Window::openFileBrowser(const FileBrowserOptions& options)
  989. {
  990. # ifdef SOFD_HAVE_X11
  991. using DISTRHO_NAMESPACE::String;
  992. // --------------------------------------------------------------------------
  993. // configure start dir
  994. // TODO: get abspath if needed
  995. // TODO: cross-platform
  996. String startDir(options.startDir);
  997. if (startDir.isEmpty())
  998. {
  999. if (char* const dir_name = get_current_dir_name())
  1000. {
  1001. startDir = dir_name;
  1002. std::free(dir_name);
  1003. }
  1004. }
  1005. DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
  1006. if (! startDir.endsWith('/'))
  1007. startDir += "/";
  1008. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
  1009. // --------------------------------------------------------------------------
  1010. // configure title
  1011. String title(options.title);
  1012. if (title.isEmpty())
  1013. {
  1014. title = pData->getTitle();
  1015. if (title.isEmpty())
  1016. title = "FileBrowser";
  1017. }
  1018. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
  1019. // --------------------------------------------------------------------------
  1020. // configure filters
  1021. x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
  1022. // --------------------------------------------------------------------------
  1023. // configure buttons
  1024. x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
  1025. x_fib_cfg_buttons(1, options.buttons.showHidden-1);
  1026. x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
  1027. // --------------------------------------------------------------------------
  1028. // show
  1029. return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
  1030. # else
  1031. // not implemented
  1032. return false;
  1033. // unused
  1034. (void)options;
  1035. # endif
  1036. }
  1037. #endif
  1038. bool Window::isVisible() const noexcept
  1039. {
  1040. return pData->fVisible;
  1041. }
  1042. void Window::setVisible(bool yesNo)
  1043. {
  1044. pData->setVisible(yesNo);
  1045. }
  1046. bool Window::isResizable() const noexcept
  1047. {
  1048. return pData->fResizable;
  1049. }
  1050. void Window::setResizable(bool yesNo)
  1051. {
  1052. pData->setResizable(yesNo);
  1053. }
  1054. void Window::setGeometryConstraints(uint width, uint height, bool aspect)
  1055. {
  1056. pData->setGeometryConstraints(width, height, aspect);
  1057. }
  1058. uint Window::getWidth() const noexcept
  1059. {
  1060. return pData->fWidth;
  1061. }
  1062. uint Window::getHeight() const noexcept
  1063. {
  1064. return pData->fHeight;
  1065. }
  1066. Size<uint> Window::getSize() const noexcept
  1067. {
  1068. return Size<uint>(pData->fWidth, pData->fHeight);
  1069. }
  1070. void Window::setSize(uint width, uint height)
  1071. {
  1072. pData->setSize(width, height);
  1073. }
  1074. void Window::setSize(Size<uint> size)
  1075. {
  1076. pData->setSize(size.getWidth(), size.getHeight());
  1077. }
  1078. const char* Window::getTitle() const noexcept
  1079. {
  1080. return pData->getTitle();
  1081. }
  1082. void Window::setTitle(const char* title)
  1083. {
  1084. pData->setTitle(title);
  1085. }
  1086. void Window::setTransientWinId(uintptr_t winId)
  1087. {
  1088. pData->setTransientWinId(winId);
  1089. }
  1090. double Window::getScaling() const noexcept
  1091. {
  1092. return pData->getScaling();
  1093. }
  1094. void Window::setScaling(double scaling) noexcept
  1095. {
  1096. pData->setScaling(scaling);
  1097. }
  1098. bool Window::getIgnoringKeyRepeat() const noexcept
  1099. {
  1100. return pData->getIgnoringKeyRepeat();
  1101. }
  1102. void Window::setIgnoringKeyRepeat(bool ignore) noexcept
  1103. {
  1104. pData->setIgnoringKeyRepeat(ignore);
  1105. }
  1106. Application& Window::getApp() const noexcept
  1107. {
  1108. return pData->fApp;
  1109. }
  1110. intptr_t Window::getWindowId() const noexcept
  1111. {
  1112. return puglGetNativeWindow(pData->fView);
  1113. }
  1114. const Context& Window::getContext() const noexcept
  1115. {
  1116. Context& context = pData->fContext;
  1117. #ifdef HAVE_DCAIRO
  1118. if (context.type == kContextCairo)
  1119. context.cairo.graphics = (cairo_t*)puglGetContext(pData->fView);
  1120. #endif
  1121. return context;
  1122. }
  1123. void Window::_addWidget(Widget* const widget)
  1124. {
  1125. pData->addWidget(widget);
  1126. }
  1127. void Window::_removeWidget(Widget* const widget)
  1128. {
  1129. pData->removeWidget(widget);
  1130. }
  1131. void Window::_idle()
  1132. {
  1133. pData->idle();
  1134. }
  1135. // -----------------------------------------------------------------------
  1136. void Window::addIdleCallback(IdleCallback* const callback)
  1137. {
  1138. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1139. pData->fApp.pData->idleCallbacks.push_back(callback);
  1140. }
  1141. void Window::removeIdleCallback(IdleCallback* const callback)
  1142. {
  1143. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1144. pData->fApp.pData->idleCallbacks.remove(callback);
  1145. }
  1146. // -----------------------------------------------------------------------
  1147. void Window::onDisplayBefore()
  1148. {
  1149. #ifdef HAVE_DGL
  1150. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1151. glLoadIdentity();
  1152. #endif
  1153. }
  1154. void Window::onDisplayAfter()
  1155. {
  1156. }
  1157. void Window::onReshape(uint width, uint height)
  1158. {
  1159. #ifdef HAVE_DGL
  1160. glEnable(GL_BLEND);
  1161. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1162. glMatrixMode(GL_PROJECTION);
  1163. glLoadIdentity();
  1164. glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
  1165. glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
  1166. glMatrixMode(GL_MODELVIEW);
  1167. glLoadIdentity();
  1168. #endif
  1169. }
  1170. void Window::onClose()
  1171. {
  1172. }
  1173. #ifndef DGL_FILE_BROWSER_DISABLED
  1174. void Window::fileBrowserSelected(const char*)
  1175. {
  1176. }
  1177. #endif
  1178. bool Window::handlePluginKeyboard(const bool press, const uint key)
  1179. {
  1180. return pData->handlePluginKeyboard(press, key);
  1181. }
  1182. bool Window::handlePluginSpecial(const bool press, const Key key)
  1183. {
  1184. return pData->handlePluginSpecial(press, key);
  1185. }
  1186. // -----------------------------------------------------------------------
  1187. StandaloneWindow::StandaloneWindow()
  1188. : Application(),
  1189. Window((Application&)*this),
  1190. fWidget(nullptr) {}
  1191. void StandaloneWindow::exec()
  1192. {
  1193. Window::show();
  1194. Application::exec();
  1195. }
  1196. void StandaloneWindow::onReshape(uint width, uint height)
  1197. {
  1198. if (fWidget != nullptr)
  1199. fWidget->setSize(width, height);
  1200. Window::onReshape(width, height);
  1201. }
  1202. void StandaloneWindow::_addWidget(Widget* widget)
  1203. {
  1204. if (fWidget == nullptr)
  1205. {
  1206. fWidget = widget;
  1207. fWidget->pData->needsFullViewport = true;
  1208. }
  1209. Window::_addWidget(widget);
  1210. }
  1211. void StandaloneWindow::_removeWidget(Widget* widget)
  1212. {
  1213. if (fWidget == widget)
  1214. {
  1215. fWidget->pData->needsFullViewport = false;
  1216. fWidget = nullptr;
  1217. }
  1218. Window::_removeWidget(widget);
  1219. }
  1220. // -----------------------------------------------------------------------
  1221. END_NAMESPACE_DGL
  1222. #undef DBG
  1223. #undef DBGF