DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

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