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.

1235 lines
34KB

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