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