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.

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