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.

1190 lines
33KB

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