Collection of DPF-based plugins for packaging
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.

600 lines
16KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2025 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 "../TopLevelWidget.hpp"
  18. #include "pugl.hpp"
  19. START_NAMESPACE_DGL
  20. // -----------------------------------------------------------------------
  21. // ScopedGraphicsContext
  22. Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win)
  23. : window(win),
  24. ppData(nullptr),
  25. active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)),
  26. reenter(false) {}
  27. Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin)
  28. : window(win),
  29. ppData(transientWin.pData),
  30. active(false),
  31. reenter(window.pData->view != nullptr)
  32. {
  33. if (reenter)
  34. {
  35. puglBackendLeave(ppData->view);
  36. active = puglBackendEnter(window.pData->view);
  37. }
  38. }
  39. Window::ScopedGraphicsContext::~ScopedGraphicsContext()
  40. {
  41. done();
  42. }
  43. void Window::ScopedGraphicsContext::done()
  44. {
  45. if (active)
  46. {
  47. puglBackendLeave(window.pData->view);
  48. active = false;
  49. }
  50. if (reenter)
  51. {
  52. reenter = false;
  53. DISTRHO_SAFE_ASSERT_RETURN(ppData != nullptr,);
  54. puglBackendEnter(ppData->view);
  55. }
  56. }
  57. void Window::ScopedGraphicsContext::reinit()
  58. {
  59. DISTRHO_SAFE_ASSERT_RETURN(!active,);
  60. DISTRHO_SAFE_ASSERT_RETURN(!reenter,);
  61. DISTRHO_SAFE_ASSERT_RETURN(ppData != nullptr,);
  62. reenter = true;
  63. puglBackendLeave(ppData->view);
  64. active = puglBackendEnter(window.pData->view);
  65. }
  66. // -----------------------------------------------------------------------
  67. // Window
  68. Window::Window(Application& app)
  69. : pData(new PrivateData(app, this))
  70. {
  71. pData->initPost();
  72. }
  73. Window::Window(Application& app, Window& transientParentWindow)
  74. : pData(new PrivateData(app, this, transientParentWindow.pData))
  75. {
  76. pData->initPost();
  77. }
  78. Window::Window(Application& app,
  79. const uintptr_t parentWindowHandle,
  80. const double scaleFactor,
  81. const bool resizable)
  82. : pData(new PrivateData(app, this, parentWindowHandle, scaleFactor, resizable))
  83. {
  84. pData->initPost();
  85. }
  86. Window::Window(Application& app,
  87. const uintptr_t parentWindowHandle,
  88. const uint width,
  89. const uint height,
  90. const double scaleFactor,
  91. const bool resizable)
  92. : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable, false, false))
  93. {
  94. pData->initPost();
  95. }
  96. Window::Window(Application& app,
  97. const uintptr_t parentWindowHandle,
  98. const uint width,
  99. const uint height,
  100. const double scaleFactor,
  101. const bool resizable,
  102. const bool usesScheduledRepaints,
  103. const bool usesSizeRequest,
  104. const bool doPostInit)
  105. : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable,
  106. usesScheduledRepaints, usesSizeRequest))
  107. {
  108. if (doPostInit)
  109. pData->initPost();
  110. }
  111. Window::~Window()
  112. {
  113. delete pData;
  114. }
  115. bool Window::isEmbed() const noexcept
  116. {
  117. return pData->isEmbed;
  118. }
  119. bool Window::isVisible() const noexcept
  120. {
  121. return pData->isVisible;
  122. }
  123. void Window::setVisible(const bool visible)
  124. {
  125. if (visible)
  126. pData->show();
  127. else
  128. pData->hide();
  129. }
  130. void Window::show()
  131. {
  132. pData->show();
  133. }
  134. void Window::hide()
  135. {
  136. pData->hide();
  137. }
  138. void Window::close()
  139. {
  140. pData->close();
  141. }
  142. bool Window::isResizable() const noexcept
  143. {
  144. return pData->view != nullptr
  145. && puglGetViewHint(pData->view, PUGL_RESIZABLE) == PUGL_TRUE;
  146. }
  147. void Window::setResizable(const bool resizable)
  148. {
  149. pData->setResizable(resizable);
  150. }
  151. int Window::getOffsetX() const noexcept
  152. {
  153. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);
  154. return puglGetFrame(pData->view).x;
  155. }
  156. int Window::getOffsetY() const noexcept
  157. {
  158. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);
  159. return puglGetFrame(pData->view).y;
  160. }
  161. Point<int> Window::getOffset() const noexcept
  162. {
  163. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>());
  164. const PuglRect rect = puglGetFrame(pData->view);
  165. return Point<int>(rect.x, rect.y);
  166. }
  167. void Window::setOffsetX(const int x)
  168. {
  169. setOffset(x, getOffsetY());
  170. }
  171. void Window::setOffsetY(const int y)
  172. {
  173. setOffset(getOffsetX(), y);
  174. }
  175. void Window::setOffset(const int x, const int y)
  176. {
  177. // do not call this for embed windows!
  178. DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,);
  179. if (pData->view != nullptr)
  180. puglSetPosition(pData->view, x, y);
  181. }
  182. void Window::setOffset(const Point<int>& offset)
  183. {
  184. setOffset(offset.getX(), offset.getY());
  185. }
  186. uint Window::getWidth() const noexcept
  187. {
  188. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);
  189. const double width = puglGetFrame(pData->view).width;
  190. DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0);
  191. return static_cast<uint>(width + 0.5);
  192. }
  193. uint Window::getHeight() const noexcept
  194. {
  195. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);
  196. const double height = puglGetFrame(pData->view).height;
  197. DISTRHO_SAFE_ASSERT_RETURN(height > 0.0, 0);
  198. return static_cast<uint>(height + 0.5);
  199. }
  200. Size<uint> Window::getSize() const noexcept
  201. {
  202. DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>());
  203. const PuglRect rect = puglGetFrame(pData->view);
  204. DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size<uint>());
  205. DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size<uint>());
  206. return Size<uint>(static_cast<uint>(rect.width + 0.5),
  207. static_cast<uint>(rect.height + 0.5));
  208. }
  209. void Window::setWidth(const uint width)
  210. {
  211. setSize(width, getHeight());
  212. }
  213. void Window::setHeight(const uint height)
  214. {
  215. setSize(getWidth(), height);
  216. }
  217. void Window::setSize(uint width, uint height)
  218. {
  219. DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,);
  220. if (pData->isEmbed)
  221. {
  222. const double scaleFactor = pData->scaleFactor;
  223. uint minWidth = pData->minWidth;
  224. uint minHeight = pData->minHeight;
  225. if (pData->autoScaling && d_isNotEqual(scaleFactor, 1.0))
  226. {
  227. minWidth = d_roundToUnsignedInt(minWidth * scaleFactor);
  228. minHeight = d_roundToUnsignedInt(minHeight * scaleFactor);
  229. }
  230. // handle geometry constraints here
  231. if (width < minWidth)
  232. width = minWidth;
  233. if (height < minHeight)
  234. height = minHeight;
  235. if (pData->keepAspectRatio)
  236. {
  237. const double ratio = static_cast<double>(pData->minWidth)
  238. / static_cast<double>(pData->minHeight);
  239. const double reqRatio = static_cast<double>(width)
  240. / static_cast<double>(height);
  241. if (d_isNotEqual(ratio, reqRatio))
  242. {
  243. // fix width
  244. if (reqRatio > ratio)
  245. width = d_roundToUnsignedInt(height * ratio);
  246. // fix height
  247. else
  248. height = d_roundToUnsignedInt(static_cast<double>(width) / ratio);
  249. }
  250. }
  251. }
  252. if (pData->usesSizeRequest)
  253. {
  254. DISTRHO_SAFE_ASSERT_RETURN(pData->topLevelWidgets.size() != 0,);
  255. TopLevelWidget* const topLevelWidget = pData->topLevelWidgets.front();
  256. DISTRHO_SAFE_ASSERT_RETURN(topLevelWidget != nullptr,);
  257. topLevelWidget->requestSizeChange(width, height);
  258. }
  259. else if (pData->view != nullptr)
  260. {
  261. puglSetSizeAndDefault(pData->view, width, height);
  262. // there are no resize events for closed windows, so short-circuit the top-level widgets here
  263. if (pData->isClosed)
  264. {
  265. for (std::list<TopLevelWidget*>::iterator it = pData->topLevelWidgets.begin(),
  266. end = pData->topLevelWidgets.end(); it != end; ++it)
  267. {
  268. ((Widget*)*it)->setSize(width, height);
  269. }
  270. }
  271. }
  272. }
  273. void Window::setSize(const Size<uint>& size)
  274. {
  275. setSize(size.getWidth(), size.getHeight());
  276. }
  277. const char* Window::getTitle() const noexcept
  278. {
  279. return pData->view != nullptr ? puglGetViewString(pData->view, PUGL_WINDOW_TITLE) : "";
  280. }
  281. void Window::setTitle(const char* const title)
  282. {
  283. if (pData->view != nullptr)
  284. puglSetViewString(pData->view, PUGL_WINDOW_TITLE, title);
  285. }
  286. bool Window::isIgnoringKeyRepeat() const noexcept
  287. {
  288. return pData->view != nullptr
  289. && puglGetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT) == PUGL_TRUE;
  290. }
  291. void Window::setIgnoringKeyRepeat(const bool ignore) noexcept
  292. {
  293. if (pData->view != nullptr)
  294. puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore);
  295. }
  296. const void* Window::getClipboard(size_t& dataSize)
  297. {
  298. return pData->getClipboard(dataSize);
  299. }
  300. bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize)
  301. {
  302. return pData->view != nullptr
  303. && puglSetClipboard(pData->view, mimeType != nullptr ? mimeType : "text/plain", data, dataSize) == PUGL_SUCCESS;
  304. }
  305. bool Window::setCursor(const MouseCursor cursor)
  306. {
  307. return pData->view != nullptr
  308. && puglSetCursor(pData->view, static_cast<PuglCursor>(cursor)) == PUGL_SUCCESS;
  309. }
  310. bool Window::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
  311. {
  312. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false)
  313. return pData->addIdleCallback(callback, timerFrequencyInMs);
  314. }
  315. bool Window::removeIdleCallback(IdleCallback* const callback)
  316. {
  317. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false)
  318. return pData->removeIdleCallback(callback);
  319. }
  320. Application& Window::getApp() const noexcept
  321. {
  322. return pData->app;
  323. }
  324. #ifndef DPF_TEST_WINDOW_CPP
  325. const GraphicsContext& Window::getGraphicsContext() const noexcept
  326. {
  327. return pData->getGraphicsContext();
  328. }
  329. #endif
  330. uintptr_t Window::getNativeWindowHandle() const noexcept
  331. {
  332. return pData->view != nullptr ? puglGetNativeView(pData->view) : 0;
  333. }
  334. double Window::getScaleFactor() const noexcept
  335. {
  336. return pData->scaleFactor;
  337. }
  338. void Window::focus()
  339. {
  340. pData->focus();
  341. }
  342. #ifdef DGL_USE_FILE_BROWSER
  343. bool Window::openFileBrowser(const FileBrowserOptions& options)
  344. {
  345. return pData->openFileBrowser(options);
  346. }
  347. #endif
  348. #ifdef DGL_USE_WEB_VIEW
  349. bool Window::createWebView(const char* const url, const DGL_NAMESPACE::WebViewOptions& options)
  350. {
  351. return pData->createWebView(url, options);
  352. }
  353. void Window::evaluateJS(const char* const js)
  354. {
  355. DISTRHO_SAFE_ASSERT_RETURN(pData->webViewHandle != nullptr,);
  356. webViewEvaluateJS(pData->webViewHandle, js);
  357. }
  358. #endif
  359. void Window::repaint() noexcept
  360. {
  361. if (pData->view == nullptr)
  362. return;
  363. if (pData->usesScheduledRepaints)
  364. pData->appData->needsRepaint = true;
  365. puglPostRedisplay(pData->view);
  366. }
  367. void Window::repaint(const Rectangle<uint>& rect) noexcept
  368. {
  369. if (pData->view == nullptr)
  370. return;
  371. if (pData->usesScheduledRepaints)
  372. pData->appData->needsRepaint = true;
  373. PuglRect prect = {
  374. static_cast<PuglCoord>(rect.getX()),
  375. static_cast<PuglCoord>(rect.getY()),
  376. static_cast<PuglSpan>(rect.getWidth()),
  377. static_cast<PuglSpan>(rect.getHeight()),
  378. };
  379. if (pData->autoScaling)
  380. {
  381. const double autoScaleFactor = pData->autoScaleFactor;
  382. prect.x = static_cast<PuglCoord>(prect.x * autoScaleFactor);
  383. prect.y = static_cast<PuglCoord>(prect.y * autoScaleFactor);
  384. prect.width = static_cast<PuglSpan>(prect.width * autoScaleFactor + 0.5);
  385. prect.height = static_cast<PuglSpan>(prect.height * autoScaleFactor + 0.5);
  386. }
  387. puglPostRedisplayRect(pData->view, prect);
  388. }
  389. void Window::renderToPicture(const char* const filename)
  390. {
  391. pData->filenameToRenderInto = strdup(filename);
  392. }
  393. void Window::runAsModal(bool blockWait)
  394. {
  395. pData->runAsModal(blockWait);
  396. }
  397. Size<uint> Window::getGeometryConstraints(bool& keepAspectRatio)
  398. {
  399. keepAspectRatio = pData->keepAspectRatio;
  400. return Size<uint>(pData->minWidth, pData->minHeight);
  401. }
  402. void Window::setGeometryConstraints(uint minimumWidth,
  403. uint minimumHeight,
  404. const bool keepAspectRatio,
  405. const bool automaticallyScale,
  406. bool resizeNowIfAutoScaling)
  407. {
  408. DISTRHO_SAFE_ASSERT_RETURN(minimumWidth > 0,);
  409. DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,);
  410. // prevent auto-scaling up 2x
  411. if (resizeNowIfAutoScaling && automaticallyScale && pData->autoScaling == automaticallyScale)
  412. resizeNowIfAutoScaling = false;
  413. pData->minWidth = minimumWidth;
  414. pData->minHeight = minimumHeight;
  415. pData->autoScaling = automaticallyScale;
  416. pData->keepAspectRatio = keepAspectRatio;
  417. if (pData->view == nullptr)
  418. return;
  419. const double scaleFactor = pData->scaleFactor;
  420. if (automaticallyScale && scaleFactor != 1.0)
  421. {
  422. minimumWidth = d_roundToUnsignedInt(minimumWidth * scaleFactor);
  423. minimumHeight = d_roundToUnsignedInt(minimumHeight * scaleFactor);
  424. }
  425. puglSetGeometryConstraints(pData->view, minimumWidth, minimumHeight, keepAspectRatio);
  426. if (scaleFactor != 1.0 && automaticallyScale && resizeNowIfAutoScaling)
  427. {
  428. const Size<uint> size(getSize());
  429. setSize(static_cast<uint>(size.getWidth() * scaleFactor + 0.5),
  430. static_cast<uint>(size.getHeight() * scaleFactor + 0.5));
  431. }
  432. }
  433. void Window::setTransientParent(const uintptr_t transientParentWindowHandle)
  434. {
  435. if (pData->view != nullptr)
  436. puglSetTransientParent(pData->view, transientParentWindowHandle);
  437. }
  438. std::vector<ClipboardDataOffer> Window::getClipboardDataOfferTypes()
  439. {
  440. std::vector<ClipboardDataOffer> offerTypes;
  441. if (pData->view == nullptr)
  442. return offerTypes;
  443. if (const uint32_t numTypes = puglGetNumClipboardTypes(pData->view))
  444. {
  445. offerTypes.reserve(numTypes);
  446. for (uint32_t i=0; i<numTypes; ++i)
  447. {
  448. const ClipboardDataOffer offer = { i + 1, puglGetClipboardType(pData->view, i) };
  449. offerTypes.push_back(offer);
  450. }
  451. }
  452. return offerTypes;
  453. }
  454. uint32_t Window::onClipboardDataOffer()
  455. {
  456. std::vector<ClipboardDataOffer> offers(getClipboardDataOfferTypes());
  457. for (std::vector<ClipboardDataOffer>::iterator it=offers.begin(), end=offers.end(); it != end;++it)
  458. {
  459. const ClipboardDataOffer offer = *it;
  460. if (std::strcmp(offer.type, "text/plain") == 0)
  461. return offer.id;
  462. }
  463. return 0;
  464. }
  465. bool Window::onClose()
  466. {
  467. return true;
  468. }
  469. void Window::onFocus(bool, CrossingMode)
  470. {
  471. }
  472. void Window::onReshape(const uint width, const uint height)
  473. {
  474. if (pData->view != nullptr)
  475. puglFallbackOnResize(pData->view, width, height);
  476. }
  477. void Window::onScaleFactorChanged(double)
  478. {
  479. }
  480. #ifdef DGL_USE_FILE_BROWSER
  481. void Window::onFileSelected(const char*)
  482. {
  483. }
  484. #endif
  485. // -----------------------------------------------------------------------
  486. END_NAMESPACE_DGL