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.

1138 lines
31KB

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