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.

1026 lines
27KB

  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. #include "../../distrho/extra/String.hpp"
  20. #ifdef DISTRHO_OS_WINDOWS
  21. # include <direct.h>
  22. #else
  23. # include <unistd.h>
  24. #endif
  25. #define DGL_DEBUG_EVENTS
  26. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  27. # include <cinttypes>
  28. #endif
  29. START_NAMESPACE_DGL
  30. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  31. # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg);
  32. # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  33. # define DGL_DBGF std::fflush(stderr);
  34. #else
  35. # define DGL_DBG(msg)
  36. # define DGL_DBGp(...)
  37. # define DGL_DBGF
  38. #endif
  39. #define DEFAULT_WIDTH 640
  40. #define DEFAULT_HEIGHT 480
  41. // -----------------------------------------------------------------------
  42. static double getDesktopScaleFactor()
  43. {
  44. if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
  45. return std::max(1.0, std::atof(scale));
  46. return 1.0;
  47. }
  48. // -----------------------------------------------------------------------
  49. Window::PrivateData::PrivateData(Application& a, Window* const s)
  50. : app(a),
  51. appData(a.pData),
  52. self(s),
  53. view(puglNewView(appData->world)),
  54. topLevelWidget(nullptr),
  55. isClosed(true),
  56. isVisible(false),
  57. isEmbed(false),
  58. scaleFactor(getDesktopScaleFactor()),
  59. autoScaling(false),
  60. autoScaleFactor(1.0),
  61. minWidth(0),
  62. minHeight(0),
  63. #ifdef DISTRHO_OS_WINDOWS
  64. win32SelectedFile(nullptr),
  65. #endif
  66. modal()
  67. {
  68. init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  69. }
  70. Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
  71. : app(a),
  72. appData(a.pData),
  73. self(s),
  74. view(puglNewView(appData->world)),
  75. topLevelWidget(nullptr),
  76. isClosed(true),
  77. isVisible(false),
  78. isEmbed(false),
  79. scaleFactor(ppData->scaleFactor),
  80. autoScaling(false),
  81. autoScaleFactor(1.0),
  82. minWidth(0),
  83. minHeight(0),
  84. #ifdef DISTRHO_OS_WINDOWS
  85. win32SelectedFile(nullptr),
  86. #endif
  87. modal(ppData)
  88. {
  89. init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
  90. puglSetTransientFor(view, puglGetNativeWindow(ppData->view));
  91. }
  92. Window::PrivateData::PrivateData(Application& a, Window* const s,
  93. const uintptr_t parentWindowHandle,
  94. const double scale, const bool resizable)
  95. : app(a),
  96. appData(a.pData),
  97. self(s),
  98. view(puglNewView(appData->world)),
  99. topLevelWidget(nullptr),
  100. isClosed(parentWindowHandle == 0),
  101. isVisible(parentWindowHandle != 0),
  102. isEmbed(parentWindowHandle != 0),
  103. scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()),
  104. autoScaling(false),
  105. autoScaleFactor(1.0),
  106. minWidth(0),
  107. minHeight(0),
  108. #ifdef DISTRHO_OS_WINDOWS
  109. win32SelectedFile(nullptr),
  110. #endif
  111. modal()
  112. {
  113. if (isEmbed)
  114. {
  115. // puglSetDefaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, height);
  116. puglSetParentWindow(view, parentWindowHandle);
  117. }
  118. init(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
  119. if (isEmbed)
  120. {
  121. appData->oneWindowShown();
  122. puglShow(view);
  123. }
  124. }
  125. Window::PrivateData::PrivateData(Application& a, Window* const s,
  126. const uintptr_t parentWindowHandle,
  127. const uint width, const uint height,
  128. const double scale, const bool resizable)
  129. : app(a),
  130. appData(a.pData),
  131. self(s),
  132. view(puglNewView(appData->world)),
  133. topLevelWidget(nullptr),
  134. isClosed(parentWindowHandle == 0),
  135. isVisible(parentWindowHandle != 0),
  136. isEmbed(parentWindowHandle != 0),
  137. scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()),
  138. autoScaling(false),
  139. autoScaleFactor(1.0),
  140. minWidth(0),
  141. minHeight(0),
  142. #ifdef DISTRHO_OS_WINDOWS
  143. win32SelectedFile(nullptr),
  144. #endif
  145. modal()
  146. {
  147. if (isEmbed)
  148. {
  149. puglSetDefaultSize(view, static_cast<int>(width), static_cast<int>(height));
  150. puglSetParentWindow(view, parentWindowHandle);
  151. }
  152. init(width, height, resizable);
  153. if (isEmbed)
  154. {
  155. appData->oneWindowShown();
  156. puglShow(view);
  157. }
  158. }
  159. Window::PrivateData::~PrivateData()
  160. {
  161. if (isEmbed)
  162. {
  163. #ifdef HAVE_X11
  164. sofdFileDialogClose(view);
  165. #endif
  166. puglHide(view);
  167. appData->oneWindowClosed();
  168. isClosed = true;
  169. isVisible = false;
  170. }
  171. appData->idleCallbacks.remove(this);
  172. appData->windows.remove(self);
  173. if (view != nullptr)
  174. puglFreeView(view);
  175. }
  176. // -----------------------------------------------------------------------
  177. void Window::PrivateData::init(const uint width, const uint height, const bool resizable)
  178. {
  179. appData->windows.push_back(self);
  180. appData->idleCallbacks.push_back(this);
  181. memset(graphicsContext, 0, sizeof(graphicsContext));
  182. if (view == nullptr)
  183. {
  184. DGL_DBG("Failed to create Pugl view, everything will fail!\n");
  185. return;
  186. }
  187. puglSetMatchingBackendForCurrentBuild(view);
  188. puglSetHandle(view, this);
  189. puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
  190. puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
  191. puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
  192. puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
  193. // PUGL_SAMPLES ??
  194. puglSetEventFunc(view, puglEventCallback);
  195. PuglRect rect = puglGetFrame(view);
  196. rect.width = width;
  197. rect.height = height;
  198. puglSetFrame(view, rect);
  199. // FIXME this is bad
  200. puglRealize(view);
  201. puglBackendEnter(view);
  202. }
  203. // -----------------------------------------------------------------------
  204. void Window::PrivateData::close()
  205. {
  206. DGL_DBG("Window close\n");
  207. // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
  208. if (isEmbed || isClosed)
  209. return;
  210. isClosed = true;
  211. hide();
  212. appData->oneWindowClosed();
  213. }
  214. // -----------------------------------------------------------------------
  215. void Window::PrivateData::show()
  216. {
  217. if (isVisible)
  218. {
  219. DGL_DBG("Window show matches current visible state, ignoring request\n");
  220. return;
  221. }
  222. if (isEmbed)
  223. {
  224. DGL_DBG("Window show cannot be called when embedded\n");
  225. return;
  226. }
  227. DGL_DBG("Window show called\n");
  228. #if 0 && defined(DISTRHO_OS_MAC)
  229. // if (mWindow != nullptr)
  230. // {
  231. // if (mParentWindow != nullptr)
  232. // [mParentWindow addChildWindow:mWindow
  233. // ordered:NSWindowAbove];
  234. // }
  235. #endif
  236. if (isClosed)
  237. {
  238. isClosed = false;
  239. appData->oneWindowShown();
  240. // FIXME
  241. PuglRect rect = puglGetFrame(view);
  242. puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));
  243. puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));
  244. #ifdef DISTRHO_OS_WINDOWS
  245. puglWin32ShowWindowCentered(view);
  246. #else
  247. puglShow(view);
  248. #endif
  249. }
  250. else
  251. {
  252. #ifdef DISTRHO_OS_WINDOWS
  253. puglWin32RestoreWindow(view);
  254. #else
  255. puglShow(view);
  256. #endif
  257. }
  258. isVisible = true;
  259. }
  260. void Window::PrivateData::hide()
  261. {
  262. if (isEmbed)
  263. {
  264. DGL_DBG("Window hide cannot be called when embedded\n");
  265. return;
  266. }
  267. if (! isVisible)
  268. {
  269. DGL_DBG("Window hide matches current visible state, ignoring request\n");
  270. return;
  271. }
  272. DGL_DBG("Window hide called\n");
  273. #if 0 && defined(DISTRHO_OS_MAC)
  274. // if (mWindow != nullptr)
  275. // {
  276. // if (mParentWindow != nullptr)
  277. // [mParentWindow removeChildWindow:mWindow];
  278. // }
  279. #endif
  280. if (modal.enabled)
  281. stopModal();
  282. #ifdef HAVE_X11
  283. sofdFileDialogClose(view);
  284. #endif
  285. puglHide(view);
  286. isVisible = false;
  287. }
  288. // -----------------------------------------------------------------------
  289. void Window::PrivateData::focus()
  290. {
  291. if (! isEmbed)
  292. puglRaiseWindow(view);
  293. puglGrabFocus(view);
  294. }
  295. // -----------------------------------------------------------------------
  296. void Window::PrivateData::setResizable(const bool resizable)
  297. {
  298. DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,);
  299. DGL_DBG("Window setResizable called\n");
  300. puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
  301. #ifdef DISTRHO_OS_WINDOWS
  302. puglWin32SetWindowResizable(view, resizable);
  303. #endif
  304. }
  305. // -----------------------------------------------------------------------
  306. void Window::PrivateData::idleCallback()
  307. {
  308. #ifndef DGL_FILE_BROWSER_DISABLED
  309. # ifdef DISTRHO_OS_WINDOWS
  310. if (char* const path = win32SelectedFile)
  311. {
  312. win32SelectedFile = nullptr;
  313. self->onFileSelected(path);
  314. std::free(path);
  315. }
  316. # endif
  317. # ifdef HAVE_X11
  318. char* path;
  319. if (sofdFileDialogGetPath(&path))
  320. {
  321. // TODO ignore null path??
  322. self->onFileSelected(path);
  323. sofdFileDialogFree(path);
  324. }
  325. # endif
  326. #endif
  327. }
  328. // -----------------------------------------------------------------------
  329. // idle callback stuff
  330. bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
  331. {
  332. if (timerFrequencyInMs == 0)
  333. {
  334. appData->idleCallbacks.push_back(callback);
  335. return true;
  336. }
  337. return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS;
  338. }
  339. bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
  340. {
  341. if (std::find(appData->idleCallbacks.begin(),
  342. appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
  343. {
  344. appData->idleCallbacks.remove(callback);
  345. return true;
  346. }
  347. return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
  348. }
  349. // -----------------------------------------------------------------------
  350. // file handling
  351. bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& options)
  352. {
  353. using DISTRHO_NAMESPACE::String;
  354. // --------------------------------------------------------------------------
  355. // configure start dir
  356. // TODO: get abspath if needed
  357. // TODO: cross-platform
  358. String startDir(options.startDir);
  359. if (startDir.isEmpty())
  360. {
  361. // TESTING verify this whole thing...
  362. #ifdef DISTRHO_OS_WINDOWS
  363. if (char* const cwd = _getcwd(nullptr, 0))
  364. {
  365. startDir = cwd;
  366. std::free(cwd);
  367. }
  368. #else
  369. if (char* const cwd = getcwd(nullptr, 0))
  370. {
  371. startDir = cwd;
  372. std::free(cwd);
  373. }
  374. #endif
  375. }
  376. DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
  377. if (! startDir.endsWith(DISTRHO_OS_SEP))
  378. startDir += DISTRHO_OS_SEP_STR;
  379. // --------------------------------------------------------------------------
  380. // configure title
  381. String title(options.title);
  382. if (title.isEmpty())
  383. {
  384. title = puglGetWindowTitle(view);
  385. if (title.isEmpty())
  386. title = "FileBrowser";
  387. }
  388. // --------------------------------------------------------------------------
  389. // show
  390. #ifdef HAVE_X11
  391. uint flags = 0x0;
  392. // TODO flags
  393. return sofdFileDialogShow(view, startDir, title, flags, options.width, options.height);
  394. #endif
  395. return false;
  396. }
  397. // -----------------------------------------------------------------------
  398. // modal handling
  399. void Window::PrivateData::startModal()
  400. {
  401. DGL_DBG("Window modal loop starting..."); DGL_DBGF;
  402. DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());
  403. // activate modal mode for this window
  404. modal.enabled = true;
  405. // make parent give focus to us
  406. modal.parent->modal.child = this;
  407. // FIXME?
  408. PuglRect rect = puglGetFrame(view);
  409. puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));
  410. // make sure both parent and ourselves are visible
  411. modal.parent->show();
  412. show();
  413. DGL_DBG("Ok\n");
  414. }
  415. void Window::PrivateData::stopModal()
  416. {
  417. DGL_DBG("Window modal loop stopping..."); DGL_DBGF;
  418. // deactivate modal mode
  419. modal.enabled = false;
  420. // safety checks, make sure we have a parent and we are currently active as the child to give focus to
  421. if (modal.parent == nullptr)
  422. return;
  423. if (modal.parent->modal.child != this)
  424. return;
  425. // stop parent from giving focus to us, so it behaves like normal
  426. modal.parent->modal.child = nullptr;
  427. // the mouse position probably changed since the modal appeared,
  428. // so send a mouse motion event to the modal's parent window
  429. #if 0
  430. #if defined(DISTRHO_OS_HAIKU)
  431. // TODO
  432. #elif defined(DISTRHO_OS_MAC)
  433. // TODO
  434. #elif defined(DISTRHO_OS_WINDOWS)
  435. // TODO
  436. #else
  437. int i, wx, wy;
  438. uint u;
  439. ::Window w;
  440. if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
  441. fModal.parent->onPuglMotion(wx, wy);
  442. #endif
  443. #endif
  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 : %i %i\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. if (topLevelWidget != nullptr)
  479. topLevelWidget->setSize(uwidth, uheight);
  480. #endif
  481. // always repaint after a resize
  482. puglPostRedisplay(view);
  483. }
  484. void Window::PrivateData::onPuglExpose()
  485. {
  486. DGL_DBGp("PUGL: onPuglExpose : %p\n", topLevelWidget);
  487. puglOnDisplayPrepare(view);
  488. #ifndef DPF_TEST_WINDOW_CPP
  489. if (topLevelWidget != nullptr)
  490. topLevelWidget->pData->display();
  491. #endif
  492. }
  493. void Window::PrivateData::onPuglClose()
  494. {
  495. DGL_DBG("PUGL: onClose\n");
  496. // if we have a parent or running as standalone we can prevent closing in certain conditions
  497. if (modal.parent != nullptr || appData->isStandalone)
  498. {
  499. // parent gives focus to us as modal, prevent closing
  500. if (modal.child != nullptr)
  501. return modal.child->focus();
  502. // ask window if we should close
  503. if (! self->onClose())
  504. return;
  505. }
  506. if (modal.enabled)
  507. stopModal();
  508. if (PrivateData* const child = modal.child)
  509. {
  510. modal.child = nullptr;
  511. child->close();
  512. }
  513. close();
  514. }
  515. void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
  516. {
  517. DGL_DBGp("onPuglFocus : %i %i\n", focus, mode);
  518. if (isClosed)
  519. return;
  520. if (modal.child != nullptr)
  521. return modal.child->focus();
  522. self->onFocus(focus, mode);
  523. }
  524. void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
  525. {
  526. DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode);
  527. if (modal.child != nullptr)
  528. return modal.child->focus();
  529. #ifndef DPF_TEST_WINDOW_CPP
  530. if (topLevelWidget != nullptr)
  531. topLevelWidget->pData->keyboardEvent(ev);
  532. #endif
  533. }
  534. void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev)
  535. {
  536. DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key);
  537. if (modal.child != nullptr)
  538. return modal.child->focus();
  539. #ifndef DPF_TEST_WINDOW_CPP
  540. if (topLevelWidget != nullptr)
  541. topLevelWidget->pData->specialEvent(ev);
  542. #endif
  543. }
  544. void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
  545. {
  546. DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string);
  547. if (modal.child != nullptr)
  548. return modal.child->focus();
  549. #ifndef DPF_TEST_WINDOW_CPP
  550. if (topLevelWidget != nullptr)
  551. topLevelWidget->pData->characterInputEvent(ev);
  552. #endif
  553. }
  554. void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
  555. {
  556. DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY());
  557. if (modal.child != nullptr)
  558. return modal.child->focus();
  559. #ifndef DPF_TEST_WINDOW_CPP
  560. if (topLevelWidget != nullptr)
  561. topLevelWidget->pData->mouseEvent(ev);
  562. #endif
  563. }
  564. void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
  565. {
  566. DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY());
  567. if (modal.child != nullptr)
  568. return modal.child->focus();
  569. #ifndef DPF_TEST_WINDOW_CPP
  570. if (topLevelWidget != nullptr)
  571. topLevelWidget->pData->motionEvent(ev);
  572. #endif
  573. }
  574. void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
  575. {
  576. DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY());
  577. if (modal.child != nullptr)
  578. return modal.child->focus();
  579. #ifndef DPF_TEST_WINDOW_CPP
  580. if (topLevelWidget != nullptr)
  581. topLevelWidget->pData->scrollEvent(ev);
  582. #endif
  583. }
  584. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  585. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
  586. #endif
  587. PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
  588. {
  589. Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
  590. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  591. printEvent(event, "pugl event: ", true);
  592. #endif
  593. switch (event->type)
  594. {
  595. ///< No event
  596. case PUGL_NOTHING:
  597. break;
  598. ///< View created, a #PuglEventCreate
  599. case PUGL_CREATE:
  600. #ifdef DGL_PUGL_USING_X11
  601. if (! pData->isEmbed)
  602. puglExtraSetWindowTypeAndPID(view);
  603. #endif
  604. break;
  605. ///< View destroyed, a #PuglEventDestroy
  606. case PUGL_DESTROY:
  607. break;
  608. ///< View moved/resized, a #PuglEventConfigure
  609. case PUGL_CONFIGURE:
  610. // unused x, y (double)
  611. pData->onPuglConfigure(event->configure.width, event->configure.height);
  612. break;
  613. ///< View made visible, a #PuglEventMap
  614. case PUGL_MAP:
  615. break;
  616. ///< View made invisible, a #PuglEventUnmap
  617. case PUGL_UNMAP:
  618. break;
  619. ///< View ready to draw, a #PuglEventUpdate
  620. case PUGL_UPDATE:
  621. break;
  622. ///< View must be drawn, a #PuglEventExpose
  623. case PUGL_EXPOSE:
  624. // unused x, y, width, height (double)
  625. pData->onPuglExpose();
  626. break;
  627. ///< View will be closed, a #PuglEventClose
  628. case PUGL_CLOSE:
  629. pData->onPuglClose();
  630. break;
  631. ///< Keyboard focus entered view, a #PuglEventFocus
  632. case PUGL_FOCUS_IN:
  633. ///< Keyboard focus left view, a #PuglEventFocus
  634. case PUGL_FOCUS_OUT:
  635. pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
  636. static_cast<CrossingMode>(event->focus.mode));
  637. break;
  638. ///< Key pressed, a #PuglEventKey
  639. case PUGL_KEY_PRESS:
  640. ///< Key released, a #PuglEventKey
  641. case PUGL_KEY_RELEASE:
  642. {
  643. // unused x, y, xRoot, yRoot (double)
  644. // TODO special keys?
  645. Widget::KeyboardEvent ev;
  646. ev.mod = event->key.state;
  647. ev.flags = event->key.flags;
  648. ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5);
  649. ev.press = event->type == PUGL_KEY_PRESS;
  650. ev.key = event->key.key;
  651. ev.keycode = event->key.keycode;
  652. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  653. ev.key -= 'a' - 'A'; // a-z -> A-Z
  654. pData->onPuglKey(ev);
  655. break;
  656. }
  657. ///< Character entered, a #PuglEventText
  658. case PUGL_TEXT:
  659. {
  660. // unused x, y, xRoot, yRoot (double)
  661. Widget::CharacterInputEvent ev;
  662. ev.mod = event->text.state;
  663. ev.flags = event->text.flags;
  664. ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5);
  665. ev.keycode = event->text.keycode;
  666. ev.character = event->text.character;
  667. std::strncpy(ev.string, event->text.string, sizeof(ev.string));
  668. pData->onPuglText(ev);
  669. break;
  670. }
  671. ///< Pointer entered view, a #PuglEventCrossing
  672. case PUGL_POINTER_IN:
  673. break;
  674. ///< Pointer left view, a #PuglEventCrossing
  675. case PUGL_POINTER_OUT:
  676. break;
  677. ///< Mouse button pressed, a #PuglEventButton
  678. case PUGL_BUTTON_PRESS:
  679. ///< Mouse button released, a #PuglEventButton
  680. case PUGL_BUTTON_RELEASE:
  681. {
  682. Widget::MouseEvent ev;
  683. ev.mod = event->button.state;
  684. ev.flags = event->button.flags;
  685. ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5);
  686. ev.button = event->button.button;
  687. ev.press = event->type == PUGL_BUTTON_PRESS;
  688. ev.pos = Point<double>(event->button.x, event->button.y);
  689. pData->onPuglMouse(ev);
  690. break;
  691. }
  692. ///< Pointer moved, a #PuglEventMotion
  693. case PUGL_MOTION:
  694. {
  695. Widget::MotionEvent ev;
  696. ev.mod = event->motion.state;
  697. ev.flags = event->motion.flags;
  698. ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
  699. ev.pos = Point<double>(event->motion.x, event->motion.y);
  700. pData->onPuglMotion(ev);
  701. break;
  702. }
  703. ///< Scrolled, a #PuglEventScroll
  704. case PUGL_SCROLL:
  705. {
  706. Widget::ScrollEvent ev;
  707. ev.mod = event->scroll.state;
  708. ev.flags = event->scroll.flags;
  709. ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
  710. ev.pos = Point<double>(event->scroll.x, event->scroll.y);
  711. ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
  712. ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
  713. pData->onPuglScroll(ev);
  714. break;
  715. }
  716. ///< Custom client message, a #PuglEventClient
  717. case PUGL_CLIENT:
  718. break;
  719. ///< Timer triggered, a #PuglEventTimer
  720. case PUGL_TIMER:
  721. if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
  722. idleCallback->idleCallback();
  723. break;
  724. ///< Recursive loop entered, a #PuglEventLoopEnter
  725. case PUGL_LOOP_ENTER:
  726. break;
  727. ///< Recursive loop left, a #PuglEventLoopLeave
  728. case PUGL_LOOP_LEAVE:
  729. break;
  730. }
  731. return PUGL_SUCCESS;
  732. }
  733. // -----------------------------------------------------------------------
  734. #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
  735. static int printModifiers(const uint32_t mods)
  736. {
  737. return fprintf(stderr, "Modifiers:%s%s%s%s\n",
  738. (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
  739. (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
  740. (mods & PUGL_MOD_ALT) ? " Alt" : "",
  741. (mods & PUGL_MOD_SUPER) ? " Super" : "");
  742. }
  743. static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
  744. {
  745. #define FFMT "%6.1f"
  746. #define PFMT FFMT " " FFMT
  747. #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
  748. switch (event->type) {
  749. case PUGL_NOTHING:
  750. return 0;
  751. case PUGL_KEY_PRESS:
  752. return PRINT("%sKey press code %3u key U+%04X\n",
  753. prefix,
  754. event->key.keycode,
  755. event->key.key);
  756. case PUGL_KEY_RELEASE:
  757. return PRINT("%sKey release code %3u key U+%04X\n",
  758. prefix,
  759. event->key.keycode,
  760. event->key.key);
  761. case PUGL_TEXT:
  762. return PRINT("%sText entry code %3u char U+%04X (%s)\n",
  763. prefix,
  764. event->text.keycode,
  765. event->text.character,
  766. event->text.string);
  767. case PUGL_BUTTON_PRESS:
  768. case PUGL_BUTTON_RELEASE:
  769. return (PRINT("%sMouse %u %s at " PFMT " ",
  770. prefix,
  771. event->button.button,
  772. (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
  773. event->button.x,
  774. event->button.y) +
  775. printModifiers(event->scroll.state));
  776. case PUGL_SCROLL:
  777. return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
  778. prefix,
  779. event->scroll.dx,
  780. event->scroll.dy,
  781. event->scroll.x,
  782. event->scroll.y) +
  783. printModifiers(event->scroll.state));
  784. case PUGL_POINTER_IN:
  785. return PRINT("%sMouse enter at " PFMT "\n",
  786. prefix,
  787. event->crossing.x,
  788. event->crossing.y);
  789. case PUGL_POINTER_OUT:
  790. return PRINT("%sMouse leave at " PFMT "\n",
  791. prefix,
  792. event->crossing.x,
  793. event->crossing.y);
  794. case PUGL_FOCUS_IN:
  795. return PRINT("%sFocus in %i\n",
  796. prefix,
  797. event->focus.mode);
  798. case PUGL_FOCUS_OUT:
  799. return PRINT("%sFocus out %i\n",
  800. prefix,
  801. event->focus.mode);
  802. case PUGL_CLIENT:
  803. return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
  804. prefix,
  805. event->client.data1,
  806. event->client.data2);
  807. case PUGL_TIMER:
  808. return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
  809. default:
  810. break;
  811. }
  812. if (verbose) {
  813. switch (event->type) {
  814. case PUGL_CREATE:
  815. return fprintf(stderr, "%sCreate\n", prefix);
  816. case PUGL_DESTROY:
  817. return fprintf(stderr, "%sDestroy\n", prefix);
  818. case PUGL_MAP:
  819. return fprintf(stderr, "%sMap\n", prefix);
  820. case PUGL_UNMAP:
  821. return fprintf(stderr, "%sUnmap\n", prefix);
  822. case PUGL_UPDATE:
  823. return 0; // fprintf(stderr, "%sUpdate\n", prefix);
  824. case PUGL_CONFIGURE:
  825. return PRINT("%sConfigure " PFMT " " PFMT "\n",
  826. prefix,
  827. event->configure.x,
  828. event->configure.y,
  829. event->configure.width,
  830. event->configure.height);
  831. case PUGL_EXPOSE:
  832. return PRINT("%sExpose " PFMT " " PFMT "\n",
  833. prefix,
  834. event->expose.x,
  835. event->expose.y,
  836. event->expose.width,
  837. event->expose.height);
  838. case PUGL_CLOSE:
  839. return PRINT("%sClose\n", prefix);
  840. case PUGL_MOTION:
  841. return PRINT("%sMouse motion at " PFMT "\n",
  842. prefix,
  843. event->motion.x,
  844. event->motion.y);
  845. default:
  846. return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
  847. }
  848. }
  849. #undef PRINT
  850. #undef PFMT
  851. #undef FFMT
  852. return 0;
  853. }
  854. #endif
  855. #undef DGL_DBG
  856. #undef DGL_DBGF
  857. // -----------------------------------------------------------------------
  858. END_NAMESPACE_DGL