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.

1326 lines
37KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2026 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. #include "WindowPrivateData.hpp"
  17. #include "TopLevelWidgetPrivateData.hpp"
  18. #include "pugl.hpp"
  19. // #define DGL_DEBUG_EVENTS
  20. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  21. # ifdef DISTRHO_PROPER_CPP11_SUPPORT
  22. # include <cinttypes>
  23. # else
  24. # include <inttypes.h>
  25. # endif
  26. #endif
  27. #ifdef DISTRHO_OS_WINDOWS
  28. # include <windows.h>
  29. #endif
  30. START_NAMESPACE_DGL
  31. #ifdef DISTRHO_OS_WINDOWS
  32. # include "pugl-upstream/src/win.h"
  33. #endif
  34. #ifdef DGL_DEBUG_EVENTS
  35. # define DGL_DBG(msg) d_stdout("%s", msg);
  36. # define DGL_DBGp(...) d_stdout(__VA_ARGS__);
  37. #else
  38. # define DGL_DBG(msg)
  39. # define DGL_DBGp(...)
  40. #endif
  41. #define DEFAULT_WIDTH 640
  42. #define DEFAULT_HEIGHT 480
  43. #define FOR_EACH_TOP_LEVEL_WIDGET(it) \
  44. for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it)
  45. #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \
  46. for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit)
  47. // -----------------------------------------------------------------------
  48. static double getScaleFactor(const PuglView* const view)
  49. {
  50. // allow custom scale for testing
  51. if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
  52. return std::max(1.0, std::atof(scale));
  53. if (view != nullptr)
  54. return puglGetScaleFactor(view);
  55. return 1.0;
  56. }
  57. static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView)
  58. {
  59. if (world == nullptr)
  60. return nullptr;
  61. if (PuglView* const view = puglNewView(world))
  62. {
  63. puglSetTransientParent(view, puglGetNativeView(transientParentView));
  64. return view;
  65. }
  66. return nullptr;
  67. }
  68. static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle)
  69. {
  70. if (world == nullptr)
  71. return nullptr;
  72. if (PuglView* const view = puglNewView(world))
  73. {
  74. puglSetParent(view, parentWindowHandle);
  75. if (parentWindowHandle != 0)
  76. puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0);
  77. return view;
  78. }
  79. return nullptr;
  80. }
  81. // -----------------------------------------------------------------------
  82. Window::PrivateData::PrivateData(Application& a, Window* const s)
  83. : app(a),
  84. appData(a.pData),
  85. self(s),
  86. view(appData->world != nullptr ? puglNewView(appData->world) : nullptr),
  87. topLevelWidgets(),
  88. isClosed(true),
  89. isVisible(false),
  90. isEmbed(false),
  91. usesScheduledRepaints(false),
  92. usesSizeRequest(false),
  93. scaleFactor(DGL_NAMESPACE::getScaleFactor(view)),
  94. autoScaling(false),
  95. autoScaleFactor(1.0),
  96. baseWidth(0),
  97. baseHeight(0),
  98. ignoreIdleCallbacks(false),
  99. waitingForClipboardData(false),
  100. waitingForClipboardEvents(false),
  101. clipboardTypeId(0),
  102. filenameToRenderInto(nullptr),
  103. #ifdef DGL_USE_FILE_BROWSER
  104. fileBrowserHandle(nullptr),
  105. #endif
  106. #ifdef DGL_USE_WEB_VIEW
  107. webViewHandle(nullptr),
  108. #endif
  109. modal()
  110. {
  111. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  112. }
  113. Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
  114. : app(a),
  115. appData(a.pData),
  116. self(s),
  117. view(puglNewViewWithTransientParent(appData->world, ppData->view)),
  118. topLevelWidgets(),
  119. isClosed(true),
  120. isVisible(false),
  121. isEmbed(false),
  122. usesScheduledRepaints(false),
  123. usesSizeRequest(false),
  124. scaleFactor(ppData->scaleFactor),
  125. autoScaling(false),
  126. autoScaleFactor(1.0),
  127. baseWidth(0),
  128. baseHeight(0),
  129. ignoreIdleCallbacks(false),
  130. waitingForClipboardData(false),
  131. waitingForClipboardEvents(false),
  132. clipboardTypeId(0),
  133. filenameToRenderInto(nullptr),
  134. #ifdef DGL_USE_FILE_BROWSER
  135. fileBrowserHandle(nullptr),
  136. #endif
  137. #ifdef DGL_USE_WEB_VIEW
  138. webViewHandle(nullptr),
  139. #endif
  140. modal(ppData)
  141. {
  142. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  143. }
  144. Window::PrivateData::PrivateData(Application& a, Window* const s,
  145. const uintptr_t parentWindowHandle,
  146. const double scale, const bool resizable)
  147. : app(a),
  148. appData(a.pData),
  149. self(s),
  150. view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
  151. topLevelWidgets(),
  152. isClosed(parentWindowHandle == 0),
  153. isVisible(parentWindowHandle != 0),
  154. isEmbed(parentWindowHandle != 0),
  155. usesScheduledRepaints(false),
  156. usesSizeRequest(false),
  157. scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)),
  158. autoScaling(false),
  159. autoScaleFactor(1.0),
  160. baseWidth(0),
  161. baseHeight(0),
  162. ignoreIdleCallbacks(false),
  163. waitingForClipboardData(false),
  164. waitingForClipboardEvents(false),
  165. clipboardTypeId(0),
  166. filenameToRenderInto(nullptr),
  167. #ifdef DGL_USE_FILE_BROWSER
  168. fileBrowserHandle(nullptr),
  169. #endif
  170. #ifdef DGL_USE_WEB_VIEW
  171. webViewHandle(nullptr),
  172. #endif
  173. modal()
  174. {
  175. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
  176. }
  177. Window::PrivateData::PrivateData(Application& a, Window* const s,
  178. const uintptr_t parentWindowHandle,
  179. const uint width, const uint height,
  180. const double scale, const bool resizable,
  181. const bool _usesScheduledRepaints,
  182. const bool _usesSizeRequest)
  183. : app(a),
  184. appData(a.pData),
  185. self(s),
  186. view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
  187. topLevelWidgets(),
  188. isClosed(parentWindowHandle == 0),
  189. isVisible(parentWindowHandle != 0 && view != nullptr),
  190. isEmbed(parentWindowHandle != 0),
  191. usesScheduledRepaints(_usesScheduledRepaints),
  192. usesSizeRequest(_usesSizeRequest),
  193. scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)),
  194. autoScaling(false),
  195. autoScaleFactor(1.0),
  196. baseWidth(0),
  197. baseHeight(0),
  198. ignoreIdleCallbacks(false),
  199. waitingForClipboardData(false),
  200. waitingForClipboardEvents(false),
  201. clipboardTypeId(0),
  202. filenameToRenderInto(nullptr),
  203. #ifdef DGL_USE_FILE_BROWSER
  204. fileBrowserHandle(nullptr),
  205. #endif
  206. #ifdef DGL_USE_WEB_VIEW
  207. webViewHandle(nullptr),
  208. #endif
  209. modal()
  210. {
  211. initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable);
  212. }
  213. Window::PrivateData::~PrivateData()
  214. {
  215. appData->idleCallbacks.remove(this);
  216. appData->windows.remove(self);
  217. std::free(filenameToRenderInto);
  218. if (view == nullptr)
  219. return;
  220. if (isEmbed)
  221. {
  222. #ifdef DGL_USE_FILE_BROWSER
  223. if (fileBrowserHandle != nullptr)
  224. fileBrowserClose(fileBrowserHandle);
  225. #endif
  226. #ifdef DGL_USE_WEB_VIEW
  227. if (webViewHandle != nullptr)
  228. webViewDestroy(webViewHandle);
  229. #endif
  230. puglHide(view);
  231. appData->oneWindowClosed();
  232. isClosed = true;
  233. isVisible = false;
  234. }
  235. destroyContext();
  236. puglFreeView(view);
  237. }
  238. // -----------------------------------------------------------------------
  239. void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable)
  240. {
  241. appData->windows.push_back(self);
  242. appData->idleCallbacks.push_back(this);
  243. memset(graphicsContext, 0, sizeof(graphicsContext));
  244. if (view == nullptr)
  245. {
  246. d_stderr2("Failed to create Pugl view, everything will fail!");
  247. return;
  248. }
  249. puglSetMatchingBackendForCurrentBuild(view);
  250. puglSetHandle(view, this);
  251. puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
  252. puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
  253. #if defined(DGL_USE_RGBA) && DGL_USE_RGBA
  254. puglSetViewHint(view, PUGL_DEPTH_BITS, 24);
  255. #else
  256. puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
  257. #endif
  258. puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
  259. // PUGL_SAMPLES ??
  260. puglSetEventFunc(view, puglEventCallback);
  261. // setting size triggers system-level calls, do it last
  262. puglSetSizeAndDefault(view, width, height);
  263. }
  264. bool Window::PrivateData::initPost()
  265. {
  266. if (view == nullptr)
  267. return false;
  268. // create view now, as a few methods we allow devs to use require it
  269. if (puglRealize(view) != PUGL_SUCCESS)
  270. {
  271. view = nullptr;
  272. d_stderr2("Failed to realize Pugl view, everything will fail!");
  273. return false;
  274. }
  275. if (isEmbed)
  276. {
  277. appData->oneWindowShown();
  278. puglShow(view, PUGL_SHOW_PASSIVE);
  279. }
  280. return true;
  281. }
  282. // -----------------------------------------------------------------------
  283. void Window::PrivateData::close()
  284. {
  285. DGL_DBG("Window close\n");
  286. // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
  287. if (isEmbed || isClosed)
  288. return;
  289. isClosed = true;
  290. hide();
  291. appData->oneWindowClosed();
  292. }
  293. // -----------------------------------------------------------------------
  294. void Window::PrivateData::show()
  295. {
  296. if (isVisible)
  297. {
  298. DGL_DBG("Window show matches current visible state, ignoring request\n");
  299. return;
  300. }
  301. if (isEmbed)
  302. {
  303. DGL_DBG("Window show cannot be called when embedded\n");
  304. return;
  305. }
  306. DGL_DBG("Window show called\n");
  307. if (view == nullptr)
  308. return;
  309. if (isClosed)
  310. {
  311. isClosed = false;
  312. appData->oneWindowShown();
  313. #if defined(DISTRHO_OS_WINDOWS)
  314. puglWin32ShowCentered(view);
  315. #elif defined(DISTRHO_OS_MAC)
  316. puglMacOSShowCentered(view);
  317. #else
  318. puglShow(view, PUGL_SHOW_RAISE);
  319. #endif
  320. }
  321. else
  322. {
  323. #ifdef DISTRHO_OS_WINDOWS
  324. puglWin32RestoreWindow(view);
  325. #else
  326. puglShow(view, PUGL_SHOW_RAISE);
  327. #endif
  328. }
  329. isVisible = true;
  330. }
  331. void Window::PrivateData::hide()
  332. {
  333. if (isEmbed)
  334. {
  335. DGL_DBG("Window hide cannot be called when embedded\n");
  336. return;
  337. }
  338. if (! isVisible)
  339. {
  340. DGL_DBG("Window hide matches current visible state, ignoring request\n");
  341. return;
  342. }
  343. DGL_DBG("Window hide called\n");
  344. if (modal.enabled)
  345. stopModal();
  346. #ifdef DGL_USE_FILE_BROWSER
  347. if (fileBrowserHandle != nullptr)
  348. {
  349. fileBrowserClose(fileBrowserHandle);
  350. fileBrowserHandle = nullptr;
  351. }
  352. #endif
  353. #ifdef DGL_USE_WEB_VIEW
  354. if (webViewHandle != nullptr)
  355. {
  356. webViewDestroy(webViewHandle);
  357. webViewHandle = nullptr;
  358. }
  359. #endif
  360. puglHide(view);
  361. isVisible = false;
  362. }
  363. // -----------------------------------------------------------------------
  364. void Window::PrivateData::focus()
  365. {
  366. if (view == nullptr)
  367. return;
  368. if (! isEmbed)
  369. puglRaiseWindow(view);
  370. puglGrabFocus(view);
  371. }
  372. // -----------------------------------------------------------------------
  373. void Window::PrivateData::setResizable(const bool resizable)
  374. {
  375. DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,);
  376. DGL_DBG("Window setResizable called\n");
  377. puglSetResizable(view, resizable);
  378. }
  379. // --------------------------------------------------------------------------------------------------------------------
  380. const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
  381. {
  382. return reinterpret_cast<const GraphicsContext&>(graphicsContext);
  383. }
  384. // -----------------------------------------------------------------------
  385. void Window::PrivateData::idleCallback()
  386. {
  387. #ifdef DGL_USE_FILE_BROWSER
  388. if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle))
  389. {
  390. self->onFileSelected(fileBrowserGetPath(fileBrowserHandle));
  391. fileBrowserClose(fileBrowserHandle);
  392. fileBrowserHandle = nullptr;
  393. }
  394. #endif
  395. #ifdef DGL_USE_WEB_VIEW
  396. if (webViewHandle != nullptr)
  397. webViewIdle(webViewHandle);
  398. #endif
  399. }
  400. // -----------------------------------------------------------------------
  401. // idle callback stuff
  402. bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
  403. {
  404. if (ignoreIdleCallbacks || view == nullptr)
  405. return false;
  406. if (timerFrequencyInMs == 0)
  407. {
  408. appData->idleCallbacks.push_back(callback);
  409. return true;
  410. }
  411. return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS;
  412. }
  413. bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
  414. {
  415. if (ignoreIdleCallbacks || view == nullptr)
  416. return false;
  417. if (std::find(appData->idleCallbacks.begin(),
  418. appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
  419. {
  420. appData->idleCallbacks.remove(callback);
  421. return true;
  422. }
  423. return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
  424. }
  425. #ifdef DGL_USE_FILE_BROWSER
  426. // -----------------------------------------------------------------------
  427. // file browser dialog
  428. bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
  429. {
  430. if (fileBrowserHandle != nullptr)
  431. fileBrowserClose(fileBrowserHandle);
  432. FileBrowserOptions options2 = options;
  433. if (options2.title == nullptr)
  434. options2.title = puglGetViewString(view, PUGL_WINDOW_TITLE);
  435. options2.className = puglGetViewString(view, PUGL_CLASS_NAME);
  436. fileBrowserHandle = fileBrowserCreate(isEmbed,
  437. puglGetNativeView(view),
  438. #if DGL_ALLOW_DEPRECATED_METHODS
  439. autoScaling ? autoScaleFactor :
  440. #endif
  441. scaleFactor,
  442. options2);
  443. return fileBrowserHandle != nullptr;
  444. }
  445. #endif // DGL_USE_FILE_BROWSER
  446. #ifdef DGL_USE_WEB_VIEW
  447. // -----------------------------------------------------------------------
  448. // file browser dialog
  449. bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPACE::WebViewOptions& options)
  450. {
  451. if (webViewHandle != nullptr)
  452. webViewDestroy(webViewHandle);
  453. const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
  454. uint initialWidth = size.width - options.offset.x;
  455. uint initialHeight = size.height - options.offset.y;
  456. webViewOffset = Point<int>(options.offset.x, options.offset.y);
  457. webViewHandle = webViewCreate(url,
  458. puglGetNativeView(view),
  459. initialWidth,
  460. initialHeight,
  461. #if DGL_ALLOW_DEPRECATED_METHODS
  462. autoScaling ? autoScaleFactor :
  463. #endif
  464. scaleFactor,
  465. options);
  466. return webViewHandle != nullptr;
  467. }
  468. #endif // DGL_USE_WEB_VIEW
  469. // -----------------------------------------------------------------------
  470. // modal handling
  471. void Window::PrivateData::startModal()
  472. {
  473. DGL_DBG("Window modal loop starting...");
  474. DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());
  475. // activate modal mode for this window
  476. modal.enabled = true;
  477. // make parent give focus to us
  478. modal.parent->modal.child = this;
  479. // make sure both parent and ourselves are visible
  480. modal.parent->show();
  481. show();
  482. #ifdef DISTRHO_OS_MAC
  483. puglMacOSAddChildWindow(modal.parent->view, view);
  484. #endif
  485. DGL_DBG("Ok\n");
  486. }
  487. void Window::PrivateData::stopModal()
  488. {
  489. DGL_DBG("Window modal loop stopping...");
  490. // deactivate modal mode
  491. modal.enabled = false;
  492. // safety checks, make sure we have a parent and we are currently active as the child to give focus to
  493. if (modal.parent == nullptr)
  494. return;
  495. if (modal.parent->modal.child != this)
  496. return;
  497. #ifdef DISTRHO_OS_MAC
  498. puglMacOSRemoveChildWindow(modal.parent->view, view);
  499. #endif
  500. // stop parent from giving focus to us, so it behaves like normal
  501. modal.parent->modal.child = nullptr;
  502. // refocus main window after closing child
  503. if (! modal.parent->isClosed)
  504. {
  505. const Widget::MotionEvent ev;
  506. modal.parent->onPuglMotion(ev);
  507. modal.parent->focus();
  508. }
  509. DGL_DBG("Ok\n");
  510. }
  511. void Window::PrivateData::runAsModal(const bool blockWait)
  512. {
  513. DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait);
  514. startModal();
  515. if (blockWait)
  516. {
  517. DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,);
  518. while (isVisible && modal.enabled)
  519. appData->idle(10);
  520. stopModal();
  521. }
  522. else
  523. {
  524. appData->idle(0);
  525. }
  526. }
  527. // -----------------------------------------------------------------------
  528. // pugl events
  529. void Window::PrivateData::onPuglConfigure(const uint width, const uint height)
  530. {
  531. DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,);
  532. DGL_DBGp("PUGL: onReshape : %d %d\n", width, height);
  533. createContextIfNeeded();
  534. if (autoScaling)
  535. {
  536. const double scaleHorizontal = width / static_cast<double>(baseWidth);
  537. const double scaleVertical = height / static_cast<double>(baseHeight);
  538. autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical;
  539. }
  540. else
  541. {
  542. autoScaleFactor = 1.0;
  543. }
  544. const uint uwidth = d_roundToUnsignedInt(width / autoScaleFactor);
  545. const uint uheight = d_roundToUnsignedInt(height / autoScaleFactor);
  546. #ifdef DGL_USE_WEB_VIEW
  547. if (webViewHandle != nullptr)
  548. webViewResize(webViewHandle,
  549. uwidth - webViewOffset.getX(),
  550. uheight - webViewOffset.getY(),
  551. #if DGL_ALLOW_DEPRECATED_METHODS
  552. autoScaling ? autoScaleFactor :
  553. #endif
  554. scaleFactor);
  555. #endif
  556. #if DGL_ALLOW_DEPRECATED_METHODS
  557. #if defined(_MSC_VER)
  558. #pragma warning(push)
  559. #pragma warning(disable:4996)
  560. #elif defined(__clang__)
  561. #pragma clang diagnostic push
  562. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  563. #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
  564. #pragma GCC diagnostic push
  565. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  566. #endif
  567. self->onReshape(uwidth, uheight);
  568. #if defined(_MSC_VER)
  569. #pragma warning(pop)
  570. #elif defined(__clang__)
  571. #pragma clang diagnostic pop
  572. #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
  573. #pragma GCC diagnostic pop
  574. #endif
  575. #endif
  576. FOR_EACH_TOP_LEVEL_WIDGET(it)
  577. {
  578. TopLevelWidget* const widget = *it;
  579. /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one.
  580. * This is because we want TopLevelWidget::setSize to handle both window and widget size,
  581. * but we dont want to change window size here, because we are the window..
  582. * So we just call the Widget specific method manually.
  583. *
  584. * Alternatively, we could expose a resize function on the pData, like done with the display function.
  585. * But there is nothing extra we need to do in there, so this works fine.
  586. */
  587. ((Widget*)widget)->setSize(uwidth, uheight);
  588. }
  589. // always repaint after a resize
  590. puglObscureView(view);
  591. }
  592. void Window::PrivateData::onPuglExpose()
  593. {
  594. // DGL_DBG("PUGL: onPuglExpose\n");
  595. puglOnDisplayPrepare(view);
  596. startContext();
  597. FOR_EACH_TOP_LEVEL_WIDGET(it)
  598. {
  599. TopLevelWidget* const widget(*it);
  600. if (widget->isVisible())
  601. widget->pData->display();
  602. }
  603. if (char* const filename = filenameToRenderInto)
  604. {
  605. const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
  606. filenameToRenderInto = nullptr;
  607. renderToPicture(filename, getGraphicsContext(), size.width, size.height);
  608. std::free(filename);
  609. }
  610. endContext();
  611. }
  612. void Window::PrivateData::onPuglClose()
  613. {
  614. DGL_DBG("PUGL: onClose\n");
  615. #ifndef DISTRHO_OS_MAC
  616. // if we are running as standalone we can prevent closing in certain conditions
  617. if (appData->isStandalone)
  618. {
  619. // a child window is active, gives focus to it
  620. if (modal.child != nullptr)
  621. return modal.child->focus();
  622. // ask window if we should close
  623. if (! self->onClose())
  624. return;
  625. }
  626. #endif
  627. if (modal.enabled)
  628. stopModal();
  629. if (modal.child != nullptr)
  630. {
  631. modal.child->close();
  632. modal.child = nullptr;
  633. }
  634. close();
  635. }
  636. void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
  637. {
  638. DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed);
  639. if (isClosed)
  640. return;
  641. if (modal.child != nullptr)
  642. return modal.child->focus();
  643. self->onFocus(focus, mode);
  644. }
  645. void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
  646. {
  647. DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode);
  648. if (modal.child != nullptr)
  649. return modal.child->focus();
  650. FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
  651. {
  652. TopLevelWidget* const widget(*rit);
  653. if (widget->isVisible() && widget->onKeyboard(ev))
  654. break;
  655. }
  656. }
  657. void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
  658. {
  659. DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string);
  660. if (modal.child != nullptr)
  661. return modal.child->focus();
  662. FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
  663. {
  664. TopLevelWidget* const widget(*rit);
  665. if (widget->isVisible() && widget->onCharacterInput(ev))
  666. break;
  667. }
  668. }
  669. void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
  670. {
  671. DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY());
  672. if (modal.child != nullptr)
  673. return modal.child->focus();
  674. FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
  675. {
  676. TopLevelWidget* const widget(*rit);
  677. if (widget->isVisible() && widget->onMouse(ev))
  678. break;
  679. }
  680. }
  681. void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
  682. {
  683. DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY());
  684. if (modal.child != nullptr)
  685. return modal.child->focus();
  686. FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
  687. {
  688. TopLevelWidget* const widget(*rit);
  689. if (widget->isVisible() && widget->onMotion(ev))
  690. break;
  691. }
  692. }
  693. void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
  694. {
  695. DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY());
  696. if (modal.child != nullptr)
  697. return modal.child->focus();
  698. FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
  699. {
  700. TopLevelWidget* const widget(*rit);
  701. if (widget->isVisible() && widget->onScroll(ev))
  702. break;
  703. }
  704. }
  705. const void* Window::PrivateData::getClipboard(size_t& dataSize)
  706. {
  707. clipboardTypeId = 0;
  708. waitingForClipboardData = true,
  709. waitingForClipboardEvents = true;
  710. // begin clipboard dance here
  711. if (puglPaste(view) != PUGL_SUCCESS)
  712. {
  713. dataSize = 0;
  714. waitingForClipboardEvents = false;
  715. return nullptr;
  716. }
  717. #ifdef DGL_USING_X11
  718. // wait for type request, clipboardTypeId must be != 0 to be valid
  719. int retry = static_cast<int>(2 / 0.03);
  720. while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0)
  721. {
  722. if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
  723. break;
  724. }
  725. #endif
  726. if (clipboardTypeId == 0)
  727. {
  728. dataSize = 0;
  729. waitingForClipboardEvents = false;
  730. return nullptr;
  731. }
  732. #ifdef DGL_USING_X11
  733. // wait for actual data (assumes offer was accepted)
  734. retry = static_cast<int>(2 / 0.03);
  735. while (waitingForClipboardData && --retry >= 0)
  736. {
  737. if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
  738. break;
  739. }
  740. #endif
  741. if (clipboardTypeId == 0)
  742. {
  743. dataSize = 0;
  744. waitingForClipboardEvents = false;
  745. return nullptr;
  746. }
  747. waitingForClipboardEvents = false;
  748. return puglGetClipboard(view, clipboardTypeId - 1, &dataSize);
  749. }
  750. uint32_t Window::PrivateData::onClipboardDataOffer()
  751. {
  752. DGL_DBG("onClipboardDataOffer\n");
  753. if ((clipboardTypeId = self->onClipboardDataOffer()) != 0)
  754. return clipboardTypeId;
  755. // stop waiting for data, it was rejected
  756. waitingForClipboardData = false;
  757. return 0;
  758. }
  759. void Window::PrivateData::onClipboardData(const uint32_t typeId)
  760. {
  761. if (clipboardTypeId != typeId)
  762. clipboardTypeId = 0;
  763. waitingForClipboardData = false;
  764. }
  765. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  766. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
  767. #endif
  768. PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
  769. {
  770. Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
  771. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  772. if (event->type != PUGL_TIMER && event->type != PUGL_EXPOSE && event->type != PUGL_MOTION) {
  773. printEvent(event, "pugl event: ", true);
  774. }
  775. #endif
  776. if (pData->waitingForClipboardEvents)
  777. {
  778. switch (event->type)
  779. {
  780. case PUGL_UPDATE:
  781. case PUGL_EXPOSE:
  782. case PUGL_FOCUS_IN:
  783. case PUGL_FOCUS_OUT:
  784. case PUGL_KEY_PRESS:
  785. case PUGL_KEY_RELEASE:
  786. case PUGL_TEXT:
  787. case PUGL_POINTER_IN:
  788. case PUGL_POINTER_OUT:
  789. case PUGL_BUTTON_PRESS:
  790. case PUGL_BUTTON_RELEASE:
  791. case PUGL_MOTION:
  792. case PUGL_SCROLL:
  793. case PUGL_TIMER:
  794. case PUGL_LOOP_ENTER:
  795. case PUGL_LOOP_LEAVE:
  796. return PUGL_SUCCESS;
  797. case PUGL_DATA_OFFER:
  798. case PUGL_DATA:
  799. break;
  800. default:
  801. d_stdout("Got event %d while waitingForClipboardEvents", event->type);
  802. break;
  803. }
  804. }
  805. switch (event->type)
  806. {
  807. ///< No event
  808. case PUGL_NOTHING:
  809. break;
  810. ///< View realized, a #PuglRealizeEvent
  811. case PUGL_REALIZE:
  812. if (! pData->isEmbed && ! puglGetTransientParent(view))
  813. {
  814. #if defined(DISTRHO_OS_WINDOWS) && defined(DGL_WINDOWS_ICON_ID)
  815. WNDCLASSEX wClass = {};
  816. const HINSTANCE hInstance = GetModuleHandle(nullptr);
  817. if (GetClassInfoEx(hInstance, view->world->strings[PUGL_CLASS_NAME], &wClass))
  818. wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID));
  819. SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)));
  820. #endif
  821. #ifdef DGL_USING_X11
  822. puglX11SetWindowType(view, pData->appData->isStandalone);
  823. #endif
  824. }
  825. break;
  826. ///< View unrealizeed, a #PuglUnrealizeEvent
  827. case PUGL_UNREALIZE:
  828. break;
  829. ///< View configured, a #PuglConfigureEvent
  830. case PUGL_CONFIGURE:
  831. // unused x, y (double)
  832. pData->onPuglConfigure(event->configure.width, event->configure.height);
  833. break;
  834. ///< View ready to draw, a #PuglUpdateEvent
  835. case PUGL_UPDATE:
  836. break;
  837. ///< View must be drawn, a #PuglExposeEvent
  838. case PUGL_EXPOSE:
  839. // unused x, y, width, height (double)
  840. pData->onPuglExpose();
  841. break;
  842. ///< View will be closed, a #PuglCloseEvent
  843. case PUGL_CLOSE:
  844. pData->onPuglClose();
  845. break;
  846. ///< Keyboard focus entered view, a #PuglFocusEvent
  847. case PUGL_FOCUS_IN:
  848. ///< Keyboard focus left view, a #PuglFocusEvent
  849. case PUGL_FOCUS_OUT:
  850. pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
  851. static_cast<CrossingMode>(event->focus.mode));
  852. break;
  853. ///< Key pressed, a #PuglKeyEvent
  854. case PUGL_KEY_PRESS:
  855. ///< Key released, a #PuglKeyEvent
  856. case PUGL_KEY_RELEASE:
  857. {
  858. // unused x, y, xRoot, yRoot (double)
  859. Widget::KeyboardEvent ev;
  860. ev.mod = event->key.state;
  861. ev.flags = event->key.flags;
  862. ev.time = d_roundToUnsignedInt(event->key.time * 1000.0);
  863. ev.press = event->type == PUGL_KEY_PRESS;
  864. ev.key = event->key.key;
  865. ev.keycode = event->key.keycode;
  866. // keyboard events must always be lowercase
  867. if (ev.key >= 'A' && ev.key <= 'Z')
  868. {
  869. ev.key += 'a' - 'A'; // A-Z -> a-z
  870. ev.mod |= kModifierShift;
  871. }
  872. pData->onPuglKey(ev);
  873. break;
  874. }
  875. ///< Character entered, a #PuglTextEvent
  876. case PUGL_TEXT:
  877. {
  878. // unused x, y, xRoot, yRoot (double)
  879. Widget::CharacterInputEvent ev;
  880. ev.mod = event->text.state;
  881. ev.flags = event->text.flags;
  882. ev.time = d_roundToUnsignedInt(event->text.time * 1000.0);
  883. ev.keycode = event->text.keycode;
  884. ev.character = event->text.character;
  885. std::strncpy(ev.string, event->text.string, sizeof(ev.string));
  886. pData->onPuglText(ev);
  887. break;
  888. }
  889. ///< Pointer entered view, a #PuglCrossingEvent
  890. case PUGL_POINTER_IN:
  891. break;
  892. ///< Pointer left view, a #PuglCrossingEvent
  893. case PUGL_POINTER_OUT:
  894. break;
  895. ///< Mouse button pressed, a #PuglButtonEvent
  896. case PUGL_BUTTON_PRESS:
  897. ///< Mouse button released, a #PuglButtonEvent
  898. case PUGL_BUTTON_RELEASE:
  899. {
  900. Widget::MouseEvent ev;
  901. ev.mod = event->button.state;
  902. ev.flags = event->button.flags;
  903. ev.time = d_roundToUnsignedInt(event->button.time * 1000.0);
  904. ev.button = event->button.button + 1;
  905. ev.press = event->type == PUGL_BUTTON_PRESS;
  906. #if DGL_ALLOW_DEPRECATED_METHODS
  907. if (pData->autoScaling && 0)
  908. {
  909. const double scaleFactor = pData->autoScaleFactor;
  910. ev.pos = Point<double>(event->button.x / scaleFactor, event->button.y / scaleFactor);
  911. }
  912. else
  913. #endif
  914. {
  915. ev.pos = Point<double>(event->button.x, event->button.y);
  916. }
  917. ev.absolutePos = ev.pos;
  918. pData->onPuglMouse(ev);
  919. break;
  920. }
  921. ///< Pointer moved, a #PuglMotionEvent
  922. case PUGL_MOTION:
  923. {
  924. Widget::MotionEvent ev;
  925. ev.mod = event->motion.state;
  926. ev.flags = event->motion.flags;
  927. ev.time = d_roundToUnsignedInt(event->motion.time * 1000.0);
  928. #if DGL_ALLOW_DEPRECATED_METHODS
  929. if (pData->autoScaling && 0)
  930. {
  931. const double scaleFactor = pData->autoScaleFactor;
  932. ev.pos = Point<double>(event->motion.x / scaleFactor, event->motion.y / scaleFactor);
  933. }
  934. else
  935. #endif
  936. {
  937. ev.pos = Point<double>(event->motion.x, event->motion.y);
  938. }
  939. ev.absolutePos = ev.pos;
  940. pData->onPuglMotion(ev);
  941. break;
  942. }
  943. ///< Scrolled, a #PuglScrollEvent
  944. case PUGL_SCROLL:
  945. {
  946. Widget::ScrollEvent ev;
  947. ev.mod = event->scroll.state;
  948. ev.flags = event->scroll.flags;
  949. ev.time = d_roundToUnsignedInt(event->scroll.time * 1000.0);
  950. #if DGL_ALLOW_DEPRECATED_METHODS
  951. if (pData->autoScaling && 0)
  952. {
  953. const double scaleFactor = pData->autoScaleFactor;
  954. ev.pos = Point<double>(event->scroll.x / scaleFactor, event->scroll.y / scaleFactor);
  955. ev.delta = Point<double>(event->scroll.dx / scaleFactor, event->scroll.dy / scaleFactor);
  956. }
  957. else
  958. #endif
  959. {
  960. ev.pos = Point<double>(event->scroll.x, event->scroll.y);
  961. ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
  962. }
  963. ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
  964. ev.absolutePos = ev.pos;
  965. pData->onPuglScroll(ev);
  966. break;
  967. }
  968. ///< Custom client message, a #PuglClientEvent
  969. case PUGL_CLIENT:
  970. break;
  971. ///< Timer triggered, a #PuglTimerEvent
  972. case PUGL_TIMER:
  973. if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
  974. idleCallback->idleCallback();
  975. break;
  976. ///< Recursive loop left, a #PuglLoopLeaveEvent
  977. case PUGL_LOOP_ENTER:
  978. break;
  979. ///< Recursive loop left, a #PuglEventLoopLeave
  980. case PUGL_LOOP_LEAVE:
  981. break;
  982. ///< Data offered from clipboard, a #PuglDataOfferEvent
  983. case PUGL_DATA_OFFER:
  984. if (const uint32_t offerTypeId = pData->onClipboardDataOffer())
  985. puglAcceptOffer(view, &event->offer, offerTypeId - 1);
  986. break;
  987. ///< Data available from clipboard, a #PuglDataEvent
  988. case PUGL_DATA:
  989. pData->onClipboardData(event->data.typeIndex + 1);
  990. break;
  991. }
  992. return PUGL_SUCCESS;
  993. }
  994. // -----------------------------------------------------------------------
  995. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  996. static int printModifiers(const uint32_t mods)
  997. {
  998. return fprintf(stderr, "Modifiers:%s%s%s%s\n",
  999. (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
  1000. (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
  1001. (mods & PUGL_MOD_ALT) ? " Alt" : "",
  1002. (mods & PUGL_MOD_SUPER) ? " Super" : "");
  1003. }
  1004. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
  1005. {
  1006. #define FFMT "%6.1f"
  1007. #define PFMT FFMT " " FFMT
  1008. #define PRINT(fmt, ...) d_stdout(fmt, __VA_ARGS__), 1
  1009. switch (event->type) {
  1010. case PUGL_NOTHING:
  1011. return 0;
  1012. case PUGL_KEY_PRESS:
  1013. return PRINT("%sKey press code %3u key U+%04X\n",
  1014. prefix,
  1015. event->key.keycode,
  1016. event->key.key);
  1017. case PUGL_KEY_RELEASE:
  1018. return PRINT("%sKey release code %3u key U+%04X\n",
  1019. prefix,
  1020. event->key.keycode,
  1021. event->key.key);
  1022. case PUGL_TEXT:
  1023. return PRINT("%sText entry code %3u char U+%04X (%s)\n",
  1024. prefix,
  1025. event->text.keycode,
  1026. event->text.character,
  1027. event->text.string);
  1028. case PUGL_BUTTON_PRESS:
  1029. case PUGL_BUTTON_RELEASE:
  1030. return (PRINT("%sMouse %u %s at " PFMT " ",
  1031. prefix,
  1032. event->button.button,
  1033. (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
  1034. event->button.x,
  1035. event->button.y) +
  1036. printModifiers(event->scroll.state));
  1037. case PUGL_SCROLL:
  1038. return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
  1039. prefix,
  1040. event->scroll.dx,
  1041. event->scroll.dy,
  1042. event->scroll.x,
  1043. event->scroll.y) +
  1044. printModifiers(event->scroll.state));
  1045. case PUGL_POINTER_IN:
  1046. return PRINT("%sMouse enter at " PFMT "\n",
  1047. prefix,
  1048. event->crossing.x,
  1049. event->crossing.y);
  1050. case PUGL_POINTER_OUT:
  1051. return PRINT("%sMouse leave at " PFMT "\n",
  1052. prefix,
  1053. event->crossing.x,
  1054. event->crossing.y);
  1055. case PUGL_FOCUS_IN:
  1056. return PRINT("%sFocus in %i\n",
  1057. prefix,
  1058. event->focus.mode);
  1059. case PUGL_FOCUS_OUT:
  1060. return PRINT("%sFocus out %i\n",
  1061. prefix,
  1062. event->focus.mode);
  1063. case PUGL_CLIENT:
  1064. return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
  1065. prefix,
  1066. event->client.data1,
  1067. event->client.data2);
  1068. case PUGL_TIMER:
  1069. return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
  1070. default:
  1071. break;
  1072. }
  1073. if (verbose) {
  1074. switch (event->type) {
  1075. case PUGL_REALIZE:
  1076. return PRINT("%sRealize\n", prefix);
  1077. case PUGL_UNREALIZE:
  1078. return PRINT("%sUnrealize\n", prefix);
  1079. case PUGL_CONFIGURE:
  1080. return PRINT("%sConfigure %d %d %d %d\n",
  1081. prefix,
  1082. event->configure.x,
  1083. event->configure.y,
  1084. event->configure.width,
  1085. event->configure.height);
  1086. case PUGL_UPDATE:
  1087. return 0; // fprintf(stderr, "%sUpdate\n", prefix);
  1088. case PUGL_EXPOSE:
  1089. return PRINT("%sExpose %d %d %d %d\n",
  1090. prefix,
  1091. event->expose.x,
  1092. event->expose.y,
  1093. event->expose.width,
  1094. event->expose.height);
  1095. case PUGL_CLOSE:
  1096. return PRINT("%sClose\n", prefix);
  1097. case PUGL_MOTION:
  1098. return PRINT("%sMouse motion at " PFMT "\n",
  1099. prefix,
  1100. event->motion.x,
  1101. event->motion.y);
  1102. default:
  1103. return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
  1104. }
  1105. }
  1106. #undef PRINT
  1107. #undef PFMT
  1108. #undef FFMT
  1109. return 0;
  1110. }
  1111. #endif
  1112. #undef DGL_DBG
  1113. #undef DGL_DBGF
  1114. // -----------------------------------------------------------------------
  1115. END_NAMESPACE_DGL