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.

1080 lines
29KB

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