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.

1218 lines
34KB

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