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.

1093 lines
29KB

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