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.

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