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.

1489 lines
37KB

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