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.

1167 lines
31KB

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