DPF

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

commit fc10a2153af8933d13f6421cee90d8769e86ef0e
parent e1aee0ec9f4693d1ad9e45a141d12bad81633c71
Author: falkTX <falktx@falktx.com>
Date:   Mon, 20 Apr 2020 00:14:23 +0100

Start split of Window.cpp into several smaller files

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdgl/Makefile | 33+++++++++++++++++++--------------
Mdgl/Window.hpp | 2+-
Adgl/src/StandaloneWindow.cpp | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdgl/src/Window.cpp | 1594+------------------------------------------------------------------------------
Adgl/src/WindowFileBrowser.cpp | 223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/WindowPrivateData.cpp | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adgl/src/WindowPrivateData.hpp | 1351+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1756 insertions(+), 1594 deletions(-)

diff --git a/dgl/Makefile b/dgl/Makefile @@ -27,8 +27,11 @@ OBJS_common = \ ../build/dgl/Color.cpp.o \ ../build/dgl/Geometry.cpp.o \ ../build/dgl/ImageBase.cpp.o \ - ../build/dgl/Resources.cpp.o \ - ../build/dgl/Widget.cpp.o + ../build/dgl/Resources.cpp.o\ + ../build/dgl/StandaloneWindow.cpp.o \ + ../build/dgl/Widget.cpp.o \ + ../build/dgl/Window.cpp.o\ + ../build/dgl/WindowFileBrowser.cpp.o # TODO: ImageWidgets.cpp @@ -36,13 +39,14 @@ OBJS_common = \ OBJS_cairo = $(OBJS_common) \ ../build/dgl/Cairo.cpp.cairo.o \ - ../build/dgl/WidgetPrivateData.cpp.cairo.o + ../build/dgl/WidgetPrivateData.cpp.cairo.o \ + ../build/dgl/WindowPrivateData.cpp.cairo.o -ifeq ($(MACOS),true) -OBJS_cairo += ../build/dgl/Window.mm.cairo.o -else -OBJS_cairo += ../build/dgl/Window.cpp.cairo.o -endif +# ifeq ($(MACOS),true) +# OBJS_cairo += ../build/dgl/Window.mm.cairo.o +# else +# OBJS_cairo += ../build/dgl/Window.cpp.cairo.o +# endif # --------------------------------------------------------------------------------------------------------------------- @@ -51,13 +55,14 @@ OBJS_opengl = $(OBJS_common) \ ../build/dgl/Image.cpp.opengl.o \ ../build/dgl/ImageWidgets.cpp.opengl.o \ ../build/dgl/NanoVG.cpp.opengl.o \ - ../build/dgl/WidgetPrivateData.cpp.opengl.o + ../build/dgl/WidgetPrivateData.cpp.opengl.o \ + ../build/dgl/WindowPrivateData.cpp.opengl.o -ifeq ($(MACOS),true) -OBJS_opengl += ../build/dgl/Window.mm.opengl.o -else -OBJS_opengl += ../build/dgl/Window.cpp.opengl.o -endif +# ifeq ($(MACOS),true) +# OBJS_opengl += ../build/dgl/Window.mm.opengl.o +# else +# OBJS_opengl += ../build/dgl/Window.cpp.opengl.o +# endif # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/Window.hpp b/dgl/Window.hpp @@ -118,7 +118,7 @@ public: void setIgnoringKeyRepeat(bool ignore) noexcept; Application& getApp() const noexcept; - intptr_t getWindowId() const noexcept; + uintptr_t getWindowId() const noexcept; const GraphicsContext& getGraphicsContext() const noexcept; diff --git a/dgl/src/StandaloneWindow.cpp b/dgl/src/StandaloneWindow.cpp @@ -0,0 +1,64 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../StandaloneWindow.hpp" +#include "WidgetPrivateData.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +StandaloneWindow::StandaloneWindow() + : Application(true), + Window((Application&)*this), + fWidget(nullptr) {} + +void StandaloneWindow::exec() +{ + Window::show(); + Application::exec(); +} + +void StandaloneWindow::onReshape(uint width, uint height) +{ + if (fWidget != nullptr) + fWidget->setSize(width, height); + Window::onReshape(width, height); +} + +void StandaloneWindow::_addWidget(Widget* widget) +{ + if (fWidget == nullptr) + { + fWidget = widget; + fWidget->pData->needsFullViewport = true; + } + Window::_addWidget(widget); +} + +void StandaloneWindow::_removeWidget(Widget* widget) +{ + if (fWidget == widget) + { + fWidget->pData->needsFullViewport = false; + fWidget = nullptr; + } + Window::_removeWidget(widget); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp @@ -1,8 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> - * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru> - * Copyright (C) 2019 Robin Gareus <robin@gareus.org> + * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -19,1330 +17,14 @@ // we need this for now //#define PUGL_GRAB_FOCUS 1 -#include "../Base.hpp" +// #include "../Base.hpp" +#include "WindowPrivateData.hpp" -#ifdef DGL_CAIRO -# define PUGL_CAIRO -# include "../Cairo.hpp" -#endif -#ifdef DGL_OPENGL -# define PUGL_OPENGL -# include "../OpenGL.hpp" -#endif - -#include "pugl/pugl.h" - -#if defined(__GNUC__) && (__GNUC__ >= 7) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#endif - -#if defined(DISTRHO_OS_HAIKU) -# define DGL_DEBUG_EVENTS -# include "pugl/pugl_haiku.cpp" -#elif defined(DISTRHO_OS_MAC) -# define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) -# define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) -# include "pugl/pugl_osx.m" -#elif defined(DISTRHO_OS_WINDOWS) -# include "pugl/pugl_win.cpp" -# undef max -# undef min -#else -# include <sys/types.h> -# include <unistd.h> -extern "C" { -# include "pugl/pugl_x11.c" -} -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 7) -# pragma GCC diagnostic pop -#endif - -#include "ApplicationPrivateData.hpp" -#include "WidgetPrivateData.hpp" -#include "../StandaloneWindow.hpp" -#include "../../distrho/extra/String.hpp" - -#define FOR_EACH_WIDGET(it) \ - for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) - -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) - -#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) -# define DBG(msg) std::fprintf(stderr, "%s", msg); -# define DBGp(...) std::fprintf(stderr, __VA_ARGS__); -# define DBGF std::fflush(stderr); -#else -# define DBG(msg) -# define DBGp(...) -# define DBGF -#endif +// #include "../../distrho/extra/String.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -// Window Private - -struct Window::PrivateData { - PrivateData(Application& app, Window* const self) - : fApp(app), - fSelf(self), - fView(puglInit()), - fFirstInit(true), - fVisible(false), - fResizable(true), - fUsingEmbed(false), - fWidth(1), - fHeight(1), - fScaling(1.0), - fAutoScaling(1.0), - fTitle(nullptr), - fWidgets(), - fModal(), -#if defined(DISTRHO_OS_HAIKU) - bApplication(nullptr), - bView(nullptr), - bWindow(nullptr) -#elif defined(DISTRHO_OS_MAC) - fNeedsIdle(true), - mView(nullptr), - mWindow(nullptr), - mParentWindow(nullptr) -# ifndef DGL_FILE_BROWSER_DISABLED - , fOpenFilePanel(nullptr), - fFilePanelDelegate(nullptr) -# endif -#elif defined(DISTRHO_OS_WINDOWS) - hwnd(nullptr), - hwndParent(nullptr) -#else - xDisplay(nullptr), - xWindow(0) -#endif - { - DBG("Creating window without parent..."); DBGF; - init(); - } - - PrivateData(Application& app, Window* const self, Window& parent) - : fApp(app), - fSelf(self), - fView(puglInit()), - fFirstInit(true), - fVisible(false), - fResizable(true), - fUsingEmbed(false), - fWidth(1), - fHeight(1), - fScaling(1.0), - fAutoScaling(1.0), - fTitle(nullptr), - fWidgets(), - fModal(parent.pData), -#if defined(DISTRHO_OS_HAIKU) - bApplication(nullptr), - bView(nullptr), - bWindow(nullptr) -#elif defined(DISTRHO_OS_MAC) - fNeedsIdle(false), - mView(nullptr), - mWindow(nullptr), - mParentWindow(nullptr) -# ifndef DGL_FILE_BROWSER_DISABLED - , fOpenFilePanel(nullptr), - fFilePanelDelegate(nullptr) -# endif -#elif defined(DISTRHO_OS_WINDOWS) - hwnd(nullptr), - hwndParent(nullptr) -#else - xDisplay(nullptr), - xWindow(0) -#endif - { - DBG("Creating window with parent..."); DBGF; - init(); - - const PuglInternals* const parentImpl(parent.pData->fView->impl); - - // NOTE: almost a 1:1 copy of setTransientWinId() -#if defined(DISTRHO_OS_HAIKU) - // TODO -#elif defined(DISTRHO_OS_MAC) - mParentWindow = parentImpl->window; -#elif defined(DISTRHO_OS_WINDOWS) - hwndParent = parentImpl->hwnd; - SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent); -#else - XSetTransientForHint(xDisplay, xWindow, parentImpl->win); -#endif - } - - PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable) - : fApp(app), - fSelf(self), - fView(puglInit()), - fFirstInit(true), - fVisible(parentId != 0), - fResizable(resizable), - fUsingEmbed(parentId != 0), - fWidth(1), - fHeight(1), - fScaling(scaling), - fAutoScaling(1.0), - fTitle(nullptr), - fWidgets(), - fModal(), -#if defined(DISTRHO_OS_HAIKU) - bApplication(nullptr), - bView(nullptr), - bWindow(nullptr) -#elif defined(DISTRHO_OS_MAC) - fNeedsIdle(parentId == 0), - mView(nullptr), - mWindow(nullptr), - mParentWindow(nullptr) -# ifndef DGL_FILE_BROWSER_DISABLED - , fOpenFilePanel(nullptr), - fFilePanelDelegate(nullptr) -# endif -#elif defined(DISTRHO_OS_WINDOWS) - hwnd(nullptr), - hwndParent(nullptr) -#else - xDisplay(nullptr), - xWindow(0) -#endif - { - if (fUsingEmbed) - { - DBG("Creating embedded window..."); DBGF; - puglInitWindowParent(fView, parentId); - } - else - { - DBG("Creating window without parent..."); DBGF; - } - - init(); - - if (fUsingEmbed) - { - DBG("NOTE: Embed window is always visible and non-resizable\n"); - puglShowWindow(fView); - fApp.pData->oneShown(); - fFirstInit = false; - } - } - - void init() - { - if (fSelf == nullptr || fView == nullptr) - { - DBG("Failed!\n"); - return; - } - - puglInitUserResizable(fView, fResizable); - puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); - - puglSetHandle(fView, this); - puglSetDisplayFunc(fView, onDisplayCallback); - puglSetKeyboardFunc(fView, onKeyboardCallback); - puglSetMotionFunc(fView, onMotionCallback); - puglSetMouseFunc(fView, onMouseCallback); - puglSetScrollFunc(fView, onScrollCallback); - puglSetSpecialFunc(fView, onSpecialCallback); - puglSetReshapeFunc(fView, onReshapeCallback); - puglSetCloseFunc(fView, onCloseCallback); -#ifndef DGL_FILE_BROWSER_DISABLED - puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); -#endif - - puglCreateWindow(fView, nullptr); - - PuglInternals* impl = fView->impl; - -#if defined(DISTRHO_OS_HAIKU) - bApplication = impl->app; - bView = impl->view; - bWindow = impl->window; -#elif defined(DISTRHO_OS_MAC) - mView = impl->view; - mWindow = impl->window; - DISTRHO_SAFE_ASSERT(mView != nullptr); - if (fUsingEmbed) { - DISTRHO_SAFE_ASSERT(mWindow == nullptr); - } else { - DISTRHO_SAFE_ASSERT(mWindow != nullptr); - } -#elif defined(DISTRHO_OS_WINDOWS) - hwnd = impl->hwnd; - DISTRHO_SAFE_ASSERT(hwnd != 0); -#else - xDisplay = impl->display; - xWindow = impl->win; - DISTRHO_SAFE_ASSERT(xWindow != 0); - - if (! fUsingEmbed) - { - const pid_t pid = getpid(); - const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False); - XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); - - const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False); - - // Setting the window to both dialog and normal will produce a decorated floating dialog - // Order is important: DIALOG needs to come before NORMAL - const Atom _wts[2] = { - XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False), - XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False) - }; - XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); - } -#endif - puglEnterContext(fView); - - fApp.pData->windows.push_back(fSelf); - - DBG("Success!\n"); - } - - ~PrivateData() - { - DBG("Destroying window..."); DBGF; - - if (fModal.enabled) - { - exec_fini(); - close(); - } - - fWidgets.clear(); - - if (fUsingEmbed) - { - puglHideWindow(fView); - fApp.pData->oneHidden(); - } - - if (fSelf != nullptr) - { - fApp.pData->windows.remove(fSelf); - fSelf = nullptr; - } - - if (fView != nullptr) - { - puglDestroy(fView); - fView = nullptr; - } - - if (fTitle != nullptr) - { - std::free(fTitle); - fTitle = nullptr; - } - -#if defined(DISTRHO_OS_HAIKU) - bApplication = nullptr; - bView = nullptr; - bWindow = nullptr; -#elif defined(DISTRHO_OS_MAC) - mView = nullptr; - mWindow = nullptr; -#elif defined(DISTRHO_OS_WINDOWS) - hwnd = 0; -#else - xDisplay = nullptr; - xWindow = 0; -#endif - -#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) - if (fOpenFilePanel) - { - [fOpenFilePanel release]; - fOpenFilePanel = nullptr; - } - if (fFilePanelDelegate) - { - [fFilePanelDelegate release]; - fFilePanelDelegate = nullptr; - } -#endif - - DBG("Success!\n"); - } - - // ------------------------------------------------------------------- - - void close() - { - DBG("Window close\n"); - - if (fUsingEmbed) - return; - - setVisible(false); - - if (! fFirstInit) - { - fApp.pData->oneHidden(); - fFirstInit = true; - } - } - - void exec(const bool lockWait) - { - DBG("Window exec\n"); - exec_init(); - - if (lockWait) - { - for (; fVisible && fModal.enabled;) - { - idle(); - d_msleep(10); - } - - exec_fini(); - } - else - { - idle(); - } - } - - // ------------------------------------------------------------------- - - void exec_init() - { - DBG("Window modal loop starting..."); DBGF; - DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true)); - - fModal.enabled = true; - fModal.parent->fModal.childFocus = this; - - fModal.parent->setVisible(true); - setVisible(true); - - DBG("Ok\n"); - } - - void exec_fini() - { - DBG("Window modal loop stopping..."); DBGF; - fModal.enabled = false; - - if (fModal.parent != nullptr) - { - fModal.parent->fModal.childFocus = nullptr; - - // the mouse position probably changed since the modal appeared, - // so send a mouse motion event to the modal's parent window -#if defined(DISTRHO_OS_HAIKU) - // TODO -#elif defined(DISTRHO_OS_MAC) - // TODO -#elif defined(DISTRHO_OS_WINDOWS) - // TODO -#else - int i, wx, wy; - uint u; - ::Window w; - if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True) - fModal.parent->onPuglMotion(wx, wy); -#endif - } - - DBG("Ok\n"); - } - - // ------------------------------------------------------------------- - - void focus() - { - DBG("Window focus\n"); -#if defined(DISTRHO_OS_HAIKU) - if (bWindow != nullptr) - { - if (bWindow->LockLooper()) - { - bWindow->Activate(true); - bWindow->UnlockLooper(); - } - } - else - { - bView->MakeFocus(true); - } -#elif defined(DISTRHO_OS_MAC) - if (mWindow != nullptr) - [mWindow makeKeyWindow]; -#elif defined(DISTRHO_OS_WINDOWS) - SetForegroundWindow(hwnd); - SetActiveWindow(hwnd); - SetFocus(hwnd); -#else - XRaiseWindow(xDisplay, xWindow); - XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime); - XFlush(xDisplay); -#endif - } - - // ------------------------------------------------------------------- - - void setVisible(const bool yesNo) - { - if (fVisible == yesNo) - { - DBG("Window setVisible matches current state, ignoring request\n"); - return; - } - if (fUsingEmbed) - { - DBG("Window setVisible cannot be called when embedded\n"); - return; - } - - DBG("Window setVisible called\n"); - - fVisible = yesNo; - - if (yesNo && fFirstInit) - setSize(fWidth, fHeight, true); - -#if defined(DISTRHO_OS_HAIKU) - if (bWindow != nullptr) - { - if (bWindow->LockLooper()) - { - if (yesNo) - bWindow->Show(); - else - bWindow->Hide(); - - // TODO use flush? - bWindow->Sync(); - bWindow->UnlockLooper(); - } - } - else - { - if (yesNo) - bView->Show(); - else - bView->Hide(); - } -#elif defined(DISTRHO_OS_MAC) - if (yesNo) - { - if (mWindow != nullptr) - { - if (mParentWindow != nullptr) - [mParentWindow addChildWindow:mWindow - ordered:NSWindowAbove]; - - [mWindow setIsVisible:YES]; - } - else - { - [mView setHidden:NO]; - } - } - else - { - if (mWindow != nullptr) - { - if (mParentWindow != nullptr) - [mParentWindow removeChildWindow:mWindow]; - - [mWindow setIsVisible:NO]; - } - else - { - [mView setHidden:YES]; - } - } -#elif defined(DISTRHO_OS_WINDOWS) - if (yesNo) - { - if (fFirstInit) - { - RECT rectChild, rectParent; - - if (hwndParent != nullptr && - GetWindowRect(hwnd, &rectChild) && - GetWindowRect(hwndParent, &rectParent)) - { - SetWindowPos(hwnd, hwndParent, - rectParent.left + (rectChild.right-rectChild.left)/2, - rectParent.top + (rectChild.bottom-rectChild.top)/2, - 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); - } - else - { - ShowWindow(hwnd, SW_SHOWNORMAL); - } - } - else - { - ShowWindow(hwnd, SW_RESTORE); - } - } - else - { - ShowWindow(hwnd, SW_HIDE); - } - - UpdateWindow(hwnd); -#else - if (yesNo) - XMapRaised(xDisplay, xWindow); - else - XUnmapWindow(xDisplay, xWindow); - - XFlush(xDisplay); -#endif - - if (yesNo) - { - if (fFirstInit) - { - fApp.pData->oneShown(); - fFirstInit = false; - } - } - else if (fModal.enabled) - exec_fini(); - } - - // ------------------------------------------------------------------- - - void setResizable(const bool yesNo) - { - if (fResizable == yesNo) - { - DBG("Window setResizable matches current state, ignoring request\n"); - return; - } - if (fUsingEmbed) - { - DBG("Window setResizable cannot be called when embedded\n"); - return; - } - - DBG("Window setResizable called\n"); - - fResizable = yesNo; - fView->user_resizable = yesNo; - -#if defined(DISTRHO_OS_HAIKU) - // TODO - // B_NO_BORDER - // B_TITLED_WINDOW_LOOK - // bWindow->SetFlags(); -#elif defined(DISTRHO_OS_MAC) - const uint flags = yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0; - [mView setAutoresizingMask:flags]; -#elif defined(DISTRHO_OS_WINDOWS) - const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX - : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; - SetWindowLong(hwnd, GWL_STYLE, winFlags); -#endif - - setSize(fWidth, fHeight, true); - } - - // ------------------------------------------------------------------- - - void setGeometryConstraints(uint width, uint height, bool aspect) - { - // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? - DISTRHO_SAFE_ASSERT_RETURN(fResizable,); - - fView->min_width = width; - fView->min_height = height; - puglUpdateGeometryConstraints(fView, width, height, aspect); - } - - // ------------------------------------------------------------------- - - void setSize(uint width, uint height, const bool forced = false) - { - if (width <= 1 || height <= 1) - { - DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height); - return; - } - - if (fWidth == width && fHeight == height && ! forced) - { - DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height); - return; - } - - fWidth = width; - fHeight = height; - - DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false"); - -#if defined(DISTRHO_OS_HAIKU) - bView->ResizeTo(width, height); - - if (bWindow != nullptr && bWindow->LockLooper()) - { - bWindow->MoveTo(50, 50); - bWindow->ResizeTo(width, height); - - if (! forced) - bWindow->Flush(); - - bWindow->UnlockLooper(); - } - // TODO resizable -#elif defined(DISTRHO_OS_MAC) - [mView setFrame:NSMakeRect(0, 0, width, height)]; - - if (mWindow != nullptr) - { - const NSSize size = NSMakeSize(width, height); - [mWindow setContentSize:size]; - - if (fResizable) - { - [mWindow setContentMinSize:NSMakeSize(1, 1)]; - [mWindow setContentMaxSize:NSMakeSize(99999, 99999)]; - [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO]; - } - else - { - [mWindow setContentMinSize:size]; - [mWindow setContentMaxSize:size]; - [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES]; - } - } -#elif defined(DISTRHO_OS_WINDOWS) - const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); - RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) }; - AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); - - SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, - SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); - - if (! forced) - UpdateWindow(hwnd); -#else - - if (! fResizable) - { - XSizeHints sizeHints; - memset(&sizeHints, 0, sizeof(sizeHints)); - - sizeHints.flags = PSize|PMinSize|PMaxSize; - sizeHints.width = static_cast<int>(width); - sizeHints.height = static_cast<int>(height); - sizeHints.min_width = static_cast<int>(width); - sizeHints.min_height = static_cast<int>(height); - sizeHints.max_width = static_cast<int>(width); - sizeHints.max_height = static_cast<int>(height); - - XSetWMNormalHints(xDisplay, xWindow, &sizeHints); - } - - XResizeWindow(xDisplay, xWindow, width, height); - - if (! forced) - XFlush(xDisplay); -#endif - - puglPostRedisplay(fView); - } - - // ------------------------------------------------------------------- - - const char* getTitle() const noexcept - { - static const char* const kFallback = ""; - - return fTitle != nullptr ? fTitle : kFallback; - } - - void setTitle(const char* const title) - { - DBGp("Window setTitle \"%s\"\n", title); - - if (fTitle != nullptr) - std::free(fTitle); - - fTitle = strdup(title); - -#if defined(DISTRHO_OS_HAIKU) - if (bWindow != nullptr&& bWindow->LockLooper()) - { - bWindow->SetTitle(title); - bWindow->UnlockLooper(); - } -#elif defined(DISTRHO_OS_MAC) - if (mWindow != nullptr) - { - NSString* titleString = [[NSString alloc] - initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; - - [mWindow setTitle:titleString]; - } -#elif defined(DISTRHO_OS_WINDOWS) - SetWindowTextA(hwnd, title); -#else - XStoreName(xDisplay, xWindow, title); - Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False); - Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False); - XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); -#endif - } - - void setTransientWinId(const uintptr_t winId) - { - DISTRHO_SAFE_ASSERT_RETURN(winId != 0,); - -#if defined(DISTRHO_OS_HAIKU) - // TODO -#elif defined(DISTRHO_OS_MAC) - NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId]; - DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,); - - [parentWindow addChildWindow:mWindow - ordered:NSWindowAbove]; -#elif defined(DISTRHO_OS_WINDOWS) - hwndParent = (HWND)winId; - SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId); -#else - XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId)); -#endif - } - - // ------------------------------------------------------------------- - - double getScaling() const noexcept - { - return fScaling; - } - - void setAutoScaling(const double scaling) noexcept - { - DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); - - fAutoScaling = scaling; - } - - // ------------------------------------------------------------------- - - bool getIgnoringKeyRepeat() const noexcept - { - return fView->ignoreKeyRepeat; - } - - void setIgnoringKeyRepeat(bool ignore) noexcept - { - puglIgnoreKeyRepeat(fView, ignore); - } - - // ------------------------------------------------------------------- - - void addWidget(Widget* const widget) - { - fWidgets.push_back(widget); - } - - void removeWidget(Widget* const widget) - { - fWidgets.remove(widget); - } - - void idle() - { - puglProcessEvents(fView); - -#ifdef DISTRHO_OS_HAIKU - if (bApplication != nullptr) - { - // bApplication->Lock(); - // bApplication->Loop(); - // bApplication->Unlock(); - } -#endif - -#ifdef DISTRHO_OS_MAC - if (fNeedsIdle) - { - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSEvent* event; - - for (;;) - { - event = [NSApp - nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - - if (event == nil) - break; - - [NSApp sendEvent: event]; - } - - [pool release]; - } -#endif - -#if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) - if (fSelectedFile.isNotEmpty()) - { - char* const buffer = fSelectedFile.getAndReleaseBuffer(); - fView->fileSelectedFunc(fView, buffer); - std::free(buffer); - } -#endif - - if (fModal.enabled && fModal.parent != nullptr) - fModal.parent->idle(); - } - - // ------------------------------------------------------------------- - - void onPuglDisplay() - { - fSelf->onDisplayBefore(); - - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - widget->pData->display(fWidth, fHeight, fAutoScaling, false); - } - - fSelf->onDisplayAfter(); - } - - int onPuglKeyboard(const bool press, const uint key) - { - DBGp("PUGL: onKeyboard : %i %i\n", press, key); - - if (fModal.childFocus != nullptr) - { - fModal.childFocus->focus(); - return 0; - } - - Widget::KeyboardEvent ev; - ev.press = press; - ev.key = key; - ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - if (widget->isVisible() && widget->onKeyboard(ev)) - return 0; - } - - return 1; - } - - int onPuglSpecial(const bool press, const Key key) - { - DBGp("PUGL: onSpecial : %i %i\n", press, key); - - if (fModal.childFocus != nullptr) - { - fModal.childFocus->focus(); - return 0; - } - - Widget::SpecialEvent ev; - ev.press = press; - ev.key = key; - ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - if (widget->isVisible() && widget->onSpecial(ev)) - return 0; - } - - return 1; - } - - void onPuglMouse(const int button, const bool press, int x, int y) - { - DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y); - - // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it - if (press && button == 0 && x == 0 && y == 0) return; - - if (fModal.childFocus != nullptr) - return fModal.childFocus->focus(); - - x /= fAutoScaling; - y /= fAutoScaling; - - Widget::MouseEvent ev; - ev.button = button; - ev.press = press; - ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onMouse(ev)) - break; - } - } - - void onPuglMotion(int x, int y) - { - // DBGp("PUGL: onMotion : %i %i\n", x, y); - - if (fModal.childFocus != nullptr) - return; - - x /= fAutoScaling; - y /= fAutoScaling; - - Widget::MotionEvent ev; - ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onMotion(ev)) - break; - } - } - - void onPuglScroll(int x, int y, float dx, float dy) - { - DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy); - - if (fModal.childFocus != nullptr) - return; - - x /= fAutoScaling; - y /= fAutoScaling; - dx /= fAutoScaling; - dy /= fAutoScaling; - - Widget::ScrollEvent ev; - ev.delta = Point<float>(dx, dy); - ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onScroll(ev)) - break; - } - } - - void onPuglReshape(const int width, const int height) - { - DBGp("PUGL: onReshape : %i %i\n", width, height); - - if (width <= 1 && height <= 1) - return; - - fWidth = static_cast<uint>(width); - fHeight = static_cast<uint>(height); - - fSelf->onReshape(fWidth, fHeight); - - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - - if (widget->pData->needsFullViewport) - widget->setSize(fWidth, fHeight); - } - } - - void onPuglClose() - { - DBG("PUGL: onClose\n"); - - if (fModal.enabled) - exec_fini(); - - fSelf->onClose(); - - if (fModal.childFocus != nullptr) - fModal.childFocus->fSelf->onClose(); - - close(); - } - - // ------------------------------------------------------------------- - - bool handlePluginKeyboard(const bool press, const uint key) - { - DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key); - - if (fModal.childFocus != nullptr) - { - fModal.childFocus->focus(); - return true; - } - - Widget::KeyboardEvent ev; - ev.press = press; - ev.key = key; - ev.mod = static_cast<Modifier>(fView->mods); - ev.time = 0; - - if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z') - ev.key -= 'a' - 'A'; // a-z -> A-Z - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - if (widget->isVisible() && widget->onKeyboard(ev)) - return true; - } - - return false; - } - - bool handlePluginSpecial(const bool press, const Key key) - { - DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); - - if (fModal.childFocus != nullptr) - { - fModal.childFocus->focus(); - return true; - } - - int mods = 0x0; - - switch (key) - { - case kKeyShift: - mods |= kModifierShift; - break; - case kKeyControl: - mods |= kModifierControl; - break; - case kKeyAlt: - mods |= kModifierAlt; - break; - default: - break; - } - - if (mods != 0x0) - { - if (press) - fView->mods |= mods; - else - fView->mods &= ~(mods); - } - - Widget::SpecialEvent ev; - ev.press = press; - ev.key = key; - ev.mod = static_cast<Modifier>(fView->mods); - ev.time = 0; - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - if (widget->isVisible() && widget->onSpecial(ev)) - return true; - } - - return false; - } - -#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) - static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) - { - PrivateData* pData = (PrivateData*)userData; - - if (returnCode == NSOKButton) - { - NSArray* urls = [panel URLs]; - NSURL* fileUrl = nullptr; - - for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) - { - NSURL* url = (NSURL*)[urls objectAtIndex:i]; - if ([url isFileURL]) - fileUrl = url; - } - - if (fileUrl) - { - PuglView* view = pData->fView; - if (view->fileSelectedFunc) - { - const char* fileName = [fileUrl.path UTF8String]; - view->fileSelectedFunc(view, fileName); - } - } - } - - [pData->fOpenFilePanel release]; - pData->fOpenFilePanel = nullptr; - } -#endif - - // ------------------------------------------------------------------- - - Application& fApp; - Window* fSelf; - GraphicsContext fContext; - PuglView* fView; - - bool fFirstInit; - bool fVisible; - bool fResizable; - bool fUsingEmbed; - uint fWidth; - uint fHeight; - double fScaling; - double fAutoScaling; - char* fTitle; - std::list<Widget*> fWidgets; - - struct Modal { - bool enabled; - PrivateData* parent; - PrivateData* childFocus; - - Modal() - : enabled(false), - parent(nullptr), - childFocus(nullptr) {} - - Modal(PrivateData* const p) - : enabled(false), - parent(p), - childFocus(nullptr) {} - - ~Modal() - { - DISTRHO_SAFE_ASSERT(! enabled); - DISTRHO_SAFE_ASSERT(childFocus == nullptr); - } - - DISTRHO_DECLARE_NON_COPY_STRUCT(Modal) - } fModal; - -#if defined(DISTRHO_OS_HAIKU) - BApplication* bApplication; - BView* bView; - BWindow* bWindow; -#elif defined(DISTRHO_OS_MAC) - bool fNeedsIdle; - NSView<PuglGenericView>* mView; - id mWindow; - id mParentWindow; -# ifndef DGL_FILE_BROWSER_DISABLED - NSOpenPanel* fOpenFilePanel; - id fFilePanelDelegate; -# endif -#elif defined(DISTRHO_OS_WINDOWS) - HWND hwnd; - HWND hwndParent; -# ifndef DGL_FILE_BROWSER_DISABLED - String fSelectedFile; -# endif -#else - Display* xDisplay; - ::Window xWindow; -#endif - - // ------------------------------------------------------------------- - // Callbacks - - #define handlePtr ((PrivateData*)puglGetHandle(view)) - - static void onDisplayCallback(PuglView* view) - { - handlePtr->onPuglDisplay(); - } - - static int onKeyboardCallback(PuglView* view, bool press, uint32_t key) - { - return handlePtr->onPuglKeyboard(press, key); - } - - static int onSpecialCallback(PuglView* view, bool press, PuglKey key) - { - return handlePtr->onPuglSpecial(press, static_cast<Key>(key)); - } - - static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) - { - handlePtr->onPuglMouse(button, press, x, y); - } - - static void onMotionCallback(PuglView* view, int x, int y) - { - handlePtr->onPuglMotion(x, y); - } - - static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy) - { - handlePtr->onPuglScroll(x, y, dx, dy); - } - - static void onReshapeCallback(PuglView* view, int width, int height) - { - handlePtr->onPuglReshape(width, height); - } - - static void onCloseCallback(PuglView* view) - { - handlePtr->onPuglClose(); - } - -#ifndef DGL_FILE_BROWSER_DISABLED - static void fileBrowserSelectedCallback(PuglView* view, const char* filename) - { - handlePtr->fSelf->fileBrowserSelected(filename); - } -#endif - - #undef handlePtr - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) -}; - -// ----------------------------------------------------------------------- // Window Window::Window(Application& app) @@ -1359,6 +41,7 @@ Window::~Window() delete pData; } +#if 0 void Window::show() { pData->setVisible(true); @@ -1389,207 +72,6 @@ void Window::repaint() noexcept puglPostRedisplay(pData->fView); } -// static int fib_filter_filename_filter(const char* const name) -// { -// return 1; -// (void)name; -// } - -#ifndef DGL_FILE_BROWSER_DISABLED - -#ifdef DISTRHO_OS_MAC -END_NAMESPACE_DGL -@interface FilePanelDelegate : NSObject -{ - void (*fCallback)(NSOpenPanel*, int, void*); - void* fUserData; -} --(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData; --(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; -@end -START_NAMESPACE_DGL -#endif - -bool Window::openFileBrowser(const FileBrowserOptions& options) -{ -# ifdef SOFD_HAVE_X11 - using DISTRHO_NAMESPACE::String; - - // -------------------------------------------------------------------------- - // configure start dir - - // TODO: get abspath if needed - // TODO: cross-platform - - String startDir(options.startDir); - -# ifdef DISTRHO_OS_LINUX - if (startDir.isEmpty()) - { - if (char* const dir_name = get_current_dir_name()) - { - startDir = dir_name; - std::free(dir_name); - } - } -# endif - - DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); - - if (! startDir.endsWith('/')) - startDir += "/"; - - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); - - // -------------------------------------------------------------------------- - // configure title - - String title(options.title); - - if (title.isEmpty()) - { - title = pData->getTitle(); - - if (title.isEmpty()) - title = "FileBrowser"; - } - - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); - - // -------------------------------------------------------------------------- - // configure filters - - x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter); - - // -------------------------------------------------------------------------- - // configure buttons - - x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); - x_fib_cfg_buttons(1, options.buttons.showHidden-1); - x_fib_cfg_buttons(2, options.buttons.showPlaces-1); - - // -------------------------------------------------------------------------- - // show - - return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); -# elif defined(DISTRHO_OS_WINDOWS) - // the old and compatible dialog API - OPENFILENAMEW ofn; - memset(&ofn, 0, sizeof(ofn)); - - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = pData->hwnd; - - // set initial directory in UTF-16 coding - std::vector<WCHAR> startDirW; - if (options.startDir) - { - startDirW.resize(strlen(options.startDir) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size())) - ofn.lpstrInitialDir = startDirW.data(); - } - - // set title in UTF-16 coding - std::vector<WCHAR> titleW; - if (options.title) - { - titleW.resize(strlen(options.title) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size())) - ofn.lpstrTitle = titleW.data(); - } - - // prepare a buffer to receive the result - std::vector<WCHAR> fileNameW(32768); // the Unicode maximum - ofn.lpstrFile = fileNameW.data(); - ofn.nMaxFile = (DWORD)fileNameW.size(); - - // TODO synchronous only, can't do better with WinAPI native dialogs. - // threading might work, if someone is motivated to risk it. - if (GetOpenFileNameW(&ofn)) - { - // back to UTF-8 - std::vector<char> fileNameA(4 * 32768); - if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr)) - { - // handle it during the next idle cycle (fake async) - pData->fSelectedFile = fileNameA.data(); - } - } - - return true; -# elif defined(DISTRHO_OS_MAC) - if (pData->fOpenFilePanel) // permit one dialog at most - { - [pData->fOpenFilePanel makeKeyAndOrderFront:nil]; - return false; - } - - NSOpenPanel* panel = [NSOpenPanel openPanel]; - pData->fOpenFilePanel = [panel retain]; - - [panel setCanChooseFiles:YES]; - [panel setCanChooseDirectories:NO]; - [panel setAllowsMultipleSelection:NO]; - - if (options.startDir) - [panel setDirectory:[NSString stringWithUTF8String:options.startDir]]; - - if (options.title) - { - NSString* titleString = [[NSString alloc] - initWithBytes:options.title - length:strlen(options.title) - encoding:NSUTF8StringEncoding]; - [panel setTitle:titleString]; - } - - id delegate = pData->fFilePanelDelegate; - if (!delegate) - { - delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd - userData:pData]; - pData->fFilePanelDelegate = [delegate retain]; - } - - [panel beginSheetForDirectory:nullptr - file:nullptr - modalForWindow:nullptr - modalDelegate:delegate - didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) - contextInfo:nullptr]; - - return true; -# else - // not implemented - return false; - - // unused - (void)options; -# endif -} - -#ifdef DISTRHO_OS_MAC -END_NAMESPACE_DGL -@implementation FilePanelDelegate --(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData -{ - [super init]; - self->fCallback = callback; - self->fUserData = userData; - return self; -} - --(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo -{ - self->fCallback(sheet, returnCode, self->fUserData); - (void)contextInfo; -} -@end -START_NAMESPACE_DGL -#endif - -#endif // !defined(DGL_FILE_BROWSER_DISABLED) - bool Window::isEmbed() const noexcept { return pData->fUsingEmbed; @@ -1674,17 +156,19 @@ void Window::setIgnoringKeyRepeat(bool ignore) noexcept { pData->setIgnoringKeyRepeat(ignore); } +#endif Application& Window::getApp() const noexcept { return pData->fApp; } -intptr_t Window::getWindowId() const noexcept +uintptr_t Window::getWindowId() const noexcept { return puglGetNativeWindow(pData->fView); } +#if 0 const GraphicsContext& Window::getGraphicsContext() const noexcept { GraphicsContext& context = pData->fContext; @@ -1713,6 +197,7 @@ void Window::_idle() { pData->idle(); } +#endif // ----------------------------------------------------------------------- @@ -1734,28 +219,17 @@ void Window::removeIdleCallback(IdleCallback* const callback) void Window::onDisplayBefore() { -#ifdef DGL_OPENGL - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); -#endif + PrivateData::Fallback::onDisplayBefore(); } void Window::onDisplayAfter() { + PrivateData::Fallback::onDisplayAfter(); } -void Window::onReshape(uint width, uint height) +void Window::onReshape(const uint width, const uint height) { -#ifdef DGL_OPENGL - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#endif + PrivateData::Fallback::onReshape(width, height); } void Window::onClose() @@ -1768,6 +242,7 @@ void Window::fileBrowserSelected(const char*) } #endif +#if 0 bool Window::handlePluginKeyboard(const bool press, const uint key) { return pData->handlePluginKeyboard(press, key); @@ -1777,46 +252,7 @@ bool Window::handlePluginSpecial(const bool press, const Key key) { return pData->handlePluginSpecial(press, key); } - -// ----------------------------------------------------------------------- - -StandaloneWindow::StandaloneWindow() - : Application(), - Window((Application&)*this), - fWidget(nullptr) {} - -void StandaloneWindow::exec() -{ - Window::show(); - Application::exec(); -} - -void StandaloneWindow::onReshape(uint width, uint height) -{ - if (fWidget != nullptr) - fWidget->setSize(width, height); - Window::onReshape(width, height); -} - -void StandaloneWindow::_addWidget(Widget* widget) -{ - if (fWidget == nullptr) - { - fWidget = widget; - fWidget->pData->needsFullViewport = true; - } - Window::_addWidget(widget); -} - -void StandaloneWindow::_removeWidget(Widget* widget) -{ - if (fWidget == widget) - { - fWidget->pData->needsFullViewport = false; - fWidget = nullptr; - } - Window::_removeWidget(widget); -} +#endif // ----------------------------------------------------------------------- diff --git a/dgl/src/WindowFileBrowser.cpp b/dgl/src/WindowFileBrowser.cpp @@ -0,0 +1,223 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> + * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru> + * Copyright (C) 2019 Robin Gareus <robin@gareus.org> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../StandaloneWindow.hpp" + +// static int fib_filter_filename_filter(const char* const name) +// { +// return 1; +// (void)name; +// } + +// TODO use DGL_NAMESPACE for class names + +#ifdef DISTRHO_OS_MAC +@interface FilePanelDelegate : NSObject +{ + void (*fCallback)(NSOpenPanel*, int, void*); + void* fUserData; +} +-(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData; +-(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; +@end + +@implementation FilePanelDelegate +-(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData +{ + [super init]; + self->fCallback = callback; + self->fUserData = userData; + return self; +} + +-(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + self->fCallback(sheet, returnCode, self->fUserData); + (void)contextInfo; +} +@end +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +bool Window::openFileBrowser(const FileBrowserOptions& options) +{ +#if defined(DISTRHO_OS_WINDOWS) + // the old and compatible dialog API + OPENFILENAMEW ofn; + memset(&ofn, 0, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = pData->hwnd; + + // set initial directory in UTF-16 coding + std::vector<WCHAR> startDirW; + if (options.startDir) + { + startDirW.resize(strlen(options.startDir) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size())) + ofn.lpstrInitialDir = startDirW.data(); + } + + // set title in UTF-16 coding + std::vector<WCHAR> titleW; + if (options.title) + { + titleW.resize(strlen(options.title) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size())) + ofn.lpstrTitle = titleW.data(); + } + + // prepare a buffer to receive the result + std::vector<WCHAR> fileNameW(32768); // the Unicode maximum + ofn.lpstrFile = fileNameW.data(); + ofn.nMaxFile = (DWORD)fileNameW.size(); + + // TODO synchronous only, can't do better with WinAPI native dialogs. + // threading might work, if someone is motivated to risk it. + if (GetOpenFileNameW(&ofn)) + { + // back to UTF-8 + std::vector<char> fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr)) + { + // handle it during the next idle cycle (fake async) + pData->fSelectedFile = fileNameA.data(); + } + } + + return true; + +#elif defined(DISTRHO_OS_MAC) + if (pData->fOpenFilePanel) // permit one dialog at most + { + [pData->fOpenFilePanel makeKeyAndOrderFront:nil]; + return false; + } + + NSOpenPanel* panel = [NSOpenPanel openPanel]; + pData->fOpenFilePanel = [panel retain]; + + [panel setCanChooseFiles:YES]; + [panel setCanChooseDirectories:NO]; + [panel setAllowsMultipleSelection:NO]; + + if (options.startDir) + [panel setDirectory:[NSString stringWithUTF8String:options.startDir]]; + + if (options.title) + { + NSString* titleString = [[NSString alloc] + initWithBytes:options.title + length:strlen(options.title) + encoding:NSUTF8StringEncoding]; + [panel setTitle:titleString]; + } + + id delegate = pData->fFilePanelDelegate; + if (!delegate) + { + delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd + userData:pData]; + pData->fFilePanelDelegate = [delegate retain]; + } + + [panel beginSheetForDirectory:nullptr + file:nullptr + modalForWindow:nullptr + modalDelegate:delegate + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo:nullptr]; + + return true; + +#elif defined(SOFD_HAVE_X11) + using DISTRHO_NAMESPACE::String; + + // -------------------------------------------------------------------------- + // configure start dir + + // TODO: get abspath if needed + // TODO: cross-platform + + String startDir(options.startDir); + +# ifdef DISTRHO_OS_LINUX + if (startDir.isEmpty()) + { + if (char* const dir_name = get_current_dir_name()) + { + startDir = dir_name; + std::free(dir_name); + } + } +# endif + + DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); + + if (! startDir.endsWith('/')) + startDir += "/"; + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); + + // -------------------------------------------------------------------------- + // configure title + + String title(options.title); + + if (title.isEmpty()) + { + title = pData->getTitle(); + + if (title.isEmpty()) + title = "FileBrowser"; + } + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); + + // -------------------------------------------------------------------------- + // configure filters + + x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter); + + // -------------------------------------------------------------------------- + // configure buttons + + x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); + x_fib_cfg_buttons(1, options.buttons.showHidden-1); + x_fib_cfg_buttons(2, options.buttons.showPlaces-1); + + // -------------------------------------------------------------------------- + // show + + return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); + +#else + // not implemented + return false; + + // unused + (void)options; +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp @@ -0,0 +1,83 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "WindowPrivateData.hpp" + +#ifdef DGL_CAIRO +# define PUGL_CAIRO +# include "../Cairo.hpp" +#endif +#ifdef DGL_OPENGL +# define PUGL_OPENGL +# include "../OpenGL.hpp" +#endif + +extern "C" { +#include "pugl-upstream/pugl/detail/implementation.c" +} + +#if defined(DISTRHO_OS_HAIKU) +# define DGL_DEBUG_EVENTS +# include "pugl-upstream/pugl/detail/haiku.cpp" +#elif defined(DISTRHO_OS_MAC) +# define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) +# define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) +# include "pugl-upstream/pugl/detail/mac.m" +#elif defined(DISTRHO_OS_WINDOWS) +# include "ppugl-upstream/pugl/detail/win.c" +# undef max +# undef min +#else +# include <sys/types.h> +# include <unistd.h> +extern "C" { +# include "pugl-upstream/pugl/detail/x11.c" +} +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +void DGL::Window::PrivateData::Fallback::onDisplayBefore() +{ +#ifdef DGL_OPENGL + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); +#endif +} + +void DGL::Window::PrivateData::Fallback::onDisplayAfter() +{ +} + +void DGL::Window::PrivateData::Fallback::onReshape(const uint width, const uint height) +{ +#ifdef DGL_OPENGL + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); + glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp @@ -0,0 +1,1351 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED +#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED + +#include "../Window.hpp" +#include "ApplicationPrivateData.hpp" + +#include "pugl-upstream/pugl/pugl.h" + +#include <vector> + +#define FOR_EACH_WIDGET(it) \ + for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) +# define DBG(msg) std::fprintf(stderr, "%s", msg); +# define DBGp(...) std::fprintf(stderr, __VA_ARGS__); +# define DBGF std::fflush(stderr); +#else +# define DBG(msg) +# define DBGp(...) +# define DBGF +#endif + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +struct Window::PrivateData { + Application& fApp; + Window* fSelf; + PuglView* fView; + + // this one depends on build type + // GraphicsContext fContext; + + // Fallback build-specific Window functions + struct Fallback { + static void onDisplayBefore(); + static void onDisplayAfter(); + static void onReshape(uint width, uint height); + }; + + PrivateData(Application& app, Window* const self) + : fApp(app), + fSelf(self) + { + } + + PrivateData(Application& app, Window* const self, Window& parent) + : fApp(app), + fSelf(self) + { + } + + PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable) + : fApp(app), + fSelf(self) + { + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED + + + + +#if 0 +// ----------------------------------------------------------------------- +// Window Private + +struct Window::PrivateData { + PrivateData(Application& app, Window* const self) + : fApp(app), + fSelf(self), + fView(puglInit()), + fFirstInit(true), + fVisible(false), + fResizable(true), + fUsingEmbed(false), + fWidth(1), + fHeight(1), + fScaling(1.0), + fAutoScaling(1.0), + fTitle(nullptr), + fWidgets(), + fModal(), +#if defined(DISTRHO_OS_HAIKU) + bApplication(nullptr), + bView(nullptr), + bWindow(nullptr) +#elif defined(DISTRHO_OS_MAC) + fNeedsIdle(true), + mView(nullptr), + mWindow(nullptr), + mParentWindow(nullptr) +# ifndef DGL_FILE_BROWSER_DISABLED + , fOpenFilePanel(nullptr), + fFilePanelDelegate(nullptr) +# endif +#elif defined(DISTRHO_OS_WINDOWS) + hwnd(nullptr), + hwndParent(nullptr) +#else + xDisplay(nullptr), + xWindow(0) +#endif + { + DBG("Creating window without parent..."); DBGF; + init(); + } + + PrivateData(Application& app, Window* const self, Window& parent) + : fApp(app), + fSelf(self), + fView(puglInit()), + fFirstInit(true), + fVisible(false), + fResizable(true), + fUsingEmbed(false), + fWidth(1), + fHeight(1), + fScaling(1.0), + fAutoScaling(1.0), + fTitle(nullptr), + fWidgets(), + fModal(parent.pData), +#if defined(DISTRHO_OS_HAIKU) + bApplication(nullptr), + bView(nullptr), + bWindow(nullptr) +#elif defined(DISTRHO_OS_MAC) + fNeedsIdle(false), + mView(nullptr), + mWindow(nullptr), + mParentWindow(nullptr) +# ifndef DGL_FILE_BROWSER_DISABLED + , fOpenFilePanel(nullptr), + fFilePanelDelegate(nullptr) +# endif +#elif defined(DISTRHO_OS_WINDOWS) + hwnd(nullptr), + hwndParent(nullptr) +#else + xDisplay(nullptr), + xWindow(0) +#endif + { + DBG("Creating window with parent..."); DBGF; + init(); + + const PuglInternals* const parentImpl(parent.pData->fView->impl); + + // NOTE: almost a 1:1 copy of setTransientWinId() +#if defined(DISTRHO_OS_HAIKU) + // TODO +#elif defined(DISTRHO_OS_MAC) + mParentWindow = parentImpl->window; +#elif defined(DISTRHO_OS_WINDOWS) + hwndParent = parentImpl->hwnd; + SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent); +#else + XSetTransientForHint(xDisplay, xWindow, parentImpl->win); +#endif + } + + PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable) + : fApp(app), + fSelf(self), + fView(puglInit()), + fFirstInit(true), + fVisible(parentId != 0), + fResizable(resizable), + fUsingEmbed(parentId != 0), + fWidth(1), + fHeight(1), + fScaling(scaling), + fAutoScaling(1.0), + fTitle(nullptr), + fWidgets(), + fModal(), +#if defined(DISTRHO_OS_HAIKU) + bApplication(nullptr), + bView(nullptr), + bWindow(nullptr) +#elif defined(DISTRHO_OS_MAC) + fNeedsIdle(parentId == 0), + mView(nullptr), + mWindow(nullptr), + mParentWindow(nullptr) +# ifndef DGL_FILE_BROWSER_DISABLED + , fOpenFilePanel(nullptr), + fFilePanelDelegate(nullptr) +# endif +#elif defined(DISTRHO_OS_WINDOWS) + hwnd(nullptr), + hwndParent(nullptr) +#else + xDisplay(nullptr), + xWindow(0) +#endif + { + if (fUsingEmbed) + { + DBG("Creating embedded window..."); DBGF; + puglInitWindowParent(fView, parentId); + } + else + { + DBG("Creating window without parent..."); DBGF; + } + + init(); + + if (fUsingEmbed) + { + DBG("NOTE: Embed window is always visible and non-resizable\n"); + puglShowWindow(fView); + fApp.pData->oneShown(); + fFirstInit = false; + } + } + + void init() + { + if (fSelf == nullptr || fView == nullptr) + { + DBG("Failed!\n"); + return; + } + + puglInitUserResizable(fView, fResizable); + puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); + + puglSetHandle(fView, this); + puglSetDisplayFunc(fView, onDisplayCallback); + puglSetKeyboardFunc(fView, onKeyboardCallback); + puglSetMotionFunc(fView, onMotionCallback); + puglSetMouseFunc(fView, onMouseCallback); + puglSetScrollFunc(fView, onScrollCallback); + puglSetSpecialFunc(fView, onSpecialCallback); + puglSetReshapeFunc(fView, onReshapeCallback); + puglSetCloseFunc(fView, onCloseCallback); +#ifndef DGL_FILE_BROWSER_DISABLED + puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); +#endif + + puglCreateWindow(fView, nullptr); + + PuglInternals* impl = fView->impl; + +#if defined(DISTRHO_OS_HAIKU) + bApplication = impl->app; + bView = impl->view; + bWindow = impl->window; +#elif defined(DISTRHO_OS_MAC) + mView = impl->view; + mWindow = impl->window; + DISTRHO_SAFE_ASSERT(mView != nullptr); + if (fUsingEmbed) { + DISTRHO_SAFE_ASSERT(mWindow == nullptr); + } else { + DISTRHO_SAFE_ASSERT(mWindow != nullptr); + } +#elif defined(DISTRHO_OS_WINDOWS) + hwnd = impl->hwnd; + DISTRHO_SAFE_ASSERT(hwnd != 0); +#else + xDisplay = impl->display; + xWindow = impl->win; + DISTRHO_SAFE_ASSERT(xWindow != 0); + + if (! fUsingEmbed) + { + const pid_t pid = getpid(); + const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False); + XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); + + const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False); + + // Setting the window to both dialog and normal will produce a decorated floating dialog + // Order is important: DIALOG needs to come before NORMAL + const Atom _wts[2] = { + XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False), + XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False) + }; + XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); + } +#endif + puglEnterContext(fView); + + fApp.pData->windows.push_back(fSelf); + + DBG("Success!\n"); + } + + ~PrivateData() + { + DBG("Destroying window..."); DBGF; + + if (fModal.enabled) + { + exec_fini(); + close(); + } + + fWidgets.clear(); + + if (fUsingEmbed) + { + puglHideWindow(fView); + fApp.pData->oneHidden(); + } + + if (fSelf != nullptr) + { + fApp.pData->windows.remove(fSelf); + fSelf = nullptr; + } + + if (fView != nullptr) + { + puglDestroy(fView); + fView = nullptr; + } + + if (fTitle != nullptr) + { + std::free(fTitle); + fTitle = nullptr; + } + +#if defined(DISTRHO_OS_HAIKU) + bApplication = nullptr; + bView = nullptr; + bWindow = nullptr; +#elif defined(DISTRHO_OS_MAC) + mView = nullptr; + mWindow = nullptr; +#elif defined(DISTRHO_OS_WINDOWS) + hwnd = 0; +#else + xDisplay = nullptr; + xWindow = 0; +#endif + +#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) + if (fOpenFilePanel) + { + [fOpenFilePanel release]; + fOpenFilePanel = nullptr; + } + if (fFilePanelDelegate) + { + [fFilePanelDelegate release]; + fFilePanelDelegate = nullptr; + } +#endif + + DBG("Success!\n"); + } + + // ------------------------------------------------------------------- + + void close() + { + DBG("Window close\n"); + + if (fUsingEmbed) + return; + + setVisible(false); + + if (! fFirstInit) + { + fApp.pData->oneHidden(); + fFirstInit = true; + } + } + + void exec(const bool lockWait) + { + DBG("Window exec\n"); + exec_init(); + + if (lockWait) + { + for (; fVisible && fModal.enabled;) + { + idle(); + d_msleep(10); + } + + exec_fini(); + } + else + { + idle(); + } + } + + // ------------------------------------------------------------------- + + void exec_init() + { + DBG("Window modal loop starting..."); DBGF; + DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true)); + + fModal.enabled = true; + fModal.parent->fModal.childFocus = this; + + fModal.parent->setVisible(true); + setVisible(true); + + DBG("Ok\n"); + } + + void exec_fini() + { + DBG("Window modal loop stopping..."); DBGF; + fModal.enabled = false; + + if (fModal.parent != nullptr) + { + fModal.parent->fModal.childFocus = nullptr; + + // the mouse position probably changed since the modal appeared, + // so send a mouse motion event to the modal's parent window +#if defined(DISTRHO_OS_HAIKU) + // TODO +#elif defined(DISTRHO_OS_MAC) + // TODO +#elif defined(DISTRHO_OS_WINDOWS) + // TODO +#else + int i, wx, wy; + uint u; + ::Window w; + if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True) + fModal.parent->onPuglMotion(wx, wy); +#endif + } + + DBG("Ok\n"); + } + + // ------------------------------------------------------------------- + + void focus() + { + DBG("Window focus\n"); +#if defined(DISTRHO_OS_HAIKU) + if (bWindow != nullptr) + { + if (bWindow->LockLooper()) + { + bWindow->Activate(true); + bWindow->UnlockLooper(); + } + } + else + { + bView->MakeFocus(true); + } +#elif defined(DISTRHO_OS_MAC) + if (mWindow != nullptr) + [mWindow makeKeyWindow]; +#elif defined(DISTRHO_OS_WINDOWS) + SetForegroundWindow(hwnd); + SetActiveWindow(hwnd); + SetFocus(hwnd); +#else + XRaiseWindow(xDisplay, xWindow); + XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime); + XFlush(xDisplay); +#endif + } + + // ------------------------------------------------------------------- + + void setVisible(const bool yesNo) + { + if (fVisible == yesNo) + { + DBG("Window setVisible matches current state, ignoring request\n"); + return; + } + if (fUsingEmbed) + { + DBG("Window setVisible cannot be called when embedded\n"); + return; + } + + DBG("Window setVisible called\n"); + + fVisible = yesNo; + + if (yesNo && fFirstInit) + setSize(fWidth, fHeight, true); + +#if defined(DISTRHO_OS_HAIKU) + if (bWindow != nullptr) + { + if (bWindow->LockLooper()) + { + if (yesNo) + bWindow->Show(); + else + bWindow->Hide(); + + // TODO use flush? + bWindow->Sync(); + bWindow->UnlockLooper(); + } + } + else + { + if (yesNo) + bView->Show(); + else + bView->Hide(); + } +#elif defined(DISTRHO_OS_MAC) + if (yesNo) + { + if (mWindow != nullptr) + { + if (mParentWindow != nullptr) + [mParentWindow addChildWindow:mWindow + ordered:NSWindowAbove]; + + [mWindow setIsVisible:YES]; + } + else + { + [mView setHidden:NO]; + } + } + else + { + if (mWindow != nullptr) + { + if (mParentWindow != nullptr) + [mParentWindow removeChildWindow:mWindow]; + + [mWindow setIsVisible:NO]; + } + else + { + [mView setHidden:YES]; + } + } +#elif defined(DISTRHO_OS_WINDOWS) + if (yesNo) + { + if (fFirstInit) + { + RECT rectChild, rectParent; + + if (hwndParent != nullptr && + GetWindowRect(hwnd, &rectChild) && + GetWindowRect(hwndParent, &rectParent)) + { + SetWindowPos(hwnd, hwndParent, + rectParent.left + (rectChild.right-rectChild.left)/2, + rectParent.top + (rectChild.bottom-rectChild.top)/2, + 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); + } + else + { + ShowWindow(hwnd, SW_SHOWNORMAL); + } + } + else + { + ShowWindow(hwnd, SW_RESTORE); + } + } + else + { + ShowWindow(hwnd, SW_HIDE); + } + + UpdateWindow(hwnd); +#else + if (yesNo) + XMapRaised(xDisplay, xWindow); + else + XUnmapWindow(xDisplay, xWindow); + + XFlush(xDisplay); +#endif + + if (yesNo) + { + if (fFirstInit) + { + fApp.pData->oneShown(); + fFirstInit = false; + } + } + else if (fModal.enabled) + exec_fini(); + } + + // ------------------------------------------------------------------- + + void setResizable(const bool yesNo) + { + if (fResizable == yesNo) + { + DBG("Window setResizable matches current state, ignoring request\n"); + return; + } + if (fUsingEmbed) + { + DBG("Window setResizable cannot be called when embedded\n"); + return; + } + + DBG("Window setResizable called\n"); + + fResizable = yesNo; + fView->user_resizable = yesNo; + +#if defined(DISTRHO_OS_HAIKU) + // TODO + // B_NO_BORDER + // B_TITLED_WINDOW_LOOK + // bWindow->SetFlags(); +#elif defined(DISTRHO_OS_MAC) + const uint flags = yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0; + [mView setAutoresizingMask:flags]; +#elif defined(DISTRHO_OS_WINDOWS) + const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX + : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; + SetWindowLong(hwnd, GWL_STYLE, winFlags); +#endif + + setSize(fWidth, fHeight, true); + } + + // ------------------------------------------------------------------- + + void setGeometryConstraints(uint width, uint height, bool aspect) + { + // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? + DISTRHO_SAFE_ASSERT_RETURN(fResizable,); + + fView->min_width = width; + fView->min_height = height; + puglUpdateGeometryConstraints(fView, width, height, aspect); + } + + // ------------------------------------------------------------------- + + void setSize(uint width, uint height, const bool forced = false) + { + if (width <= 1 || height <= 1) + { + DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height); + return; + } + + if (fWidth == width && fHeight == height && ! forced) + { + DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height); + return; + } + + fWidth = width; + fHeight = height; + + DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false"); + +#if defined(DISTRHO_OS_HAIKU) + bView->ResizeTo(width, height); + + if (bWindow != nullptr && bWindow->LockLooper()) + { + bWindow->MoveTo(50, 50); + bWindow->ResizeTo(width, height); + + if (! forced) + bWindow->Flush(); + + bWindow->UnlockLooper(); + } + // TODO resizable +#elif defined(DISTRHO_OS_MAC) + [mView setFrame:NSMakeRect(0, 0, width, height)]; + + if (mWindow != nullptr) + { + const NSSize size = NSMakeSize(width, height); + [mWindow setContentSize:size]; + + if (fResizable) + { + [mWindow setContentMinSize:NSMakeSize(1, 1)]; + [mWindow setContentMaxSize:NSMakeSize(99999, 99999)]; + [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO]; + } + else + { + [mWindow setContentMinSize:size]; + [mWindow setContentMaxSize:size]; + [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES]; + } + } +#elif defined(DISTRHO_OS_WINDOWS) + const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); + RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) }; + AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); + + SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, + SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); + + if (! forced) + UpdateWindow(hwnd); +#else + + if (! fResizable) + { + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + + sizeHints.flags = PSize|PMinSize|PMaxSize; + sizeHints.width = static_cast<int>(width); + sizeHints.height = static_cast<int>(height); + sizeHints.min_width = static_cast<int>(width); + sizeHints.min_height = static_cast<int>(height); + sizeHints.max_width = static_cast<int>(width); + sizeHints.max_height = static_cast<int>(height); + + XSetWMNormalHints(xDisplay, xWindow, &sizeHints); + } + + XResizeWindow(xDisplay, xWindow, width, height); + + if (! forced) + XFlush(xDisplay); +#endif + + puglPostRedisplay(fView); + } + + // ------------------------------------------------------------------- + + const char* getTitle() const noexcept + { + static const char* const kFallback = ""; + + return fTitle != nullptr ? fTitle : kFallback; + } + + void setTitle(const char* const title) + { + DBGp("Window setTitle \"%s\"\n", title); + + if (fTitle != nullptr) + std::free(fTitle); + + fTitle = strdup(title); + +#if defined(DISTRHO_OS_HAIKU) + if (bWindow != nullptr&& bWindow->LockLooper()) + { + bWindow->SetTitle(title); + bWindow->UnlockLooper(); + } +#elif defined(DISTRHO_OS_MAC) + if (mWindow != nullptr) + { + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + [mWindow setTitle:titleString]; + } +#elif defined(DISTRHO_OS_WINDOWS) + SetWindowTextA(hwnd, title); +#else + XStoreName(xDisplay, xWindow, title); + Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False); + Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False); + XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); +#endif + } + + void setTransientWinId(const uintptr_t winId) + { + DISTRHO_SAFE_ASSERT_RETURN(winId != 0,); + +#if defined(DISTRHO_OS_HAIKU) + // TODO +#elif defined(DISTRHO_OS_MAC) + NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId]; + DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,); + + [parentWindow addChildWindow:mWindow + ordered:NSWindowAbove]; +#elif defined(DISTRHO_OS_WINDOWS) + hwndParent = (HWND)winId; + SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId); +#else + XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId)); +#endif + } + + // ------------------------------------------------------------------- + + double getScaling() const noexcept + { + return fScaling; + } + + void setAutoScaling(const double scaling) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); + + fAutoScaling = scaling; + } + + // ------------------------------------------------------------------- + + bool getIgnoringKeyRepeat() const noexcept + { + return fView->ignoreKeyRepeat; + } + + void setIgnoringKeyRepeat(bool ignore) noexcept + { + puglIgnoreKeyRepeat(fView, ignore); + } + + // ------------------------------------------------------------------- + + void addWidget(Widget* const widget) + { + fWidgets.push_back(widget); + } + + void removeWidget(Widget* const widget) + { + fWidgets.remove(widget); + } + + void idle() + { + puglProcessEvents(fView); + +#ifdef DISTRHO_OS_HAIKU + if (bApplication != nullptr) + { + // bApplication->Lock(); + // bApplication->Loop(); + // bApplication->Unlock(); + } +#endif + +#ifdef DISTRHO_OS_MAC + if (fNeedsIdle) + { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event; + + for (;;) + { + event = [NSApp + nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + + if (event == nil) + break; + + [NSApp sendEvent: event]; + } + + [pool release]; + } +#endif + +#if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) + if (fSelectedFile.isNotEmpty()) + { + char* const buffer = fSelectedFile.getAndReleaseBuffer(); + fView->fileSelectedFunc(fView, buffer); + std::free(buffer); + } +#endif + + if (fModal.enabled && fModal.parent != nullptr) + fModal.parent->idle(); + } + + // ------------------------------------------------------------------- + + void onPuglDisplay() + { + fSelf->onDisplayBefore(); + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->pData->display(fWidth, fHeight, fAutoScaling, false); + } + + fSelf->onDisplayAfter(); + } + + int onPuglKeyboard(const bool press, const uint key) + { + DBGp("PUGL: onKeyboard : %i %i\n", press, key); + + if (fModal.childFocus != nullptr) + { + fModal.childFocus->focus(); + return 0; + } + + Widget::KeyboardEvent ev; + ev.press = press; + ev.key = key; + ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onKeyboard(ev)) + return 0; + } + + return 1; + } + + int onPuglSpecial(const bool press, const Key key) + { + DBGp("PUGL: onSpecial : %i %i\n", press, key); + + if (fModal.childFocus != nullptr) + { + fModal.childFocus->focus(); + return 0; + } + + Widget::SpecialEvent ev; + ev.press = press; + ev.key = key; + ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onSpecial(ev)) + return 0; + } + + return 1; + } + + void onPuglMouse(const int button, const bool press, int x, int y) + { + DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y); + + // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it + if (press && button == 0 && x == 0 && y == 0) return; + + if (fModal.childFocus != nullptr) + return fModal.childFocus->focus(); + + x /= fAutoScaling; + y /= fAutoScaling; + + Widget::MouseEvent ev; + ev.button = button; + ev.press = press; + ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onMouse(ev)) + break; + } + } + + void onPuglMotion(int x, int y) + { + // DBGp("PUGL: onMotion : %i %i\n", x, y); + + if (fModal.childFocus != nullptr) + return; + + x /= fAutoScaling; + y /= fAutoScaling; + + Widget::MotionEvent ev; + ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onMotion(ev)) + break; + } + } + + void onPuglScroll(int x, int y, float dx, float dy) + { + DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy); + + if (fModal.childFocus != nullptr) + return; + + x /= fAutoScaling; + y /= fAutoScaling; + dx /= fAutoScaling; + dy /= fAutoScaling; + + Widget::ScrollEvent ev; + ev.delta = Point<float>(dx, dy); + ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onScroll(ev)) + break; + } + } + + void onPuglReshape(const int width, const int height) + { + DBGp("PUGL: onReshape : %i %i\n", width, height); + + if (width <= 1 && height <= 1) + return; + + fWidth = static_cast<uint>(width); + fHeight = static_cast<uint>(height); + + fSelf->onReshape(fWidth, fHeight); + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + + if (widget->pData->needsFullViewport) + widget->setSize(fWidth, fHeight); + } + } + + void onPuglClose() + { + DBG("PUGL: onClose\n"); + + if (fModal.enabled) + exec_fini(); + + fSelf->onClose(); + + if (fModal.childFocus != nullptr) + fModal.childFocus->fSelf->onClose(); + + close(); + } + + // ------------------------------------------------------------------- + + bool handlePluginKeyboard(const bool press, const uint key) + { + DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key); + + if (fModal.childFocus != nullptr) + { + fModal.childFocus->focus(); + return true; + } + + Widget::KeyboardEvent ev; + ev.press = press; + ev.key = key; + ev.mod = static_cast<Modifier>(fView->mods); + ev.time = 0; + + if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z') + ev.key -= 'a' - 'A'; // a-z -> A-Z + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onKeyboard(ev)) + return true; + } + + return false; + } + + bool handlePluginSpecial(const bool press, const Key key) + { + DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); + + if (fModal.childFocus != nullptr) + { + fModal.childFocus->focus(); + return true; + } + + int mods = 0x0; + + switch (key) + { + case kKeyShift: + mods |= kModifierShift; + break; + case kKeyControl: + mods |= kModifierControl; + break; + case kKeyAlt: + mods |= kModifierAlt; + break; + default: + break; + } + + if (mods != 0x0) + { + if (press) + fView->mods |= mods; + else + fView->mods &= ~(mods); + } + + Widget::SpecialEvent ev; + ev.press = press; + ev.key = key; + ev.mod = static_cast<Modifier>(fView->mods); + ev.time = 0; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + if (widget->isVisible() && widget->onSpecial(ev)) + return true; + } + + return false; + } + +#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) + static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) + { + PrivateData* pData = (PrivateData*)userData; + + if (returnCode == NSOKButton) + { + NSArray* urls = [panel URLs]; + NSURL* fileUrl = nullptr; + + for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) + { + NSURL* url = (NSURL*)[urls objectAtIndex:i]; + if ([url isFileURL]) + fileUrl = url; + } + + if (fileUrl) + { + PuglView* view = pData->fView; + if (view->fileSelectedFunc) + { + const char* fileName = [fileUrl.path UTF8String]; + view->fileSelectedFunc(view, fileName); + } + } + } + + [pData->fOpenFilePanel release]; + pData->fOpenFilePanel = nullptr; + } +#endif + + // ------------------------------------------------------------------- + + Application& fApp; + Window* fSelf; + GraphicsContext fContext; + PuglView* fView; + + bool fFirstInit; + bool fVisible; + bool fResizable; + bool fUsingEmbed; + uint fWidth; + uint fHeight; + double fScaling; + double fAutoScaling; + char* fTitle; + std::list<Widget*> fWidgets; + + struct Modal { + bool enabled; + PrivateData* parent; + PrivateData* childFocus; + + Modal() + : enabled(false), + parent(nullptr), + childFocus(nullptr) {} + + Modal(PrivateData* const p) + : enabled(false), + parent(p), + childFocus(nullptr) {} + + ~Modal() + { + DISTRHO_SAFE_ASSERT(! enabled); + DISTRHO_SAFE_ASSERT(childFocus == nullptr); + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(Modal) + } fModal; + +#if defined(DISTRHO_OS_HAIKU) + BApplication* bApplication; + BView* bView; + BWindow* bWindow; +#elif defined(DISTRHO_OS_MAC) + bool fNeedsIdle; + NSView<PuglGenericView>* mView; + id mWindow; + id mParentWindow; +# ifndef DGL_FILE_BROWSER_DISABLED + NSOpenPanel* fOpenFilePanel; + id fFilePanelDelegate; +# endif +#elif defined(DISTRHO_OS_WINDOWS) + HWND hwnd; + HWND hwndParent; +# ifndef DGL_FILE_BROWSER_DISABLED + String fSelectedFile; +# endif +#else + Display* xDisplay; + ::Window xWindow; +#endif + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((PrivateData*)puglGetHandle(view)) + + static void onDisplayCallback(PuglView* view) + { + handlePtr->onPuglDisplay(); + } + + static int onKeyboardCallback(PuglView* view, bool press, uint32_t key) + { + return handlePtr->onPuglKeyboard(press, key); + } + + static int onSpecialCallback(PuglView* view, bool press, PuglKey key) + { + return handlePtr->onPuglSpecial(press, static_cast<Key>(key)); + } + + static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) + { + handlePtr->onPuglMouse(button, press, x, y); + } + + static void onMotionCallback(PuglView* view, int x, int y) + { + handlePtr->onPuglMotion(x, y); + } + + static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy) + { + handlePtr->onPuglScroll(x, y, dx, dy); + } + + static void onReshapeCallback(PuglView* view, int width, int height) + { + handlePtr->onPuglReshape(width, height); + } + + static void onCloseCallback(PuglView* view) + { + handlePtr->onPuglClose(); + } + +#ifndef DGL_FILE_BROWSER_DISABLED + static void fileBrowserSelectedCallback(PuglView* view, const char* filename) + { + handlePtr->fSelf->fileBrowserSelected(filename); + } +#endif + + #undef handlePtr + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) +}; +#endif