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.

1528 lines
38KB

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