DPF

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

WindowPrivateData.cpp (35025B)


      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 "TopLevelWidgetPrivateData.hpp"
     19 
     20 #include "pugl.hpp"
     21 
     22 // #define DGL_DEBUG_EVENTS
     23 
     24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
     25 # ifdef DISTRHO_PROPER_CPP11_SUPPORT
     26 #  include <cinttypes>
     27 # else
     28 #  include <inttypes.h>
     29 # endif
     30 #endif
     31 
     32 #ifdef DISTRHO_OS_WINDOWS
     33 # include <windows.h>
     34 #endif
     35 
     36 START_NAMESPACE_DGL
     37 
     38 #ifdef DISTRHO_OS_WINDOWS
     39 # include "pugl-upstream/src/win.h"
     40 #endif
     41 
     42 #ifdef DGL_DEBUG_EVENTS
     43 # define DGL_DBG(msg)  std::fprintf(stderr, "%s", msg);
     44 # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__);
     45 # define DGL_DBGF      std::fflush(stderr);
     46 #else
     47 # define DGL_DBG(msg)
     48 # define DGL_DBGp(...)
     49 # define DGL_DBGF
     50 #endif
     51 
     52 #define DEFAULT_WIDTH 640
     53 #define DEFAULT_HEIGHT 480
     54 
     55 #define FOR_EACH_TOP_LEVEL_WIDGET(it) \
     56   for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it)
     57 
     58 #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \
     59   for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit)
     60 
     61 // -----------------------------------------------------------------------
     62 
     63 static double getScaleFactor(const PuglView* const view)
     64 {
     65     // allow custom scale for testing
     66     if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
     67         return std::max(1.0, std::atof(scale));
     68 
     69     if (view != nullptr)
     70         return puglGetScaleFactor(view);
     71 
     72     return 1.0;
     73 }
     74 
     75 static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView)
     76 {
     77     if (world == nullptr)
     78         return nullptr;
     79 
     80     if (PuglView* const view = puglNewView(world))
     81     {
     82         puglSetTransientParent(view, puglGetNativeView(transientParentView));
     83         return view;
     84     }
     85 
     86     return nullptr;
     87 }
     88 
     89 static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle)
     90 {
     91     if (world == nullptr)
     92         return nullptr;
     93 
     94     if (PuglView* const view = puglNewView(world))
     95     {
     96         puglSetParentWindow(view, parentWindowHandle);
     97 
     98         if (parentWindowHandle != 0)
     99             puglSetPosition(view, 0, 0);
    100 
    101         return view;
    102     }
    103 
    104     return nullptr;
    105 }
    106 
    107 // -----------------------------------------------------------------------
    108 
    109 Window::PrivateData::PrivateData(Application& a, Window* const s)
    110     : app(a),
    111       appData(a.pData),
    112       self(s),
    113       view(appData->world != nullptr ? puglNewView(appData->world) : nullptr),
    114       topLevelWidgets(),
    115       isClosed(true),
    116       isVisible(false),
    117       isEmbed(false),
    118       usesScheduledRepaints(false),
    119       usesSizeRequest(false),
    120       scaleFactor(DGL_NAMESPACE::getScaleFactor(view)),
    121       autoScaling(false),
    122       autoScaleFactor(1.0),
    123       minWidth(0),
    124       minHeight(0),
    125       keepAspectRatio(false),
    126       ignoreIdleCallbacks(false),
    127       waitingForClipboardData(false),
    128       waitingForClipboardEvents(false),
    129       clipboardTypeId(0),
    130       filenameToRenderInto(nullptr),
    131      #ifndef DGL_FILE_BROWSER_DISABLED
    132       fileBrowserHandle(nullptr),
    133      #endif
    134       modal()
    135 {
    136     initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
    137 }
    138 
    139 Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData)
    140     : app(a),
    141       appData(a.pData),
    142       self(s),
    143       view(puglNewViewWithTransientParent(appData->world, ppData->view)),
    144       topLevelWidgets(),
    145       isClosed(true),
    146       isVisible(false),
    147       isEmbed(false),
    148       usesScheduledRepaints(false),
    149       usesSizeRequest(false),
    150       scaleFactor(ppData->scaleFactor),
    151       autoScaling(false),
    152       autoScaleFactor(1.0),
    153       minWidth(0),
    154       minHeight(0),
    155       keepAspectRatio(false),
    156       ignoreIdleCallbacks(false),
    157       waitingForClipboardData(false),
    158       waitingForClipboardEvents(false),
    159       clipboardTypeId(0),
    160       filenameToRenderInto(nullptr),
    161      #ifndef DGL_FILE_BROWSER_DISABLED
    162       fileBrowserHandle(nullptr),
    163      #endif
    164       modal(ppData)
    165 {
    166     initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
    167 }
    168 
    169 Window::PrivateData::PrivateData(Application& a, Window* const s,
    170                                  const uintptr_t parentWindowHandle,
    171                                  const double scale, const bool resizable)
    172     : app(a),
    173       appData(a.pData),
    174       self(s),
    175       view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
    176       topLevelWidgets(),
    177       isClosed(parentWindowHandle == 0),
    178       isVisible(parentWindowHandle != 0),
    179       isEmbed(parentWindowHandle != 0),
    180       usesScheduledRepaints(false),
    181       usesSizeRequest(false),
    182       scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)),
    183       autoScaling(false),
    184       autoScaleFactor(1.0),
    185       minWidth(0),
    186       minHeight(0),
    187       keepAspectRatio(false),
    188       ignoreIdleCallbacks(false),
    189       waitingForClipboardData(false),
    190       waitingForClipboardEvents(false),
    191       clipboardTypeId(0),
    192       filenameToRenderInto(nullptr),
    193      #ifndef DGL_FILE_BROWSER_DISABLED
    194       fileBrowserHandle(nullptr),
    195      #endif
    196       modal()
    197 {
    198     initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
    199 }
    200 
    201 Window::PrivateData::PrivateData(Application& a, Window* const s,
    202                                  const uintptr_t parentWindowHandle,
    203                                  const uint width, const uint height,
    204                                  const double scale, const bool resizable,
    205                                  const bool _usesScheduledRepaints,
    206                                  const bool _usesSizeRequest)
    207     : app(a),
    208       appData(a.pData),
    209       self(s),
    210       view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)),
    211       topLevelWidgets(),
    212       isClosed(parentWindowHandle == 0),
    213       isVisible(parentWindowHandle != 0 && view != nullptr),
    214       isEmbed(parentWindowHandle != 0),
    215       usesScheduledRepaints(_usesScheduledRepaints),
    216       usesSizeRequest(_usesSizeRequest),
    217       scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)),
    218       autoScaling(false),
    219       autoScaleFactor(1.0),
    220       minWidth(0),
    221       minHeight(0),
    222       keepAspectRatio(false),
    223       ignoreIdleCallbacks(false),
    224       waitingForClipboardData(false),
    225       waitingForClipboardEvents(false),
    226       clipboardTypeId(0),
    227       filenameToRenderInto(nullptr),
    228      #ifndef DGL_FILE_BROWSER_DISABLED
    229       fileBrowserHandle(nullptr),
    230      #endif
    231       modal()
    232 {
    233     initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable);
    234 }
    235 
    236 Window::PrivateData::~PrivateData()
    237 {
    238     appData->idleCallbacks.remove(this);
    239     appData->windows.remove(self);
    240     std::free(filenameToRenderInto);
    241 
    242     if (view == nullptr)
    243         return;
    244 
    245     if (isEmbed)
    246     {
    247        #ifndef DGL_FILE_BROWSER_DISABLED
    248         if (fileBrowserHandle != nullptr)
    249             fileBrowserClose(fileBrowserHandle);
    250        #endif
    251         puglHide(view);
    252         appData->oneWindowClosed();
    253         isClosed = true;
    254         isVisible = false;
    255     }
    256 
    257     puglFreeView(view);
    258 }
    259 
    260 // -----------------------------------------------------------------------
    261 
    262 void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable)
    263 {
    264     appData->windows.push_back(self);
    265     appData->idleCallbacks.push_back(this);
    266     memset(graphicsContext, 0, sizeof(graphicsContext));
    267 
    268     if (view == nullptr)
    269     {
    270         d_stderr2("Failed to create Pugl view, everything will fail!");
    271         return;
    272     }
    273 
    274     puglSetMatchingBackendForCurrentBuild(view);
    275     puglSetHandle(view, this);
    276 
    277     puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
    278     puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
    279    #if defined(DGL_USE_RGBA) && DGL_USE_RGBA
    280     puglSetViewHint(view, PUGL_DEPTH_BITS, 24);
    281    #else
    282     puglSetViewHint(view, PUGL_DEPTH_BITS, 16);
    283    #endif
    284     puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
    285 
    286     // PUGL_SAMPLES ??
    287     puglSetEventFunc(view, puglEventCallback);
    288 
    289     // setting default size triggers system-level calls, do it last
    290     puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height));
    291 }
    292 
    293 bool Window::PrivateData::initPost()
    294 {
    295     if (view == nullptr)
    296         return false;
    297 
    298     // create view now, as a few methods we allow devs to use require it
    299     if (puglRealize(view) != PUGL_SUCCESS)
    300     {
    301         view = nullptr;
    302         d_stderr2("Failed to realize Pugl view, everything will fail!");
    303         return false;
    304     }
    305 
    306     if (isEmbed)
    307     {
    308         appData->oneWindowShown();
    309         puglShow(view, PUGL_SHOW_PASSIVE);
    310     }
    311 
    312     return true;
    313 }
    314 
    315 // -----------------------------------------------------------------------
    316 
    317 void Window::PrivateData::close()
    318 {
    319     DGL_DBG("Window close\n");
    320     // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData);
    321 
    322     if (isEmbed || isClosed)
    323         return;
    324 
    325     isClosed = true;
    326     hide();
    327     appData->oneWindowClosed();
    328 }
    329 
    330 // -----------------------------------------------------------------------
    331 
    332 void Window::PrivateData::show()
    333 {
    334     if (isVisible)
    335     {
    336         DGL_DBG("Window show matches current visible state, ignoring request\n");
    337         return;
    338     }
    339     if (isEmbed)
    340     {
    341         DGL_DBG("Window show cannot be called when embedded\n");
    342         return;
    343     }
    344 
    345     DGL_DBG("Window show called\n");
    346 
    347     if (view == nullptr)
    348         return;
    349 
    350     if (isClosed)
    351     {
    352         isClosed = false;
    353         appData->oneWindowShown();
    354 
    355         // FIXME
    356 //         PuglRect rect = puglGetFrame(view);
    357 //         puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));
    358 
    359 #if defined(DISTRHO_OS_WINDOWS)
    360         puglWin32ShowCentered(view);
    361 #elif defined(DISTRHO_OS_MAC)
    362         puglMacOSShowCentered(view);
    363 #else
    364         puglShow(view, PUGL_SHOW_RAISE);
    365 #endif
    366     }
    367     else
    368     {
    369 #ifdef DISTRHO_OS_WINDOWS
    370         puglWin32RestoreWindow(view);
    371 #else
    372         puglShow(view, PUGL_SHOW_RAISE);
    373 #endif
    374     }
    375 
    376     isVisible = true;
    377 }
    378 
    379 void Window::PrivateData::hide()
    380 {
    381     if (isEmbed)
    382     {
    383         DGL_DBG("Window hide cannot be called when embedded\n");
    384         return;
    385     }
    386     if (! isVisible)
    387     {
    388         DGL_DBG("Window hide matches current visible state, ignoring request\n");
    389         return;
    390     }
    391 
    392     DGL_DBG("Window hide called\n");
    393 
    394     if (modal.enabled)
    395         stopModal();
    396 
    397 #ifndef DGL_FILE_BROWSER_DISABLED
    398     if (fileBrowserHandle != nullptr)
    399     {
    400         fileBrowserClose(fileBrowserHandle);
    401         fileBrowserHandle = nullptr;
    402     }
    403 #endif
    404 
    405     puglHide(view);
    406 
    407     isVisible = false;
    408 }
    409 
    410 // -----------------------------------------------------------------------
    411 
    412 void Window::PrivateData::focus()
    413 {
    414     if (view == nullptr)
    415         return;
    416 
    417     if (! isEmbed)
    418         puglRaiseWindow(view);
    419 
    420     puglGrabFocus(view);
    421 }
    422 
    423 // -----------------------------------------------------------------------
    424 
    425 void Window::PrivateData::setResizable(const bool resizable)
    426 {
    427     DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,);
    428 
    429     DGL_DBG("Window setResizable called\n");
    430 
    431     puglSetResizable(view, resizable);
    432 }
    433 
    434 // -----------------------------------------------------------------------
    435 
    436 void Window::PrivateData::idleCallback()
    437 {
    438 #ifndef DGL_FILE_BROWSER_DISABLED
    439     if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle))
    440     {
    441         self->onFileSelected(fileBrowserGetPath(fileBrowserHandle));
    442         fileBrowserClose(fileBrowserHandle);
    443         fileBrowserHandle = nullptr;
    444     }
    445 #endif
    446 }
    447 
    448 // -----------------------------------------------------------------------
    449 // idle callback stuff
    450 
    451 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
    452 {
    453     if (ignoreIdleCallbacks || view == nullptr)
    454         return false;
    455 
    456     if (timerFrequencyInMs == 0)
    457     {
    458         appData->idleCallbacks.push_back(callback);
    459         return true;
    460     }
    461 
    462     return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS;
    463 }
    464 
    465 bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)
    466 {
    467     if (ignoreIdleCallbacks || view == nullptr)
    468         return false;
    469 
    470     if (std::find(appData->idleCallbacks.begin(),
    471                   appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end())
    472     {
    473         appData->idleCallbacks.remove(callback);
    474         return true;
    475     }
    476 
    477     return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS;
    478 }
    479 
    480 #ifndef DGL_FILE_BROWSER_DISABLED
    481 // -----------------------------------------------------------------------
    482 // file handling
    483 
    484 bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
    485 {
    486     if (fileBrowserHandle != nullptr)
    487         fileBrowserClose(fileBrowserHandle);
    488 
    489     FileBrowserOptions options2 = options;
    490 
    491     if (options2.title == nullptr)
    492         options2.title = puglGetViewString(view, PUGL_WINDOW_TITLE);
    493 
    494     fileBrowserHandle = fileBrowserCreate(isEmbed,
    495                                           puglGetNativeView(view),
    496                                           autoScaling ? autoScaleFactor : scaleFactor,
    497                                           options2);
    498 
    499     return fileBrowserHandle != nullptr;
    500 }
    501 #endif // ! DGL_FILE_BROWSER_DISABLED
    502 
    503 // -----------------------------------------------------------------------
    504 // modal handling
    505 
    506 void Window::PrivateData::startModal()
    507 {
    508     DGL_DBG("Window modal loop starting..."); DGL_DBGF;
    509     DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());
    510 
    511     // activate modal mode for this window
    512     modal.enabled = true;
    513 
    514     // make parent give focus to us
    515     modal.parent->modal.child = this;
    516 
    517     // make sure both parent and ourselves are visible
    518     modal.parent->show();
    519     show();
    520 
    521 #ifdef DISTRHO_OS_MAC
    522     puglMacOSAddChildWindow(modal.parent->view, view);
    523 #endif
    524 
    525     DGL_DBG("Ok\n");
    526 }
    527 
    528 void Window::PrivateData::stopModal()
    529 {
    530     DGL_DBG("Window modal loop stopping..."); DGL_DBGF;
    531 
    532     // deactivate modal mode
    533     modal.enabled = false;
    534 
    535     // safety checks, make sure we have a parent and we are currently active as the child to give focus to
    536     if (modal.parent == nullptr)
    537         return;
    538     if (modal.parent->modal.child != this)
    539         return;
    540 
    541 #ifdef DISTRHO_OS_MAC
    542     puglMacOSRemoveChildWindow(modal.parent->view, view);
    543 #endif
    544 
    545     // stop parent from giving focus to us, so it behaves like normal
    546     modal.parent->modal.child = nullptr;
    547 
    548     // refocus main window after closing child
    549     if (! modal.parent->isClosed)
    550     {
    551         const Widget::MotionEvent ev;
    552         modal.parent->onPuglMotion(ev);
    553         modal.parent->focus();
    554     }
    555 
    556     DGL_DBG("Ok\n");
    557 }
    558 
    559 void Window::PrivateData::runAsModal(const bool blockWait)
    560 {
    561     DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait);
    562     startModal();
    563 
    564     if (blockWait)
    565     {
    566         DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,);
    567 
    568         while (isVisible && modal.enabled)
    569             appData->idle(10);
    570 
    571         stopModal();
    572     }
    573     else
    574     {
    575         appData->idle(0);
    576     }
    577 }
    578 
    579 // -----------------------------------------------------------------------
    580 // pugl events
    581 
    582 void Window::PrivateData::onPuglConfigure(const double width, const double height)
    583 {
    584     DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,);
    585 
    586     DGL_DBGp("PUGL: onReshape : %f %f\n", width, height);
    587 
    588     if (autoScaling)
    589     {
    590         const double scaleHorizontal = width  / static_cast<double>(minWidth);
    591         const double scaleVertical   = height / static_cast<double>(minHeight);
    592         autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical;
    593     }
    594     else
    595     {
    596         autoScaleFactor = 1.0;
    597     }
    598 
    599     const uint uwidth = static_cast<uint>(width / autoScaleFactor + 0.5);
    600     const uint uheight = static_cast<uint>(height / autoScaleFactor + 0.5);
    601 
    602     self->onReshape(uwidth, uheight);
    603 
    604 #ifndef DPF_TEST_WINDOW_CPP
    605     FOR_EACH_TOP_LEVEL_WIDGET(it)
    606     {
    607         TopLevelWidget* const widget = *it;
    608 
    609         /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one.
    610          * This is because we want TopLevelWidget::setSize to handle both window and widget size,
    611          * but we dont want to change window size here, because we are the window..
    612          * So we just call the Widget specific method manually.
    613          *
    614          * Alternatively, we could expose a resize function on the pData, like done with the display function.
    615          * But there is nothing extra we need to do in there, so this works fine.
    616          */
    617         ((Widget*)widget)->setSize(uwidth, uheight);
    618     }
    619 #endif
    620 
    621     // always repaint after a resize
    622     puglPostRedisplay(view);
    623 }
    624 
    625 void Window::PrivateData::onPuglExpose()
    626 {
    627     // DGL_DBG("PUGL: onPuglExpose\n");
    628 
    629     puglOnDisplayPrepare(view);
    630 
    631 #ifndef DPF_TEST_WINDOW_CPP
    632     FOR_EACH_TOP_LEVEL_WIDGET(it)
    633     {
    634         TopLevelWidget* const widget(*it);
    635 
    636         if (widget->isVisible())
    637             widget->pData->display();
    638     }
    639 
    640     if (char* const filename = filenameToRenderInto)
    641     {
    642         const PuglRect rect = puglGetFrame(view);
    643         filenameToRenderInto = nullptr;
    644         renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height));
    645         std::free(filename);
    646     }
    647 #endif
    648 }
    649 
    650 void Window::PrivateData::onPuglClose()
    651 {
    652     DGL_DBG("PUGL: onClose\n");
    653 
    654 #ifndef DISTRHO_OS_MAC
    655     // if we are running as standalone we can prevent closing in certain conditions
    656     if (appData->isStandalone)
    657     {
    658         // a child window is active, gives focus to it
    659         if (modal.child != nullptr)
    660             return modal.child->focus();
    661 
    662         // ask window if we should close
    663         if (! self->onClose())
    664             return;
    665     }
    666 #endif
    667 
    668     if (modal.enabled)
    669         stopModal();
    670 
    671     if (modal.child != nullptr)
    672     {
    673         modal.child->close();
    674         modal.child = nullptr;
    675     }
    676 
    677     close();
    678 }
    679 
    680 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode)
    681 {
    682     DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed);
    683 
    684     if (isClosed)
    685         return;
    686 
    687     if (modal.child != nullptr)
    688         return modal.child->focus();
    689 
    690     self->onFocus(focus, mode);
    691 }
    692 
    693 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
    694 {
    695     DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode);
    696 
    697     if (modal.child != nullptr)
    698         return modal.child->focus();
    699 
    700 #ifndef DPF_TEST_WINDOW_CPP
    701     FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
    702     {
    703         TopLevelWidget* const widget(*rit);
    704 
    705         if (widget->isVisible() && widget->onKeyboard(ev))
    706             break;
    707     }
    708 #endif
    709 }
    710 
    711 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
    712 {
    713     DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string);
    714 
    715     if (modal.child != nullptr)
    716         return modal.child->focus();
    717 
    718 #ifndef DPF_TEST_WINDOW_CPP
    719     FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
    720     {
    721         TopLevelWidget* const widget(*rit);
    722 
    723         if (widget->isVisible() && widget->onCharacterInput(ev))
    724             break;
    725     }
    726 #endif
    727 }
    728 
    729 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
    730 {
    731     DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY());
    732 
    733     if (modal.child != nullptr)
    734         return modal.child->focus();
    735 
    736 #ifndef DPF_TEST_WINDOW_CPP
    737     FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
    738     {
    739         TopLevelWidget* const widget(*rit);
    740 
    741         if (widget->isVisible() && widget->onMouse(ev))
    742             break;
    743     }
    744 #endif
    745 }
    746 
    747 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
    748 {
    749     DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY());
    750 
    751     if (modal.child != nullptr)
    752         return modal.child->focus();
    753 
    754 #ifndef DPF_TEST_WINDOW_CPP
    755     FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
    756     {
    757         TopLevelWidget* const widget(*rit);
    758 
    759         if (widget->isVisible() && widget->onMotion(ev))
    760             break;
    761     }
    762 #endif
    763 }
    764 
    765 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
    766 {
    767     DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY());
    768 
    769     if (modal.child != nullptr)
    770         return modal.child->focus();
    771 
    772 #ifndef DPF_TEST_WINDOW_CPP
    773     FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
    774     {
    775         TopLevelWidget* const widget(*rit);
    776 
    777         if (widget->isVisible() && widget->onScroll(ev))
    778             break;
    779     }
    780 #endif
    781 }
    782 
    783 const void* Window::PrivateData::getClipboard(size_t& dataSize)
    784 {
    785     clipboardTypeId = 0;
    786     waitingForClipboardData = true,
    787     waitingForClipboardEvents = true;
    788 
    789     // begin clipboard dance here
    790     if (puglPaste(view) != PUGL_SUCCESS)
    791     {
    792         dataSize = 0;
    793         waitingForClipboardEvents = false;
    794         return nullptr;
    795     }
    796 
    797    #ifdef DGL_USING_X11
    798     // wait for type request, clipboardTypeId must be != 0 to be valid
    799     int retry = static_cast<int>(2 / 0.03);
    800     while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0)
    801     {
    802         if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
    803             break;
    804     }
    805    #endif
    806 
    807     if (clipboardTypeId == 0)
    808     {
    809         dataSize = 0;
    810         waitingForClipboardEvents = false;
    811         return nullptr;
    812     }
    813 
    814    #ifdef DGL_USING_X11
    815     // wait for actual data (assumes offer was accepted)
    816     retry = static_cast<int>(2 / 0.03);
    817     while (waitingForClipboardData && --retry >= 0)
    818     {
    819         if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS)
    820             break;
    821     }
    822    #endif
    823 
    824     if (clipboardTypeId == 0)
    825     {
    826         dataSize = 0;
    827         waitingForClipboardEvents = false;
    828         return nullptr;
    829     }
    830 
    831     waitingForClipboardEvents = false;
    832     return puglGetClipboard(view, clipboardTypeId - 1, &dataSize);
    833 }
    834 
    835 uint32_t Window::PrivateData::onClipboardDataOffer()
    836 {
    837     DGL_DBG("onClipboardDataOffer\n");
    838 
    839     if ((clipboardTypeId = self->onClipboardDataOffer()) != 0)
    840         return clipboardTypeId;
    841 
    842     // stop waiting for data, it was rejected
    843     waitingForClipboardData = false;
    844     return 0;
    845 }
    846 
    847 void Window::PrivateData::onClipboardData(const uint32_t typeId)
    848 {
    849     if (clipboardTypeId != typeId)
    850         clipboardTypeId = 0;
    851 
    852     waitingForClipboardData = false;
    853 }
    854 
    855 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
    856 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose);
    857 #endif
    858 
    859 PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event)
    860 {
    861     Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
    862 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
    863     if (event->type != PUGL_TIMER) {
    864         printEvent(event, "pugl event: ", true);
    865     }
    866 #endif
    867 
    868     if (pData->waitingForClipboardEvents)
    869     {
    870         switch (event->type)
    871         {
    872         case PUGL_UPDATE:
    873         case PUGL_EXPOSE:
    874         case PUGL_FOCUS_IN:
    875         case PUGL_FOCUS_OUT:
    876         case PUGL_KEY_PRESS:
    877         case PUGL_KEY_RELEASE:
    878         case PUGL_TEXT:
    879         case PUGL_POINTER_IN:
    880         case PUGL_POINTER_OUT:
    881         case PUGL_BUTTON_PRESS:
    882         case PUGL_BUTTON_RELEASE:
    883         case PUGL_MOTION:
    884         case PUGL_SCROLL:
    885         case PUGL_TIMER:
    886         case PUGL_LOOP_ENTER:
    887         case PUGL_LOOP_LEAVE:
    888             return PUGL_SUCCESS;
    889         case PUGL_DATA_OFFER:
    890         case PUGL_DATA:
    891             break;
    892         default:
    893             d_stdout("Got event %d while waitingForClipboardEvents", event->type);
    894             break;
    895         }
    896     }
    897 
    898     switch (event->type)
    899     {
    900     ///< No event
    901     case PUGL_NOTHING:
    902         break;
    903 
    904     ///< View realized, a #PuglRealizeEvent
    905     case PUGL_REALIZE:
    906         if (! pData->isEmbed && ! puglGetTransientParent(view))
    907         {
    908            #if defined(DISTRHO_OS_WINDOWS) && defined(DGL_WINDOWS_ICON_ID)
    909             WNDCLASSEX wClass = {};
    910             const HINSTANCE hInstance = GetModuleHandle(nullptr);
    911 
    912             if (GetClassInfoEx(hInstance, view->world->strings[PUGL_CLASS_NAME], &wClass))
    913                 wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID));
    914 
    915             SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)));
    916            #endif
    917            #ifdef DGL_USING_X11
    918             puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
    919            #endif
    920         }
    921         break;
    922 
    923     ///< View unrealizeed, a #PuglUnrealizeEvent
    924     case PUGL_UNREALIZE:
    925         break;
    926 
    927     ///< View configured, a #PuglConfigureEvent
    928     case PUGL_CONFIGURE:
    929         // unused x, y (double)
    930         pData->onPuglConfigure(event->configure.width, event->configure.height);
    931         break;
    932 
    933     ///< View ready to draw, a #PuglUpdateEvent
    934     case PUGL_UPDATE:
    935         break;
    936 
    937     ///< View must be drawn, a #PuglExposeEvent
    938     case PUGL_EXPOSE:
    939         // unused x, y, width, height (double)
    940         pData->onPuglExpose();
    941         break;
    942 
    943     ///< View will be closed, a #PuglCloseEvent
    944     case PUGL_CLOSE:
    945         pData->onPuglClose();
    946         break;
    947 
    948     ///< Keyboard focus entered view, a #PuglFocusEvent
    949     case PUGL_FOCUS_IN:
    950     ///< Keyboard focus left view, a #PuglFocusEvent
    951     case PUGL_FOCUS_OUT:
    952         pData->onPuglFocus(event->type == PUGL_FOCUS_IN,
    953                            static_cast<CrossingMode>(event->focus.mode));
    954         break;
    955 
    956     ///< Key pressed, a #PuglKeyEvent
    957     case PUGL_KEY_PRESS:
    958     ///< Key released, a #PuglKeyEvent
    959     case PUGL_KEY_RELEASE:
    960     {
    961         // unused x, y, xRoot, yRoot (double)
    962         Widget::KeyboardEvent ev;
    963         ev.mod     = event->key.state;
    964         ev.flags   = event->key.flags;
    965         ev.time    = static_cast<uint>(event->key.time * 1000.0 + 0.5);
    966         ev.press   = event->type == PUGL_KEY_PRESS;
    967         ev.key     = event->key.key;
    968         ev.keycode = event->key.keycode;
    969 
    970         // keyboard events must always be lowercase
    971         if (ev.key >= 'A' && ev.key <= 'Z')
    972         {
    973             ev.key += 'a' - 'A'; // A-Z -> a-z
    974             ev.mod |= kModifierShift;
    975         }
    976 
    977         pData->onPuglKey(ev);
    978         break;
    979     }
    980 
    981     ///< Character entered, a #PuglTextEvent
    982     case PUGL_TEXT:
    983     {
    984         // unused x, y, xRoot, yRoot (double)
    985         Widget::CharacterInputEvent ev;
    986         ev.mod       = event->text.state;
    987         ev.flags     = event->text.flags;
    988         ev.time      = static_cast<uint>(event->text.time * 1000.0 + 0.5);
    989         ev.keycode   = event->text.keycode;
    990         ev.character = event->text.character;
    991         std::strncpy(ev.string, event->text.string, sizeof(ev.string));
    992         pData->onPuglText(ev);
    993         break;
    994     }
    995 
    996     ///< Pointer entered view, a #PuglCrossingEvent
    997     case PUGL_POINTER_IN:
    998         break;
    999     ///< Pointer left view, a #PuglCrossingEvent
   1000     case PUGL_POINTER_OUT:
   1001         break;
   1002 
   1003     ///< Mouse button pressed, a #PuglButtonEvent
   1004     case PUGL_BUTTON_PRESS:
   1005     ///< Mouse button released, a #PuglButtonEvent
   1006     case PUGL_BUTTON_RELEASE:
   1007     {
   1008         Widget::MouseEvent ev;
   1009         ev.mod    = event->button.state;
   1010         ev.flags  = event->button.flags;
   1011         ev.time   = static_cast<uint>(event->button.time * 1000.0 + 0.5);
   1012         ev.button = event->button.button + 1;
   1013         ev.press  = event->type == PUGL_BUTTON_PRESS;
   1014         if (pData->autoScaling && 0)
   1015         {
   1016             const double scaleFactor = pData->autoScaleFactor;
   1017             ev.pos = Point<double>(event->button.x / scaleFactor, event->button.y / scaleFactor);
   1018         }
   1019         else
   1020         {
   1021             ev.pos = Point<double>(event->button.x, event->button.y);
   1022         }
   1023         ev.absolutePos = ev.pos;
   1024         pData->onPuglMouse(ev);
   1025         break;
   1026     }
   1027 
   1028     ///< Pointer moved, a #PuglMotionEvent
   1029     case PUGL_MOTION:
   1030     {
   1031         Widget::MotionEvent ev;
   1032         ev.mod   = event->motion.state;
   1033         ev.flags = event->motion.flags;
   1034         ev.time  = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
   1035         if (pData->autoScaling && 0)
   1036         {
   1037             const double scaleFactor = pData->autoScaleFactor;
   1038             ev.pos = Point<double>(event->motion.x / scaleFactor, event->motion.y / scaleFactor);
   1039         }
   1040         else
   1041         {
   1042             ev.pos = Point<double>(event->motion.x, event->motion.y);
   1043         }
   1044         ev.absolutePos = ev.pos;
   1045         pData->onPuglMotion(ev);
   1046         break;
   1047     }
   1048 
   1049     ///< Scrolled, a #PuglScrollEvent
   1050     case PUGL_SCROLL:
   1051     {
   1052         Widget::ScrollEvent ev;
   1053         ev.mod       = event->scroll.state;
   1054         ev.flags     = event->scroll.flags;
   1055         ev.time      = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
   1056         if (pData->autoScaling && 0)
   1057         {
   1058             const double scaleFactor = pData->autoScaleFactor;
   1059             ev.pos   = Point<double>(event->scroll.x / scaleFactor, event->scroll.y / scaleFactor);
   1060             ev.delta = Point<double>(event->scroll.dx / scaleFactor, event->scroll.dy / scaleFactor);
   1061         }
   1062         else
   1063         {
   1064             ev.pos   = Point<double>(event->scroll.x, event->scroll.y);
   1065             ev.delta = Point<double>(event->scroll.dx, event->scroll.dy);
   1066         }
   1067         ev.direction = static_cast<ScrollDirection>(event->scroll.direction);
   1068         ev.absolutePos = ev.pos;
   1069         pData->onPuglScroll(ev);
   1070         break;
   1071     }
   1072 
   1073     ///< Custom client message, a #PuglClientEvent
   1074     case PUGL_CLIENT:
   1075         break;
   1076 
   1077     ///< Timer triggered, a #PuglTimerEvent
   1078     case PUGL_TIMER:
   1079         if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id))
   1080             idleCallback->idleCallback();
   1081         break;
   1082 
   1083     ///< Recursive loop left, a #PuglLoopLeaveEvent
   1084     case PUGL_LOOP_ENTER:
   1085         break;
   1086 
   1087     ///< Recursive loop left, a #PuglEventLoopLeave
   1088     case PUGL_LOOP_LEAVE:
   1089         break;
   1090 
   1091     ///< Data offered from clipboard, a #PuglDataOfferEvent
   1092     case PUGL_DATA_OFFER:
   1093         if (const uint32_t offerTypeId = pData->onClipboardDataOffer())
   1094             puglAcceptOffer(view, &event->offer, offerTypeId - 1);
   1095         break;
   1096 
   1097     ///< Data available from clipboard, a #PuglDataEvent
   1098     case PUGL_DATA:
   1099         pData->onClipboardData(event->data.typeIndex + 1);
   1100         break;
   1101     }
   1102 
   1103     return PUGL_SUCCESS;
   1104 }
   1105 
   1106 // -----------------------------------------------------------------------
   1107 
   1108 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
   1109 static int printModifiers(const uint32_t mods)
   1110 {
   1111 	return fprintf(stderr, "Modifiers:%s%s%s%s\n",
   1112 	               (mods & PUGL_MOD_SHIFT) ? " Shift"   : "",
   1113 	               (mods & PUGL_MOD_CTRL)  ? " Ctrl"    : "",
   1114 	               (mods & PUGL_MOD_ALT)   ? " Alt"     : "",
   1115 	               (mods & PUGL_MOD_SUPER) ? " Super" : "");
   1116 }
   1117 
   1118 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
   1119 {
   1120 #define FFMT            "%6.1f"
   1121 #define PFMT            FFMT " " FFMT
   1122 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
   1123 
   1124 	switch (event->type) {
   1125 	case PUGL_NOTHING:
   1126 		return 0;
   1127 	case PUGL_KEY_PRESS:
   1128 		return PRINT("%sKey press   code %3u key  U+%04X\n",
   1129 		             prefix,
   1130 		             event->key.keycode,
   1131 		             event->key.key);
   1132 	case PUGL_KEY_RELEASE:
   1133 		return PRINT("%sKey release code %3u key  U+%04X\n",
   1134 		             prefix,
   1135 		             event->key.keycode,
   1136 		             event->key.key);
   1137 	case PUGL_TEXT:
   1138 		return PRINT("%sText entry  code %3u char U+%04X (%s)\n",
   1139 		             prefix,
   1140 		             event->text.keycode,
   1141 		             event->text.character,
   1142 		             event->text.string);
   1143 	case PUGL_BUTTON_PRESS:
   1144 	case PUGL_BUTTON_RELEASE:
   1145 		return (PRINT("%sMouse %u %s at " PFMT " ",
   1146 		              prefix,
   1147 		              event->button.button,
   1148 		              (event->type == PUGL_BUTTON_PRESS) ? "down" : "up  ",
   1149 		              event->button.x,
   1150 		              event->button.y) +
   1151 		        printModifiers(event->scroll.state));
   1152 	case PUGL_SCROLL:
   1153 		return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
   1154 		              prefix,
   1155 		              event->scroll.dx,
   1156 		              event->scroll.dy,
   1157 		              event->scroll.x,
   1158 		              event->scroll.y) +
   1159 		        printModifiers(event->scroll.state));
   1160 	case PUGL_POINTER_IN:
   1161 		return PRINT("%sMouse enter  at " PFMT "\n",
   1162 		             prefix,
   1163 		             event->crossing.x,
   1164 		             event->crossing.y);
   1165 	case PUGL_POINTER_OUT:
   1166 		return PRINT("%sMouse leave  at " PFMT "\n",
   1167 		             prefix,
   1168 		             event->crossing.x,
   1169 		             event->crossing.y);
   1170 	case PUGL_FOCUS_IN:
   1171 		return PRINT("%sFocus in %i\n",
   1172 		             prefix,
   1173 		             event->focus.mode);
   1174 	case PUGL_FOCUS_OUT:
   1175 		return PRINT("%sFocus out %i\n",
   1176 		             prefix,
   1177 		             event->focus.mode);
   1178 	case PUGL_CLIENT:
   1179 		return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
   1180 		             prefix,
   1181 		             event->client.data1,
   1182 		             event->client.data2);
   1183 	case PUGL_TIMER:
   1184 		return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
   1185 	default:
   1186 		break;
   1187 	}
   1188 
   1189 	if (verbose) {
   1190 		switch (event->type) {
   1191 		case PUGL_CREATE:
   1192 			return fprintf(stderr, "%sCreate\n", prefix);
   1193 		case PUGL_DESTROY:
   1194 			return fprintf(stderr, "%sDestroy\n", prefix);
   1195 		case PUGL_MAP:
   1196 			return fprintf(stderr, "%sMap\n", prefix);
   1197 		case PUGL_UNMAP:
   1198 			return fprintf(stderr, "%sUnmap\n", prefix);
   1199 		case PUGL_UPDATE:
   1200 			return 0; // fprintf(stderr, "%sUpdate\n", prefix);
   1201 		case PUGL_CONFIGURE:
   1202 			return PRINT("%sConfigure " PFMT " " PFMT "\n",
   1203 			             prefix,
   1204 			             event->configure.x,
   1205 			             event->configure.y,
   1206 			             event->configure.width,
   1207 			             event->configure.height);
   1208 		case PUGL_EXPOSE:
   1209 			return PRINT("%sExpose    " PFMT " " PFMT "\n",
   1210 			             prefix,
   1211 			             event->expose.x,
   1212 			             event->expose.y,
   1213 			             event->expose.width,
   1214 			             event->expose.height);
   1215 		case PUGL_CLOSE:
   1216 			return PRINT("%sClose\n", prefix);
   1217 		case PUGL_MOTION:
   1218 			return PRINT("%sMouse motion at " PFMT "\n",
   1219 			             prefix,
   1220 			             event->motion.x,
   1221 			             event->motion.y);
   1222 		default:
   1223 			return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
   1224 		}
   1225 	}
   1226 
   1227 #undef PRINT
   1228 #undef PFMT
   1229 #undef FFMT
   1230 
   1231 	return 0;
   1232 }
   1233 #endif
   1234 
   1235 #undef DGL_DBG
   1236 #undef DGL_DBGF
   1237 
   1238 // -----------------------------------------------------------------------
   1239 
   1240 END_NAMESPACE_DGL