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.

1192 lines
32KB

  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. #ifdef 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. waitingForClipboardData(false),
  92. waitingForClipboardEvents(false),
  93. clipboardTypeId(0),
  94. filenameToRenderInto(nullptr),
  95. #ifndef DGL_FILE_BROWSER_DISABLED
  96. fileBrowserHandle(nullptr),
  97. #endif
  98. modal()
  99. {
  100. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  101. }
  102. Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
  103. : app(a),
  104. appData(a.pData),
  105. self(s),
  106. view(puglNewViewWithTransientParent(appData->world, ppData->view)),
  107. topLevelWidgets(),
  108. isClosed(true),
  109. isVisible(false),
  110. isEmbed(false),
  111. usesSizeRequest(false),
  112. scaleFactor(ppData->scaleFactor),
  113. autoScaling(false),
  114. autoScaleFactor(1.0),
  115. minWidth(0),
  116. minHeight(0),
  117. keepAspectRatio(false),
  118. ignoreIdleCallbacks(false),
  119. waitingForClipboardData(false),
  120. waitingForClipboardEvents(false),
  121. clipboardTypeId(0),
  122. filenameToRenderInto(nullptr),
  123. #ifndef DGL_FILE_BROWSER_DISABLED
  124. fileBrowserHandle(nullptr),
  125. #endif
  126. modal(ppData)
  127. {
  128. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  129. }
  130. Window::PrivateData::PrivateData(Application& a, Window* const s,
  131. const uintptr_t parentWindowHandle,
  132. const double scale, const bool resizable)
  133. : app(a),
  134. appData(a.pData),
  135. self(s),
  136. view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
  137. topLevelWidgets(),
  138. isClosed(parentWindowHandle == 0),
  139. isVisible(parentWindowHandle != 0),
  140. isEmbed(parentWindowHandle != 0),
  141. usesSizeRequest(false),
  142. scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)),
  143. autoScaling(false),
  144. autoScaleFactor(1.0),
  145. minWidth(0),
  146. minHeight(0),
  147. keepAspectRatio(false),
  148. ignoreIdleCallbacks(false),
  149. waitingForClipboardData(false),
  150. waitingForClipboardEvents(false),
  151. clipboardTypeId(0),
  152. filenameToRenderInto(nullptr),
  153. #ifndef DGL_FILE_BROWSER_DISABLED
  154. fileBrowserHandle(nullptr),
  155. #endif
  156. modal()
  157. {
  158. initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
  159. }
  160. Window::PrivateData::PrivateData(Application& a, Window* const s,
  161. const uintptr_t parentWindowHandle,
  162. const uint width, const uint height,
  163. const double scale, const bool resizable, const bool usesSizeRequest_)
  164. : app(a),
  165. appData(a.pData),
  166. self(s),
  167. view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
  168. topLevelWidgets(),
  169. isClosed(parentWindowHandle == 0),
  170. isVisible(parentWindowHandle != 0 && view != nullptr),
  171. isEmbed(parentWindowHandle != 0),
  172. usesSizeRequest(usesSizeRequest_),
  173. scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)),
  174. autoScaling(false),
  175. autoScaleFactor(1.0),
  176. minWidth(0),
  177. minHeight(0),
  178. keepAspectRatio(false),
  179. ignoreIdleCallbacks(false),
  180. waitingForClipboardData(false),
  181. waitingForClipboardEvents(false),
  182. clipboardTypeId(0),
  183. filenameToRenderInto(nullptr),
  184. #ifndef DGL_FILE_BROWSER_DISABLED
  185. fileBrowserHandle(nullptr),
  186. #endif
  187. modal()
  188. {
  189. if (isEmbed)
  190. puglSetParentWindow(view, parentWindowHandle);
  191. initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable);
  192. }
  193. Window::PrivateData::~PrivateData()
  194. {
  195. appData->idleCallbacks.remove(this);
  196. appData->windows.remove(self);
  197. std::free(filenameToRenderInto);
  198. if (view == nullptr)
  199. return;
  200. if (isEmbed)
  201. {
  202. #ifndef DGL_FILE_BROWSER_DISABLED
  203. if (fileBrowserHandle != nullptr)
  204. fileBrowserClose(fileBrowserHandle);
  205. #endif
  206. puglHide(view);
  207. appData->oneWindowClosed();
  208. isClosed = true;
  209. isVisible = false;
  210. }
  211. puglFreeView(view);
  212. }
  213. // -----------------------------------------------------------------------
  214. void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable)
  215. {
  216. appData->windows.push_back(self);
  217. appData->idleCallbacks.push_back(this);
  218. memset(graphicsContext, 0, sizeof(graphicsContext));
  219. if (view == nullptr)
  220. {
  221. d_stderr2("Failed to create Pugl view, everything will fail!");
  222. return;
  223. }
  224. puglSetMatchingBackendForCurrentBuild(view);
  225. puglSetHandle(view, this);
  226. puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
  227. puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
  228. #if DGL_USE_RGBA
  229. puglSetViewHint(view, PUGL_DEPTH_BITS, 24);
  230. #else
  231. puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
  232. #endif
  233. puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
  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_DBG("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. waitingForClipboardData = true,
  622. waitingForClipboardEvents = true;
  623. // begin clipboard dance here
  624. if (puglPaste(view) != PUGL_SUCCESS)
  625. {
  626. dataSize = 0;
  627. waitingForClipboardEvents = false;
  628. return nullptr;
  629. }
  630. #ifdef DGL_USING_X11
  631. // wait for type request, clipboardTypeId must be != 0 to be valid
  632. int retry = static_cast<int>(2 / 0.03);
  633. while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0)
  634. {
  635. if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
  636. break;
  637. }
  638. #endif
  639. if (clipboardTypeId == 0)
  640. {
  641. dataSize = 0;
  642. waitingForClipboardEvents = false;
  643. return nullptr;
  644. }
  645. #ifdef DGL_USING_X11
  646. // wait for actual data (assumes offer was accepted)
  647. retry = static_cast<int>(2 / 0.03);
  648. while (waitingForClipboardData && --retry >= 0)
  649. {
  650. if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
  651. break;
  652. }
  653. #endif
  654. if (clipboardTypeId == 0)
  655. {
  656. dataSize = 0;
  657. waitingForClipboardEvents = false;
  658. return nullptr;
  659. }
  660. waitingForClipboardEvents = false;
  661. return puglGetClipboard(view, clipboardTypeId - 1, &dataSize);
  662. }
  663. uint32_t Window::PrivateData::onClipboardDataOffer()
  664. {
  665. DGL_DBG("onClipboardDataOffer\n");
  666. if ((clipboardTypeId = self->onClipboardDataOffer()) != 0)
  667. return clipboardTypeId;
  668. // stop waiting for data, it was rejected
  669. waitingForClipboardData = false;
  670. return 0;
  671. }
  672. void Window::PrivateData::onClipboardData(const uint32_t typeId)
  673. {
  674. if (clipboardTypeId != typeId)
  675. clipboardTypeId = 0;
  676. waitingForClipboardData = false;
  677. }
  678. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  679. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
  680. #endif
  681. PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
  682. {
  683. Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
  684. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  685. if (event->type != PUGL_TIMER) {
  686. printEvent(event, "pugl event: ", true);
  687. }
  688. #endif
  689. if (pData->waitingForClipboardEvents)
  690. {
  691. switch (event->type)
  692. {
  693. case PUGL_UPDATE:
  694. case PUGL_EXPOSE:
  695. case PUGL_FOCUS_IN:
  696. case PUGL_FOCUS_OUT:
  697. case PUGL_KEY_PRESS:
  698. case PUGL_KEY_RELEASE:
  699. case PUGL_TEXT:
  700. case PUGL_POINTER_IN:
  701. case PUGL_POINTER_OUT:
  702. case PUGL_BUTTON_PRESS:
  703. case PUGL_BUTTON_RELEASE:
  704. case PUGL_MOTION:
  705. case PUGL_SCROLL:
  706. case PUGL_TIMER:
  707. case PUGL_LOOP_ENTER:
  708. case PUGL_LOOP_LEAVE:
  709. return PUGL_SUCCESS;
  710. case PUGL_DATA_OFFER:
  711. case PUGL_DATA:
  712. break;
  713. default:
  714. d_stdout("Got event %d while waitingForClipboardEvents", event->type);
  715. break;
  716. }
  717. }
  718. switch (event->type)
  719. {
  720. ///< No event
  721. case PUGL_NOTHING:
  722. break;
  723. ///< View created, a #PuglEventCreate
  724. case PUGL_CREATE:
  725. #ifdef DGL_USING_X11
  726. if (! pData->isEmbed)
  727. puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
  728. #endif
  729. break;
  730. ///< View destroyed, a #PuglEventDestroy
  731. case PUGL_DESTROY:
  732. break;
  733. ///< View moved/resized, a #PuglEventConfigure
  734. case PUGL_CONFIGURE:
  735. // unused x, y (double)
  736. pData->onPuglConfigure(event->configure.width, event->configure.height);
  737. break;
  738. ///< View made visible, a #PuglEventMap
  739. case PUGL_MAP:
  740. break;
  741. ///< View made invisible, a #PuglEventUnmap
  742. case PUGL_UNMAP:
  743. break;
  744. ///< View ready to draw, a #PuglEventUpdate
  745. case PUGL_UPDATE:
  746. break;
  747. ///< View must be drawn, a #PuglEventExpose
  748. case PUGL_EXPOSE:
  749. // unused x, y, width, height (double)
  750. pData->onPuglExpose();
  751. break;
  752. ///< View will be closed, a #PuglEventClose
  753. case PUGL_CLOSE:
  754. pData->onPuglClose();
  755. break;
  756. ///< Keyboard focus entered view, a #PuglEventFocus
  757. case PUGL_FOCUS_IN:
  758. ///< Keyboard focus left view, a #PuglEventFocus
  759. case PUGL_FOCUS_OUT:
  760. pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
  761. static_cast<CrossingMode>(event->focus.mode));
  762. break;
  763. ///< Key pressed, a #PuglEventKey
  764. case PUGL_KEY_PRESS:
  765. ///< Key released, a #PuglEventKey
  766. case PUGL_KEY_RELEASE:
  767. {
  768. // unused x, y, xRoot, yRoot (double)
  769. Widget::KeyboardEvent ev;
  770. ev.mod = event->key.state;
  771. ev.flags = event->key.flags;
  772. ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5);
  773. ev.press = event->type == PUGL_KEY_PRESS;
  774. ev.key = event->key.key;
  775. ev.keycode = event->key.keycode;
  776. // keyboard events must always be lowercase
  777. if (ev.key >= 'A' && ev.key <= 'Z')
  778. {
  779. ev.key += 'a' - 'A'; // A-Z -> a-z
  780. ev.mod |= kModifierShift;
  781. }
  782. pData->onPuglKey(ev);
  783. break;
  784. }
  785. ///< Character entered, a #PuglEventText
  786. case PUGL_TEXT:
  787. {
  788. // unused x, y, xRoot, yRoot (double)
  789. Widget::CharacterInputEvent ev;
  790. ev.mod = event->text.state;
  791. ev.flags = event->text.flags;
  792. ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5);
  793. ev.keycode = event->text.keycode;
  794. ev.character = event->text.character;
  795. std::strncpy(ev.string, event->text.string, sizeof(ev.string));
  796. pData->onPuglText(ev);
  797. break;
  798. }
  799. ///< Pointer entered view, a #PuglEventCrossing
  800. case PUGL_POINTER_IN:
  801. break;
  802. ///< Pointer left view, a #PuglEventCrossing
  803. case PUGL_POINTER_OUT:
  804. break;
  805. ///< Mouse button pressed, a #PuglEventButton
  806. case PUGL_BUTTON_PRESS:
  807. ///< Mouse button released, a #PuglEventButton
  808. case PUGL_BUTTON_RELEASE:
  809. {
  810. Widget::MouseEvent ev;
  811. ev.mod = event->button.state;
  812. ev.flags = event->button.flags;
  813. ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5);
  814. ev.button = event->button.button + 1;
  815. ev.press = event->type == PUGL_BUTTON_PRESS;
  816. ev.pos = Point<double>(event->button.x, event->button.y);
  817. ev.absolutePos = ev.pos;
  818. pData->onPuglMouse(ev);
  819. break;
  820. }
  821. ///< Pointer moved, a #PuglEventMotion
  822. case PUGL_MOTION:
  823. {
  824. Widget::MotionEvent ev;
  825. ev.mod = event->motion.state;
  826. ev.flags = event->motion.flags;
  827. ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
  828. ev.pos = Point<double>(event->motion.x, event->motion.y);
  829. ev.absolutePos = ev.pos;
  830. pData->onPuglMotion(ev);
  831. break;
  832. }
  833. ///< Scrolled, a #PuglEventScroll
  834. case PUGL_SCROLL:
  835. {
  836. Widget::ScrollEvent ev;
  837. ev.mod = event->scroll.state;
  838. ev.flags = event->scroll.flags;
  839. ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
  840. ev.pos = Point<double>(event->scroll.x, event->scroll.y);
  841. ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
  842. ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
  843. ev.absolutePos = ev.pos;
  844. pData->onPuglScroll(ev);
  845. break;
  846. }
  847. ///< Custom client message, a #PuglEventClient
  848. case PUGL_CLIENT:
  849. break;
  850. ///< Timer triggered, a #PuglEventTimer
  851. case PUGL_TIMER:
  852. if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
  853. idleCallback->idleCallback();
  854. break;
  855. ///< Recursive loop entered, a #PuglEventLoopEnter
  856. case PUGL_LOOP_ENTER:
  857. break;
  858. ///< Recursive loop left, a #PuglEventLoopLeave
  859. case PUGL_LOOP_LEAVE:
  860. break;
  861. ///< Data offered from clipboard, a #PuglDataOfferEvent
  862. case PUGL_DATA_OFFER:
  863. if (const uint32_t offerTypeId = pData->onClipboardDataOffer())
  864. puglAcceptOffer(view, &event->offer, offerTypeId - 1);
  865. break;
  866. ///< Data available from clipboard, a #PuglDataEvent
  867. case PUGL_DATA:
  868. pData->onClipboardData(event->data.typeIndex + 1);
  869. break;
  870. }
  871. return PUGL_SUCCESS;
  872. }
  873. // -----------------------------------------------------------------------
  874. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  875. static int printModifiers(const uint32_t mods)
  876. {
  877. return fprintf(stderr, "Modifiers:%s%s%s%s\n",
  878. (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
  879. (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
  880. (mods & PUGL_MOD_ALT) ? " Alt" : "",
  881. (mods & PUGL_MOD_SUPER) ? " Super" : "");
  882. }
  883. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
  884. {
  885. #define FFMT "%6.1f"
  886. #define PFMT FFMT " " FFMT
  887. #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
  888. switch (event->type) {
  889. case PUGL_NOTHING:
  890. return 0;
  891. case PUGL_KEY_PRESS:
  892. return PRINT("%sKey press code %3u key U+%04X\n",
  893. prefix,
  894. event->key.keycode,
  895. event->key.key);
  896. case PUGL_KEY_RELEASE:
  897. return PRINT("%sKey release code %3u key U+%04X\n",
  898. prefix,
  899. event->key.keycode,
  900. event->key.key);
  901. case PUGL_TEXT:
  902. return PRINT("%sText entry code %3u char U+%04X (%s)\n",
  903. prefix,
  904. event->text.keycode,
  905. event->text.character,
  906. event->text.string);
  907. case PUGL_BUTTON_PRESS:
  908. case PUGL_BUTTON_RELEASE:
  909. return (PRINT("%sMouse %u %s at " PFMT " ",
  910. prefix,
  911. event->button.button,
  912. (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
  913. event->button.x,
  914. event->button.y) +
  915. printModifiers(event->scroll.state));
  916. case PUGL_SCROLL:
  917. return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
  918. prefix,
  919. event->scroll.dx,
  920. event->scroll.dy,
  921. event->scroll.x,
  922. event->scroll.y) +
  923. printModifiers(event->scroll.state));
  924. case PUGL_POINTER_IN:
  925. return PRINT("%sMouse enter at " PFMT "\n",
  926. prefix,
  927. event->crossing.x,
  928. event->crossing.y);
  929. case PUGL_POINTER_OUT:
  930. return PRINT("%sMouse leave at " PFMT "\n",
  931. prefix,
  932. event->crossing.x,
  933. event->crossing.y);
  934. case PUGL_FOCUS_IN:
  935. return PRINT("%sFocus in %i\n",
  936. prefix,
  937. event->focus.mode);
  938. case PUGL_FOCUS_OUT:
  939. return PRINT("%sFocus out %i\n",
  940. prefix,
  941. event->focus.mode);
  942. case PUGL_CLIENT:
  943. return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
  944. prefix,
  945. event->client.data1,
  946. event->client.data2);
  947. case PUGL_TIMER:
  948. return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
  949. default:
  950. break;
  951. }
  952. if (verbose) {
  953. switch (event->type) {
  954. case PUGL_CREATE:
  955. return fprintf(stderr, "%sCreate\n", prefix);
  956. case PUGL_DESTROY:
  957. return fprintf(stderr, "%sDestroy\n", prefix);
  958. case PUGL_MAP:
  959. return fprintf(stderr, "%sMap\n", prefix);
  960. case PUGL_UNMAP:
  961. return fprintf(stderr, "%sUnmap\n", prefix);
  962. case PUGL_UPDATE:
  963. return 0; // fprintf(stderr, "%sUpdate\n", prefix);
  964. case PUGL_CONFIGURE:
  965. return PRINT("%sConfigure " PFMT " " PFMT "\n",
  966. prefix,
  967. event->configure.x,
  968. event->configure.y,
  969. event->configure.width,
  970. event->configure.height);
  971. case PUGL_EXPOSE:
  972. return PRINT("%sExpose " PFMT " " PFMT "\n",
  973. prefix,
  974. event->expose.x,
  975. event->expose.y,
  976. event->expose.width,
  977. event->expose.height);
  978. case PUGL_CLOSE:
  979. return PRINT("%sClose\n", prefix);
  980. case PUGL_MOTION:
  981. return PRINT("%sMouse motion at " PFMT "\n",
  982. prefix,
  983. event->motion.x,
  984. event->motion.y);
  985. default:
  986. return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
  987. }
  988. }
  989. #undef PRINT
  990. #undef PFMT
  991. #undef FFMT
  992. return 0;
  993. }
  994. #endif
  995. #undef DGL_DBG
  996. #undef DGL_DBGF
  997. // -----------------------------------------------------------------------
  998. END_NAMESPACE_DGL