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.

1241 lines
34KB

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