From c36623946d7ff7ba70f3b19aad14604e6640e222 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Apr 2020 22:45:26 +0100 Subject: [PATCH 001/159] Add raw/upstream pugl as submodule Signed-off-by: falkTX --- .gitmodules | 3 +++ dgl/src/pugl-upstream | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 dgl/src/pugl-upstream diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..2d4619a5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dgl/src/pugl-upstream"] + path = dgl/src/pugl-upstream + url = git@github.com:lv2/pugl.git diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream new file mode 160000 index 00000000..69366ae5 --- /dev/null +++ b/dgl/src/pugl-upstream @@ -0,0 +1 @@ +Subproject commit 69366ae5c185b6604aed8fd0dc81ed84d342fafd From c0d18986db50b3472ef59088f2d0acc9b0a8689f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 17:58:41 +0000 Subject: [PATCH 002/159] Update pugl-upstream submodule Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 69366ae5..2d3100e4 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 69366ae5c185b6604aed8fd0dc81ed84d342fafd +Subproject commit 2d3100e47eee6a8bf2209ee19157de6d23dd1c55 From 87d60f027c73c554573ff915d091d85675c7b629 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 18:00:13 +0000 Subject: [PATCH 003/159] Move old custom plugin to separate dir Signed-off-by: falkTX --- dgl/src/{pugl => pugl-custom}/pugl.h | 0 dgl/src/{pugl => pugl-custom}/pugl_haiku.cpp | 0 dgl/src/{pugl => pugl-custom}/pugl_internal.h | 0 dgl/src/{pugl => pugl-custom}/pugl_osx.m | 0 dgl/src/{pugl => pugl-custom}/pugl_win.cpp | 0 dgl/src/{pugl => pugl-custom}/pugl_x11.c | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename dgl/src/{pugl => pugl-custom}/pugl.h (100%) rename dgl/src/{pugl => pugl-custom}/pugl_haiku.cpp (100%) rename dgl/src/{pugl => pugl-custom}/pugl_internal.h (100%) rename dgl/src/{pugl => pugl-custom}/pugl_osx.m (100%) rename dgl/src/{pugl => pugl-custom}/pugl_win.cpp (100%) rename dgl/src/{pugl => pugl-custom}/pugl_x11.c (100%) diff --git a/dgl/src/pugl/pugl.h b/dgl/src/pugl-custom/pugl.h similarity index 100% rename from dgl/src/pugl/pugl.h rename to dgl/src/pugl-custom/pugl.h diff --git a/dgl/src/pugl/pugl_haiku.cpp b/dgl/src/pugl-custom/pugl_haiku.cpp similarity index 100% rename from dgl/src/pugl/pugl_haiku.cpp rename to dgl/src/pugl-custom/pugl_haiku.cpp diff --git a/dgl/src/pugl/pugl_internal.h b/dgl/src/pugl-custom/pugl_internal.h similarity index 100% rename from dgl/src/pugl/pugl_internal.h rename to dgl/src/pugl-custom/pugl_internal.h diff --git a/dgl/src/pugl/pugl_osx.m b/dgl/src/pugl-custom/pugl_osx.m similarity index 100% rename from dgl/src/pugl/pugl_osx.m rename to dgl/src/pugl-custom/pugl_osx.m diff --git a/dgl/src/pugl/pugl_win.cpp b/dgl/src/pugl-custom/pugl_win.cpp similarity index 100% rename from dgl/src/pugl/pugl_win.cpp rename to dgl/src/pugl-custom/pugl_win.cpp diff --git a/dgl/src/pugl/pugl_x11.c b/dgl/src/pugl-custom/pugl_x11.c similarity index 100% rename from dgl/src/pugl/pugl_x11.c rename to dgl/src/pugl-custom/pugl_x11.c From 8e5bb75a862e1ee43927dc26a1fb252aab938fe0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Apr 2020 23:35:25 +0100 Subject: [PATCH 004/159] Adjust DGL Makefile to point to new pugl Signed-off-by: falkTX --- dgl/Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 20bd79e3..49081b38 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -9,9 +9,12 @@ include ../Makefile.base.mk # --------------------------------------------------------------------------------------------------------------------- BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc -BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter +BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -Isrc/pugl-upstream -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter LINK_FLAGS += $(DGL_LIBS) +# TODO fix these after pugl-upstream is done +BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra -Wno-narrowing + # ifneq ($(MACOS_OLD),true) # needed by sofd right now, fix later # BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive @@ -110,12 +113,12 @@ all: $(TARGETS) @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl/* +../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -@mkdir -p ../build/dgl @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl/* +../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -@mkdir -p ../build/dgl @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@ @@ -127,12 +130,12 @@ all: $(TARGETS) @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl/* +../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl/* +../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@ From e1aee0ec9f4693d1ad9e45a141d12bad81633c71 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Apr 2020 23:36:13 +0100 Subject: [PATCH 005/159] Port Application to new pugl style Signed-off-by: falkTX --- dgl/Application.hpp | 7 ++-- dgl/src/Application.cpp | 38 ++++------------- dgl/src/ApplicationPrivateData.hpp | 67 +++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index dc630ceb..87802973 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2020 Filipe Coelho * * 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 @@ -44,7 +44,8 @@ public: /** Constructor. */ - Application(); + // NOTE: the default value is not yet passed, so we catch where we use this + Application(bool isStandalone /* = true */); /** Destructor. @@ -62,7 +63,7 @@ public: idle() is called at regular intervals. @note This function is meant for standalones only, *never* call this from plugins. */ - void exec(int idleTime = 10); + void exec(uint idleTime = 10); /** Quit the application. diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 114ee8fc..db99e746 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2020 Filipe Coelho * * 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 @@ -15,14 +15,13 @@ */ #include "ApplicationPrivateData.hpp" -#include "../Window.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -Application::Application() - : pData(new PrivateData()) {} +Application::Application(const bool isStandalone) + : pData(new PrivateData(isStandalone)) {} Application::~Application() { @@ -31,42 +30,23 @@ Application::~Application() void Application::idle() { - for (std::list::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it) - { - Window* const window(*it); - window->_idle(); - } - - for (std::list::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it) - { - IdleCallback* const idleCallback(*it); - idleCallback->idleCallback(); - } + pData->idle(0); } -void Application::exec(int idleTime) +void Application::exec(const uint idleTime) { - for (; pData->doLoop;) - { - idle(); - d_msleep(idleTime); - } + while (!pData->isQuitting) + pData->idle(idleTime); } void Application::quit() { - pData->doLoop = false; - - for (std::list::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit) - { - Window* const window(*rit); - window->close(); - } + pData->quit(); } bool Application::isQuiting() const noexcept { - return !pData->doLoop; + return pData->isQuitting; } // ----------------------------------------------------------------------- diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index e95d44a7..05b23c0c 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2020 Filipe Coelho * * 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 @@ -18,7 +18,9 @@ #define DGL_APP_PRIVATE_DATA_HPP_INCLUDED #include "../Application.hpp" -#include "../../distrho/extra/Sleep.hpp" +#include "../Window.hpp" + +#include "pugl-upstream/pugl/pugl.h" #include @@ -27,38 +29,81 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- struct Application::PrivateData { - bool doLoop; + bool isQuitting; + bool isStandalone; uint visibleWindows; std::list windows; std::list idleCallbacks; + PuglWorld* const world; - PrivateData() - : doLoop(true), + PrivateData(const bool standalone) + : isQuitting(false), + isStandalone(standalone), visibleWindows(0), windows(), - idleCallbacks() {} + idleCallbacks(), + world(puglNewWorld(isStandalone ? PUGL_PROGRAM : PUGL_MODULE, + isStandalone ? PUGL_WORLD_THREADS : 0x0)) + { + puglSetWorldHandle(world, this); + + // TODO puglSetClassName + } ~PrivateData() { - DISTRHO_SAFE_ASSERT(! doLoop); + DISTRHO_SAFE_ASSERT(isQuitting); DISTRHO_SAFE_ASSERT(visibleWindows == 0); windows.clear(); idleCallbacks.clear(); + + puglFreeWorld(world); } - void oneShown() noexcept + void oneWindowShown() noexcept { + DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); + if (++visibleWindows == 1) - doLoop = true; + isQuitting = false; } - void oneHidden() noexcept + void oneWindowHidden() noexcept { + DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,); if (--visibleWindows == 0) - doLoop = false; + isQuitting = true; + } + + void idle(const uint timeout) + { + puglUpdate(world, timeout == 0 ? 0.0 : (1.0 / static_cast(timeout))); + + for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) + { + Window* const window(*it); + window->_idle(); + } + + for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) + { + IdleCallback* const idleCallback(*it); + idleCallback->idleCallback(); + } + } + + void quit() + { + isQuitting = true; + + for (std::list::reverse_iterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) + { + Window* const window(*rit); + window->close(); + } } DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) From fc10a2153af8933d13f6421cee90d8769e86ef0e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2020 00:14:50 +0100 Subject: [PATCH 006/159] Start split of Window.cpp into several smaller files Signed-off-by: falkTX --- dgl/Makefile | 33 +- dgl/Window.hpp | 2 +- dgl/src/StandaloneWindow.cpp | 64 ++ dgl/src/Window.cpp | 1594 +-------------------------------- dgl/src/WindowFileBrowser.cpp | 223 +++++ dgl/src/WindowPrivateData.cpp | 83 ++ dgl/src/WindowPrivateData.hpp | 1351 ++++++++++++++++++++++++++++ 7 files changed, 1756 insertions(+), 1594 deletions(-) create mode 100644 dgl/src/StandaloneWindow.cpp create mode 100644 dgl/src/WindowFileBrowser.cpp create mode 100644 dgl/src/WindowPrivateData.cpp create mode 100644 dgl/src/WindowPrivateData.hpp diff --git a/dgl/Makefile b/dgl/Makefile index 49081b38..fe372d1b 100644 --- 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 index 931177ea..6de780f5 100644 --- 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 new file mode 100644 index 00000000..71dc9a27 --- /dev/null +++ b/dgl/src/StandaloneWindow.cpp @@ -0,0 +1,64 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho + * + * 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 index 61218b08..dac1b9f9 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -1,8 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho - * Copyright (C) 2019 Jean Pierre Cimalando - * Copyright (C) 2019 Robin Gareus + * Copyright (C) 2012-2020 Filipe Coelho * * 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,1329 +17,13 @@ // 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 -# include -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::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) - -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::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(fWidth), static_cast(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(width), static_cast(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(width); - sizeHints.height = static_cast(height); - sizeHints.min_width = static_cast(width); - sizeHints.min_height = static_cast(height); - sizeHints.max_width = static_cast(width); - sizeHints.max_height = static_cast(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(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(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(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(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(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(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(dx, dy); - ev.mod = static_cast(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(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(width); - fHeight = static_cast(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(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(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 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* 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)); - } - - 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 @@ -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 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 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 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 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(width), static_cast(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast(width), static_cast(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 new file mode 100644 index 00000000..b5044ca7 --- /dev/null +++ b/dgl/src/WindowFileBrowser.cpp @@ -0,0 +1,223 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2019 Jean Pierre Cimalando + * Copyright (C) 2019 Robin Gareus + * + * 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 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 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 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 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 new file mode 100644 index 00000000..cc506757 --- /dev/null +++ b/dgl/src/WindowPrivateData.cpp @@ -0,0 +1,83 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho + * + * 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 +# include +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(width), static_cast(height), 0.0, 0.0, 1.0); + glViewport(0, 0, static_cast(width), static_cast(height)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp new file mode 100644 index 00000000..f2dc1899 --- /dev/null +++ b/dgl/src/WindowPrivateData.hpp @@ -0,0 +1,1351 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho + * + * 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 + +#define FOR_EACH_WIDGET(it) \ + for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list::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(fWidth), static_cast(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(width), static_cast(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(width); + sizeHints.height = static_cast(height); + sizeHints.min_width = static_cast(width); + sizeHints.min_height = static_cast(height); + sizeHints.max_width = static_cast(width); + sizeHints.max_height = static_cast(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(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(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(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point(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(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point(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(dx, dy); + ev.mod = static_cast(puglGetModifiers(fView)); + ev.time = puglGetEventTimestamp(fView); + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + ev.pos = Point(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(width); + fHeight = static_cast(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(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(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 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* 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)); + } + + 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 From c1305ef83114927dba12d888866a05f2b1f98081 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 18:30:46 +0000 Subject: [PATCH 007/159] Split DistrhoUIInternal base struct into separate file; Cleanup Signed-off-by: falkTX --- dgl/Makefile | 2 +- dgl/Window.hpp | 2 + dgl/src/ApplicationPrivateData.hpp | 4 +- dgl/src/WindowPrivateData.cpp | 14 ++- dgl/src/WindowPrivateData.hpp | 6 +- distrho/src/DistrhoPluginChecks.h | 8 ++ distrho/src/DistrhoPluginJack.cpp | 1 + distrho/src/DistrhoUI.cpp | 52 ++++++--- distrho/src/DistrhoUIInternal.hpp | 150 ++------------------------ distrho/src/DistrhoUIPrivateData.hpp | 152 +++++++++++++++++++++++++++ 10 files changed, 222 insertions(+), 169 deletions(-) create mode 100644 distrho/src/DistrhoUIPrivateData.hpp diff --git a/dgl/Makefile b/dgl/Makefile index fe372d1b..45f75f5b 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -9,7 +9,7 @@ include ../Makefile.base.mk # --------------------------------------------------------------------------------------------------------------------- BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc -BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -Isrc/pugl-upstream -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter +BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -Isrc/pugl-upstream/include -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter LINK_FLAGS += $(DGL_LIBS) # TODO fix these after pugl-upstream is done diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 6de780f5..7f4e16e8 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -21,6 +21,7 @@ #ifdef DISTRHO_DEFINES_H_INCLUDED START_NAMESPACE_DISTRHO +class UI; class UIExporter; END_NAMESPACE_DISTRHO #endif @@ -145,6 +146,7 @@ private: friend class Widget; friend class StandaloneWindow; #ifdef DISTRHO_DEFINES_H_INCLUDED + friend class DISTRHO_NAMESPACE::UI; friend class DISTRHO_NAMESPACE::UIExporter; #endif diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 05b23c0c..477f5bb6 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,7 +20,7 @@ #include "../Application.hpp" #include "../Window.hpp" -#include "pugl-upstream/pugl/pugl.h" +#include "pugl-upstream/include/pugl/pugl.h" #include diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index cc506757..7b2d6d6c 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -26,25 +26,25 @@ #endif extern "C" { -#include "pugl-upstream/pugl/detail/implementation.c" +#include "pugl-upstream/src/implementation.c" } #if defined(DISTRHO_OS_HAIKU) # define DGL_DEBUG_EVENTS -# include "pugl-upstream/pugl/detail/haiku.cpp" +# include "pugl-upstream/src/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" +# include "pugl-upstream/src/mac.m" #elif defined(DISTRHO_OS_WINDOWS) -# include "ppugl-upstream/pugl/detail/win.c" +# include "ppugl-upstream/src/win.c" # undef max # undef min #else # include # include extern "C" { -# include "pugl-upstream/pugl/detail/x11.c" +# include "pugl-upstream/src/x11.c" } #endif @@ -75,6 +75,10 @@ void DGL::Window::PrivateData::Fallback::onReshape(const uint width, const uint glViewport(0, 0, static_cast(width), static_cast(height)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); +#else + // unused + (void)width; + (void)height; #endif } diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index f2dc1899..c917ffe2 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -20,7 +20,7 @@ #include "../Window.hpp" #include "ApplicationPrivateData.hpp" -#include "pugl-upstream/pugl/pugl.h" +#include "pugl-upstream/include/pugl/pugl.h" #include @@ -77,6 +77,10 @@ struct Window::PrivateData { { } +#ifdef DISTRHO_DEFINES_H_INCLUDED + friend class DISTRHO_NAMESPACE::UI; +#endif + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) }; diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index 69033dee..7bc5dd67 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -161,6 +161,14 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif +// ----------------------------------------------------------------------- +// Prevent users from messing about with DPF internals + +#ifdef DISTRHO_UI_IS_STANDALONE +# error DISTRHO_UI_IS_STANDALONE must not be defined +#endif + + // ----------------------------------------------------------------------- #endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index a99892d9..59393472 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -17,6 +17,7 @@ #include "DistrhoPluginInternal.hpp" #if DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_UI_IS_STANDALONE true # include "DistrhoUIInternal.hpp" #else # include "../extra/Sleep.hpp" diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index bda5eb4c..7bb52475 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -14,7 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "DistrhoUIInternal.hpp" +#include "DistrhoUIPrivateData.hpp" +#include "src/WindowPrivateData.hpp" #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "src/WidgetPrivateData.hpp" #endif @@ -22,7 +23,7 @@ START_NAMESPACE_DISTRHO /* ------------------------------------------------------------------------------------------------------------ - * Static data, see DistrhoUIInternal.hpp */ + * Static data, see DistrhoUIInternal.hpp and DistrhoUIPrivateData.hpp */ double d_lastUiSampleRate = 0.0; void* d_lastUiDspPtr = nullptr; @@ -34,6 +35,36 @@ uintptr_t g_nextWindowId = 0; Window* d_lastUiWindow = nullptr; #endif +// ----------------------------------------------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI +static inline +UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath) +{ + d_lastUiDspPtr = dspPtr; + g_nextWindowId = winId; + g_nextScaleFactor = scaleFactor; + g_nextBundlePath = bundlePath; + UI* const ret = createUI(); + d_lastUiDspPtr = nullptr; + g_nextWindowId = 0; + g_nextScaleFactor = 1.0; + g_nextBundlePath = nullptr; + return ret; +} +#else +static inline +UI* createUiWrapper(void* const dspPtr, Window* const window) +{ + d_lastUiDspPtr = dspPtr; + d_lastUiWindow = window; + UI* const ret = createUI(); + d_lastUiDspPtr = nullptr; + d_lastUiWindow = nullptr; + return ret; +} +#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI + /* ------------------------------------------------------------------------------------------------------------ * UI */ @@ -157,7 +188,7 @@ uintptr_t UI::getNextWindowId() noexcept return g_nextWindowId; } # endif -#endif +#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI /* ------------------------------------------------------------------------------------------------------------ * DSP/Plugin Callbacks (optional) */ @@ -176,20 +207,7 @@ void UI::uiFileBrowserSelected(const char*) void UI::uiReshape(uint width, 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(width), static_cast(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast(width), static_cast(height)); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#else - // unused - (void)width; - (void)height; -#endif + Window::PrivateData::Fallback::onReshape(width, height); } /* ------------------------------------------------------------------------------------------------------------ diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 80fc4257..25c066bd 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -17,7 +17,7 @@ #ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED #define DISTRHO_UI_INTERNAL_HPP_INCLUDED -#include "../DistrhoUI.hpp" +#include "DistrhoUIPrivateData.hpp" #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "../extra/Sleep.hpp" @@ -46,153 +46,17 @@ extern Window* d_lastUiWindow; #endif // ----------------------------------------------------------------------- -// UI callbacks -typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); -typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); -typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); -typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); -typedef void (*setSizeFunc) (void* ptr, uint width, uint height); -typedef bool (*fileRequestFunc) (void* ptr, const char* key); - -// ----------------------------------------------------------------------- -// UI private data - -struct UI::PrivateData { - // DSP - double sampleRate; - uint32_t parameterOffset; -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - void* dspPtr; -#endif - - // UI - bool automaticallyScale; - bool resizeInProgress; - uint minWidth; - uint minHeight; - uint bgColor; - uint fgColor; - - // Callbacks - void* callbacksPtr; - editParamFunc editParamCallbackFunc; - setParamFunc setParamCallbackFunc; - setStateFunc setStateCallbackFunc; - sendNoteFunc sendNoteCallbackFunc; - setSizeFunc setSizeCallbackFunc; - fileRequestFunc fileRequestCallbackFunc; - - PrivateData() noexcept - : sampleRate(d_lastUiSampleRate), - parameterOffset(0), -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - dspPtr(d_lastUiDspPtr), -#endif - automaticallyScale(false), - resizeInProgress(false), - minWidth(0), - minHeight(0), - bgColor(0), - fgColor(0), - callbacksPtr(nullptr), - editParamCallbackFunc(nullptr), - setParamCallbackFunc(nullptr), - setStateCallbackFunc(nullptr), - sendNoteCallbackFunc(nullptr), - setSizeCallbackFunc(nullptr), - fileRequestCallbackFunc(nullptr) - { - DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); - -#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) - parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; -# if DISTRHO_PLUGIN_WANT_LATENCY - parameterOffset += 1; -# endif -#endif - -#ifdef DISTRHO_PLUGIN_TARGET_LV2 -# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) - parameterOffset += 1; -# if DISTRHO_PLUGIN_WANT_STATE - parameterOffset += 1; -# endif -# endif +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI +UI* createUiWrapper(void* dspPtr, uintptr_t winId, double scaleFactor, const char* bundlePath); +#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI +UI* createUiWrapper(void* dspPtr, Window* window); #endif - } - - void editParamCallback(const uint32_t rindex, const bool started) - { - if (editParamCallbackFunc != nullptr) - editParamCallbackFunc(callbacksPtr, rindex, started); - } - - void setParamCallback(const uint32_t rindex, const float value) - { - if (setParamCallbackFunc != nullptr) - setParamCallbackFunc(callbacksPtr, rindex, value); - } - - void setStateCallback(const char* const key, const char* const value) - { - if (setStateCallbackFunc != nullptr) - setStateCallbackFunc(callbacksPtr, key, value); - } - - void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) - { - if (sendNoteCallbackFunc != nullptr) - sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); - } - - void setSizeCallback(const uint width, const uint height) - { - if (setSizeCallbackFunc != nullptr) - setSizeCallbackFunc(callbacksPtr, width, height); - } - - bool fileRequestCallback(const char* key) - { - if (fileRequestCallbackFunc != nullptr) - return fileRequestCallbackFunc(callbacksPtr, key); - - // TODO use old style DPF dialog here - - return false; - } -}; // ----------------------------------------------------------------------- // Plugin Window, needed to take care of resize properly -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -static inline -UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath) -{ - d_lastUiDspPtr = dspPtr; - g_nextWindowId = winId; - g_nextScaleFactor = scaleFactor; - g_nextBundlePath = bundlePath; - UI* const ret = createUI(); - d_lastUiDspPtr = nullptr; - g_nextWindowId = 0; - g_nextScaleFactor = 1.0; - g_nextBundlePath = nullptr; - return ret; -} -#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -static inline -UI* createUiWrapper(void* const dspPtr, Window* const window) -{ - d_lastUiDspPtr = dspPtr; - d_lastUiWindow = window; - UI* const ret = createUI(); - d_lastUiDspPtr = nullptr; - d_lastUiWindow = nullptr; - return ret; -} - +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI class UIExporterWindow : public Window { public: @@ -284,7 +148,7 @@ public: #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)), #else - : glApp(), + : glApp(DISTRHO_UI_IS_STANDALONE), glWindow(glApp, winId, scaleFactor, dspPtr), fChangingSize(false), fUI(glWindow.getUI()), diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp new file mode 100644 index 00000000..be90aef2 --- /dev/null +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -0,0 +1,152 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2020 Filipe Coelho + * + * 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 DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED +#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED + +#include "../DistrhoUI.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Static data, see DistrhoUI.cpp + +extern double d_lastUiSampleRate; +extern void* d_lastUiDspPtr; + +// ----------------------------------------------------------------------- +// UI callbacks + +typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); +typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); +typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); +typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); +typedef void (*setSizeFunc) (void* ptr, uint width, uint height); +typedef bool (*fileRequestFunc) (void* ptr, const char* key); + +// ----------------------------------------------------------------------- +// UI private data + +struct UI::PrivateData { + // DSP + double sampleRate; + uint32_t parameterOffset; +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + void* dspPtr; +#endif + + // UI + bool automaticallyScale; + bool resizeInProgress; + uint minWidth; + uint minHeight; + uint bgColor; + uint fgColor; + + // Callbacks + void* callbacksPtr; + editParamFunc editParamCallbackFunc; + setParamFunc setParamCallbackFunc; + setStateFunc setStateCallbackFunc; + sendNoteFunc sendNoteCallbackFunc; + setSizeFunc setSizeCallbackFunc; + fileRequestFunc fileRequestCallbackFunc; + + PrivateData() noexcept + : sampleRate(d_lastUiSampleRate), + parameterOffset(0), +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + dspPtr(d_lastUiDspPtr), +#endif + automaticallyScale(false), + resizeInProgress(false), + minWidth(0), + minHeight(0), + bgColor(0), + fgColor(0), + callbacksPtr(nullptr), + editParamCallbackFunc(nullptr), + setParamCallbackFunc(nullptr), + setStateCallbackFunc(nullptr), + sendNoteCallbackFunc(nullptr), + setSizeCallbackFunc(nullptr), + fileRequestCallbackFunc(nullptr) + { + DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); + +#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) + parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; +# if DISTRHO_PLUGIN_WANT_LATENCY + parameterOffset += 1; +# endif +#endif + +#ifdef DISTRHO_PLUGIN_TARGET_LV2 +# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) + parameterOffset += 1; +# if DISTRHO_PLUGIN_WANT_STATE + parameterOffset += 1; +# endif +# endif +#endif + } + + void editParamCallback(const uint32_t rindex, const bool started) + { + if (editParamCallbackFunc != nullptr) + editParamCallbackFunc(callbacksPtr, rindex, started); + } + + void setParamCallback(const uint32_t rindex, const float value) + { + if (setParamCallbackFunc != nullptr) + setParamCallbackFunc(callbacksPtr, rindex, value); + } + + void setStateCallback(const char* const key, const char* const value) + { + if (setStateCallbackFunc != nullptr) + setStateCallbackFunc(callbacksPtr, key, value); + } + + void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + if (sendNoteCallbackFunc != nullptr) + sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); + } + + void setSizeCallback(const uint width, const uint height) + { + if (setSizeCallbackFunc != nullptr) + setSizeCallbackFunc(callbacksPtr, width, height); + } + + bool fileRequestCallback(const char* key) + { + if (fileRequestCallbackFunc != nullptr) + return fileRequestCallbackFunc(callbacksPtr, key); + + // TODO use old style DPF dialog here + + return false; + } +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED From 23db12f290be2fcafddc9468bcad7ca18f5e12bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2020 00:48:00 +0100 Subject: [PATCH 008/159] Fix wrong UI parameter offset for LV2. See #209 Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 2 +- distrho/src/DistrhoUIPrivateData.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index c2f2aa3c..088ffe1b 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -113,7 +113,7 @@ struct Plugin::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_LV2 -# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) +# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; # if DISTRHO_PLUGIN_WANT_STATE parameterOffset += 1; diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index be90aef2..7f871e09 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -95,7 +95,7 @@ struct UI::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_LV2 -# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) +# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; # if DISTRHO_PLUGIN_WANT_STATE parameterOffset += 1; From dce7676eb83ede14ff92d76beddddc30e3df674e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2020 07:33:52 +0100 Subject: [PATCH 009/159] Restructure of code for new pugl, WIP Signed-off-by: falkTX --- Makefile | 6 +- dgl/Widget.hpp | 3 + dgl/Window.hpp | 42 +- dgl/src/Application.cpp | 2 +- dgl/src/ApplicationPrivateData.hpp | 3 + dgl/src/ImageWidgets.cpp | 4 +- dgl/src/Widget.cpp | 2 + dgl/src/Window.cpp | 141 ++-- dgl/src/WindowPrivateData.cpp | 329 +++++++- dgl/src/WindowPrivateData.hpp | 1133 ++++++---------------------- dgl/src/pugl-extra/extras.c | 29 + dgl/src/pugl-extra/extras.h | 50 ++ dgl/src/pugl-extra/haiku.cpp | 81 ++ dgl/src/pugl-extra/haiku.h | 35 + dgl/src/pugl-extra/mac.m | 48 ++ dgl/src/pugl-extra/win.c | 118 +++ dgl/src/pugl-extra/x11.c | 111 +++ distrho/src/DistrhoPluginVST.cpp | 1 + distrho/src/DistrhoUI.cpp | 2 - distrho/src/DistrhoUIDSSI.cpp | 1 + distrho/src/DistrhoUIInternal.hpp | 6 +- distrho/src/DistrhoUILV2.cpp | 3 +- 22 files changed, 1171 insertions(+), 979 deletions(-) create mode 100644 dgl/src/pugl-extra/extras.c create mode 100644 dgl/src/pugl-extra/extras.h create mode 100644 dgl/src/pugl-extra/haiku.cpp create mode 100644 dgl/src/pugl-extra/haiku.h create mode 100644 dgl/src/pugl-extra/mac.m create mode 100644 dgl/src/pugl-extra/win.c create mode 100644 dgl/src/pugl-extra/x11.c diff --git a/Makefile b/Makefile index e573a456..f57cc1e9 100644 --- a/Makefile +++ b/Makefile @@ -27,9 +27,9 @@ examples: dgl $(MAKE) all -C examples/Parameters $(MAKE) all -C examples/States -ifeq ($(HAVE_CAIRO),true) - $(MAKE) all -C examples/CairoUI -endif +# ifeq ($(HAVE_CAIRO),true) +# $(MAKE) all -C examples/CairoUI +# endif ifneq ($(MACOS_OR_WINDOWS),true) # ExternalUI is WIP diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index f8d7139d..a54c3de0 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -301,11 +301,14 @@ public: */ void setAbsolutePos(const Point& pos) noexcept; +#if 0 + // TODO: should we remove this? /** Get this widget's window application. Same as calling getParentWindow().getApp(). */ Application& getParentApp() const noexcept; +#endif /** Get parent window, as passed in the constructor. diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 7f4e16e8..1fa7f819 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2020 Filipe Coelho * * 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 @@ -77,8 +77,8 @@ public: #endif // DGL_FILE_BROWSER_DISABLED explicit Window(Application& app); - explicit Window(Application& app, Window& parent); - explicit Window(Application& app, intptr_t parentId, double scaling, bool resizable); + explicit Window(Window& transientParentWindow); + explicit Window(Application& app, uintptr_t parentWindowHandle, double scaling, bool resizable); virtual ~Window(); void show(); @@ -88,6 +88,7 @@ public: void focus(); void repaint() noexcept; + void repaint(const Rectangle& rect) noexcept; #ifndef DGL_FILE_BROWSER_DISABLED bool openFileBrowser(const FileBrowserOptions& options); @@ -96,10 +97,13 @@ public: bool isEmbed() const noexcept; bool isVisible() const noexcept; - void setVisible(bool yesNo); + void setVisible(bool visible); bool isResizable() const noexcept; - void setResizable(bool yesNo); + void setResizable(bool resizable); + + bool getIgnoringKeyRepeat() const noexcept; + void setIgnoringKeyRepeat(bool ignore) noexcept; uint getWidth() const noexcept; uint getHeight() const noexcept; @@ -115,11 +119,20 @@ public: double getScaling() const noexcept; - bool getIgnoringKeyRepeat() const noexcept; - void setIgnoringKeyRepeat(bool ignore) noexcept; - +#if 0 + // should this be removed? Application& getApp() const noexcept; - uintptr_t getWindowId() const noexcept; +#endif + + /** + Get the "native" window handle. + Returned value depends on the platform: + - HaikuOS: This is a pointer to a `BView`. + - MacOS: This is a pointer to an `NSView*`. + - Windows: This is a `HWND`. + - Everything else: This is an [X11] `Window`. + */ + uintptr_t getNativeWindowHandle() const noexcept; const GraphicsContext& getGraphicsContext() const noexcept; @@ -157,7 +170,16 @@ private: bool handlePluginKeyboard(const bool press, const uint key); bool handlePluginSpecial(const bool press, const Key key); - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) + // Prevent copies +#ifdef DISTRHO_PROPER_CPP11_SUPPORT + Window& operator=(Window&) = delete; + Window& operator=(const Window&) = delete; +#else + Window& operator=(Window&); + Window& operator=(const Window&); +#endif + + DISTRHO_LEAK_DETECTOR(Window); }; // ----------------------------------------------------------------------- diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index db99e746..0ab17764 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -35,7 +35,7 @@ void Application::idle() void Application::exec(const uint idleTime) { - while (!pData->isQuitting) + while (! pData->isQuitting) pData->idle(idleTime); } diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 477f5bb6..ad64e4f9 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -47,6 +47,8 @@ struct Application::PrivateData { { puglSetWorldHandle(world, this); + // puglSetLogLevel(world, PUGL_LOG_LEVEL_DEBUG); + // TODO puglSetClassName } @@ -58,6 +60,7 @@ struct Application::PrivateData { windows.clear(); idleCallbacks.clear(); + d_stdout("calling puglFreeWorld"); puglFreeWorld(world); } diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index de94ca02..f42f066d 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -27,7 +27,7 @@ START_NAMESPACE_DGL #ifndef DISTRHO_OS_HAIKU ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image) - : Window(parent.getApp(), parent), + : Window(parent), Widget((Window&)*this), fImgBackground(image) { @@ -37,7 +37,7 @@ ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image) } ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) - : Window(widget->getParentApp(), widget->getParentWindow()), + : Window(widget->getParentWindow()), Widget((Window&)*this), fImgBackground(image) { diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 4ef69b8e..89083cab 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -179,10 +179,12 @@ void Widget::setAbsolutePos(const Point& pos) noexcept pData->parent.repaint(); } +#if 0 Application& Widget::getParentApp() const noexcept { return pData->parent.getApp(); } +#endif Window& Widget::getParentWindow() const noexcept { diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index dac1b9f9..f424be99 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -14,34 +14,27 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -// we need this for now -//#define PUGL_GRAB_FOCUS 1 - -// #include "../Base.hpp" #include "WindowPrivateData.hpp" -// #include "../../distrho/extra/String.hpp" - START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Window Window::Window(Application& app) - : pData(new PrivateData(app, this)) {} + : pData(new PrivateData(app.pData, this)) {} -Window::Window(Application& app, Window& parent) - : pData(new PrivateData(app, this, parent)) {} +Window::Window(Window& transientParentWindow) + : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} -Window::Window(Application& app, intptr_t parentId, double scaling, bool resizable) - : pData(new PrivateData(app, this, parentId, scaling, resizable)) {} +Window::Window(Application& app, const uintptr_t parentWindowHandle, const double scaling, const bool resizable) + : pData(new PrivateData(app.pData, this, parentWindowHandle, scaling, resizable)) {} Window::~Window() { delete pData; } -#if 0 void Window::show() { pData->setVisible(true); @@ -57,14 +50,19 @@ void Window::close() pData->close(); } +#if 0 void Window::exec(bool lockWait) { pData->exec(lockWait); } +#endif void Window::focus() { - pData->focus(); + if (! pData->fUsingEmbed) + puglRaiseWindow(pData->fView); + + puglGrabFocus(pData->fView); } void Window::repaint() noexcept @@ -72,6 +70,17 @@ void Window::repaint() noexcept puglPostRedisplay(pData->fView); } +void Window::repaint(const Rectangle& rect) noexcept +{ + const PuglRect prect = { + static_cast(rect.getX()), + static_cast(rect.getY()), + static_cast(rect.getWidth()), + static_cast(rect.getHeight()), + }; + puglPostRedisplayRect(pData->fView, prect); +} + bool Window::isEmbed() const noexcept { return pData->fUsingEmbed; @@ -82,88 +91,107 @@ bool Window::isVisible() const noexcept return pData->fVisible; } -void Window::setVisible(bool yesNo) +void Window::setVisible(const bool visible) { - pData->setVisible(yesNo); + pData->setVisible(visible); } bool Window::isResizable() const noexcept { - return pData->fResizable; + return puglGetViewHint(pData->fView, PUGL_RESIZABLE) == PUGL_TRUE; } -void Window::setResizable(bool yesNo) +void Window::setResizable(const bool resizable) { - pData->setResizable(yesNo); + DISTRHO_SAFE_ASSERT_RETURN(pData->fUsingEmbed,); + if (pData->fUsingEmbed) + { + DGL_DBG("Window setResizable cannot be called when embedded\n"); + return; + } + + DGL_DBG("Window setResizable called\n"); + + puglSetViewHint(pData->fView, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); +#ifdef DISTRHO_OS_WINDOWS + puglWin32SetWindowResizable(pData->fView, resizable); +#endif } -void Window::setGeometryConstraints(uint width, uint height, bool aspect) +bool Window::getIgnoringKeyRepeat() const noexcept { - pData->setGeometryConstraints(width, height, aspect); + return puglGetViewHint(pData->fView, PUGL_IGNORE_KEY_REPEAT) == PUGL_TRUE; } -uint Window::getWidth() const noexcept +void Window::setIgnoringKeyRepeat(const bool ignore) noexcept { - return pData->fWidth; + puglSetViewHint(pData->fView, PUGL_IGNORE_KEY_REPEAT, ignore); } -uint Window::getHeight() const noexcept +void Window::setGeometryConstraints(const uint width, const uint height, bool aspect) { - return pData->fHeight; + // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? + DISTRHO_SAFE_ASSERT_RETURN(isResizable(),); + + puglUpdateGeometryConstraints(pData->fView, width, height, aspect); } -Size Window::getSize() const noexcept +uint Window::getWidth() const noexcept { - return Size(pData->fWidth, pData->fHeight); + return puglGetFrame(pData->fView).width; } -void Window::setSize(uint width, uint height) +uint Window::getHeight() const noexcept { - pData->setSize(width, height); + return puglGetFrame(pData->fView).height; } -void Window::setSize(Size size) +Size Window::getSize() const noexcept { - pData->setSize(size.getWidth(), size.getHeight()); + const PuglRect rect = puglGetFrame(pData->fView); + return Size(rect.width, rect.height); } -const char* Window::getTitle() const noexcept +void Window::setSize(const uint width, const uint height) { - return pData->getTitle(); + DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); + + puglSetWindowSize(pData->fView, width, height); } -void Window::setTitle(const char* title) +void Window::setSize(const Size size) { - pData->setTitle(title); + setSize(size.getWidth(), size.getHeight()); } -void Window::setTransientWinId(uintptr_t winId) +const char* Window::getTitle() const noexcept { - pData->setTransientWinId(winId); + return puglGetWindowTitle(pData->fView); } -double Window::getScaling() const noexcept +void Window::setTitle(const char* const title) { - return pData->getScaling(); + puglSetWindowTitle(pData->fView, title); } -bool Window::getIgnoringKeyRepeat() const noexcept +void Window::setTransientWinId(const uintptr_t winId) { - return pData->getIgnoringKeyRepeat(); + puglSetTransientFor(pData->fView, winId); } -void Window::setIgnoringKeyRepeat(bool ignore) noexcept +double Window::getScaling() const noexcept { - pData->setIgnoringKeyRepeat(ignore); + return pData->fScaling; } -#endif +#if 0 Application& Window::getApp() const noexcept { return pData->fApp; } +#endif -uintptr_t Window::getWindowId() const noexcept +uintptr_t Window::getNativeWindowHandle() const noexcept { return puglGetNativeWindow(pData->fView); } @@ -177,10 +205,13 @@ const GraphicsContext& Window::getGraphicsContext() const noexcept #endif return context; } +#endif void Window::_setAutoScaling(double scaling) noexcept { - pData->setAutoScaling(scaling); + DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); + + pData->fAutoScaling = scaling; } void Window::_addWidget(Widget* const widget) @@ -195,9 +226,8 @@ void Window::_removeWidget(Widget* const widget) void Window::_idle() { - pData->idle(); + pData->windowSpecificIdle(); } -#endif // ----------------------------------------------------------------------- @@ -205,14 +235,14 @@ void Window::addIdleCallback(IdleCallback* const callback) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) - pData->fApp.pData->idleCallbacks.push_back(callback); + pData->fAppData->idleCallbacks.push_back(callback); } void Window::removeIdleCallback(IdleCallback* const callback) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) - pData->fApp.pData->idleCallbacks.remove(callback); + pData->fAppData->idleCallbacks.remove(callback); } // ----------------------------------------------------------------------- @@ -242,21 +272,20 @@ void Window::fileBrowserSelected(const char*) } #endif -#if 0 bool Window::handlePluginKeyboard(const bool press, const uint key) { - return pData->handlePluginKeyboard(press, key); + // TODO + return false; + // return pData->handlePluginKeyboard(press, key); } bool Window::handlePluginSpecial(const bool press, const Key key) { - return pData->handlePluginSpecial(press, key); + // TODO + return false; + // return pData->handlePluginSpecial(press, key); } -#endif // ----------------------------------------------------------------------- END_NAMESPACE_DGL - -#undef DBG -#undef DBGF diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 7b2d6d6c..08ef6333 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -27,6 +27,7 @@ extern "C" { #include "pugl-upstream/src/implementation.c" +#include "pugl-extra/extras.c" } #if defined(DISTRHO_OS_HAIKU) @@ -41,18 +42,336 @@ extern "C" { # undef max # undef min #else -# include -# include +# define DGL_PUGL_USING_X11 extern "C" { # include "pugl-upstream/src/x11.c" +// # ifdef DGL_CAIRO +// # include "pugl-upstream/src/x11_cairo.c" +// # endif +# ifdef DGL_OPENGL +# include "pugl-upstream/src/x11_gl.c" +# endif +# define PUGL_DETAIL_X11_H_INCLUDED +# include "pugl-extra/x11.c" } #endif +#include +#include +#include +#include +#include +#include + START_NAMESPACE_DGL // ----------------------------------------------------------------------- -void DGL::Window::PrivateData::Fallback::onDisplayBefore() +void Window::PrivateData::init(const bool resizable) +{ + if (fSelf == nullptr || fView == nullptr) + { + DGL_DBG("Failed!\n"); + return; + } + +// #ifdef DGL_CAIRO +// puglSetBackend(fView, puglCairoBackend()); +// #endif +#ifdef DGL_OPENGL + puglSetBackend(fView, puglGlBackend()); +#endif + + puglSetHandle(fView, this); + puglSetViewHint(fView, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); + puglSetViewHint(fView, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); + puglSetEventFunc(fView, puglEventCallback); +// #ifndef DGL_FILE_BROWSER_DISABLED +// puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); +// #endif + + fAppData->windows.push_back(fSelf); + + DGL_DBG("Success!\n"); +} + +void Window::PrivateData::setVisible(const bool visible) +{ + if (fVisible == visible) + { + DGL_DBG("Window setVisible matches current state, ignoring request\n"); + return; + } + if (fUsingEmbed) + { + DGL_DBG("Window setVisible cannot be called when embedded\n"); + return; + } + + DGL_DBG("Window setVisible called\n"); + + fVisible = visible; + + if (visible) + { +#if 0 && defined(DISTRHO_OS_MAC) + if (mWindow != nullptr) + { + if (mParentWindow != nullptr) + [mParentWindow addChildWindow:mWindow + ordered:NSWindowAbove]; + } +#endif + + if (fFirstInit) + { + puglRealize(fView); +#ifdef DISTRHO_OS_WINDOWS + puglShowWindowCentered(fView); +#else + puglShowWindow(fView); +#endif + fAppData->oneWindowShown(); + fFirstInit = false; + } + else + { +#ifdef DISTRHO_OS_WINDOWS + puglWin32RestoreWindow(fView); +#else + puglShowWindow(fView); +#endif + } + } + else + { +#if 0 && defined(DISTRHO_OS_MAC) + if (mWindow != nullptr) + { + if (mParentWindow != nullptr) + [mParentWindow removeChildWindow:mWindow]; + } +#endif + + puglHideWindow(fView); + +// if (fModal.enabled) +// exec_fini(); + } +} + +void Window::PrivateData::windowSpecificIdle() +{ +#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->windowSpecificIdle(); +} + +// ----------------------------------------------------------------------- + +static inline int +printModifiers(const uint32_t mods) +{ + return fprintf(stderr, "Modifiers:%s%s%s%s\n", + (mods & PUGL_MOD_SHIFT) ? " Shift" : "", + (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", + (mods & PUGL_MOD_ALT) ? " Alt" : "", + (mods & PUGL_MOD_SUPER) ? " Super" : ""); +} + +static inline int +printEvent(const PuglEvent* event, const char* prefix, const bool verbose) +{ +#define FFMT "%6.1f" +#define PFMT FFMT " " FFMT +#define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + + switch (event->type) { + case PUGL_NOTHING: + return 0; + case PUGL_KEY_PRESS: + return PRINT("%sKey press code %3u key U+%04X\n", + prefix, + event->key.keycode, + event->key.key); + case PUGL_KEY_RELEASE: + return PRINT("%sKey release code %3u key U+%04X\n", + prefix, + event->key.keycode, + event->key.key); + case PUGL_TEXT: + return PRINT("%sText entry code %3u char U+%04X (%s)\n", + prefix, + event->text.keycode, + event->text.character, + event->text.string); + case PUGL_BUTTON_PRESS: + case PUGL_BUTTON_RELEASE: + return (PRINT("%sMouse %u %s at " PFMT " ", + prefix, + event->button.button, + (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ", + event->button.x, + event->button.y) + + printModifiers(event->scroll.state)); + case PUGL_SCROLL: + return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ", + prefix, + event->scroll.dx, + event->scroll.dy, + event->scroll.x, + event->scroll.y) + + printModifiers(event->scroll.state)); + case PUGL_POINTER_IN: + return PRINT("%sMouse enter at " PFMT "\n", + prefix, + event->crossing.x, + event->crossing.y); + case PUGL_POINTER_OUT: + return PRINT("%sMouse leave at " PFMT "\n", + prefix, + event->crossing.x, + event->crossing.y); + case PUGL_FOCUS_IN: + return PRINT("%sFocus in %i\n", + prefix, + event->focus.mode); + case PUGL_FOCUS_OUT: + return PRINT("%sFocus out %i\n", + prefix, + event->focus.mode); + case PUGL_CLIENT: + return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n", + prefix, + event->client.data1, + event->client.data2); + case PUGL_TIMER: + return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id); + default: + break; + } + + if (verbose) { + switch (event->type) { + case PUGL_CREATE: + return fprintf(stderr, "%sCreate\n", prefix); + case PUGL_DESTROY: + return fprintf(stderr, "%sDestroy\n", prefix); + case PUGL_MAP: + return fprintf(stderr, "%sMap\n", prefix); + case PUGL_UNMAP: + return fprintf(stderr, "%sUnmap\n", prefix); + case PUGL_UPDATE: + return fprintf(stderr, "%sUpdate\n", prefix); + case PUGL_CONFIGURE: + return PRINT("%sConfigure " PFMT " " PFMT "\n", + prefix, + event->configure.x, + event->configure.y, + event->configure.width, + event->configure.height); + case PUGL_EXPOSE: + return PRINT("%sExpose " PFMT " " PFMT "\n", + prefix, + event->expose.x, + event->expose.y, + event->expose.width, + event->expose.height); + case PUGL_CLOSE: + return PRINT("%sClose\n", prefix); + case PUGL_MOTION: + return PRINT("%sMouse motion at " PFMT "\n", + prefix, + event->motion.x, + event->motion.y); + default: + return PRINT("%sUnknown event type %d\n", prefix, (int)event->type); + } + } + +#undef PRINT +#undef PFMT +#undef FFMT + + return 0; +} + +PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) +{ + printEvent(event, "", true); + Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); + + switch (event->type) + { + ///< No event + case PUGL_NOTHING: + break; + + ///< View created, a #PuglEventCreate + case PUGL_CREATE: +#ifdef DGL_PUGL_USING_X11 + if (! pData->fUsingEmbed) + puglExtraSetWindowTypeAndPID(view); +#endif + break; + + ///< View destroyed, a #PuglEventDestroy + case PUGL_DESTROY: + break; + + ///< View moved/resized, a #PuglEventConfigure + case PUGL_CONFIGURE: + pData->onPuglReshape(event->configure.width, event->configure.height); + break; + + case PUGL_MAP: ///< View made visible, a #PuglEventMap + case PUGL_UNMAP: ///< View made invisible, a #PuglEventUnmap + break; + + ///< View ready to draw, a #PuglEventUpdate + case PUGL_UPDATE: + break; + + ///< View must be drawn, a #PuglEventExpose + case PUGL_EXPOSE: + pData->onPuglDisplay(); + break; + + ///< View will be closed, a #PuglEventClose + case PUGL_CLOSE: + pData->onPuglClose(); + break; + + case PUGL_FOCUS_IN: ///< Keyboard focus entered view, a #PuglEventFocus + case PUGL_FOCUS_OUT: ///< Keyboard focus left view, a #PuglEventFocus + case PUGL_KEY_PRESS: ///< Key pressed, a #PuglEventKey + case PUGL_KEY_RELEASE: ///< Key released, a #PuglEventKey + case PUGL_TEXT: ///< Character entered, a #PuglEventText + case PUGL_POINTER_IN: ///< Pointer entered view, a #PuglEventCrossing + case PUGL_POINTER_OUT: ///< Pointer left view, a #PuglEventCrossing + case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton + case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton + case PUGL_MOTION: ///< Pointer moved, a #PuglEventMotion + case PUGL_SCROLL: ///< Scrolled, a #PuglEventScroll + case PUGL_CLIENT: ///< Custom client message, a #PuglEventClient + case PUGL_TIMER: ///< Timer triggered, a #PuglEventTimer + break; + } + + return PUGL_SUCCESS; +} + +// ----------------------------------------------------------------------- + +void Window::PrivateData::Fallback::onDisplayBefore() { #ifdef DGL_OPENGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -60,11 +379,11 @@ void DGL::Window::PrivateData::Fallback::onDisplayBefore() #endif } -void DGL::Window::PrivateData::Fallback::onDisplayAfter() +void Window::PrivateData::Fallback::onDisplayAfter() { } -void DGL::Window::PrivateData::Fallback::onReshape(const uint width, const uint height) +void Window::PrivateData::Fallback::onReshape(const uint width, const uint height) { #ifdef DGL_OPENGL glEnable(GL_BLEND); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index c917ffe2..004875e7 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,9 +18,12 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" + #include "ApplicationPrivateData.hpp" +#include "WidgetPrivateData.hpp" #include "pugl-upstream/include/pugl/pugl.h" +#include "pugl-extra/extras.h" #include @@ -30,14 +33,16 @@ #define FOR_EACH_WIDGET_INV(rit) \ for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) +#define DGL_DEBUG_EVENTS + #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); +# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); +# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); +# define DGL_DBGF std::fflush(stderr); #else -# define DBG(msg) -# define DBGp(...) -# define DBGF +# define DGL_DBG(msg) +# define DGL_DBGp(...) +# define DGL_DBGF #endif START_NAMESPACE_DGL @@ -45,13 +50,66 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- struct Window::PrivateData { - Application& fApp; - Window* fSelf; - PuglView* fView; + typedef Application::PrivateData AppData; + + AppData* const fAppData; + Window* const fSelf; + PuglView* const fView; // this one depends on build type // GraphicsContext fContext; + bool fFirstInit; + bool fVisible; + bool fUsingEmbed; + double fScaling; + double fAutoScaling; + std::list 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; +#if defined(DISTRHO_OS_MAC) +// NSView* 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 +#endif + // Fallback build-specific Window functions struct Fallback { static void onDisplayBefore(); @@ -59,6 +117,7 @@ struct Window::PrivateData { static void onReshape(uint width, uint height); }; + /* PrivateData(Application& app, Window* const self) : fApp(app), fSelf(self) @@ -76,301 +135,99 @@ struct Window::PrivateData { fSelf(self) { } - -#ifdef DISTRHO_DEFINES_H_INCLUDED - friend class DISTRHO_NAMESPACE::UI; -#endif - - 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), + */ + PrivateData(Application::PrivateData* const appData, Window* const self) + : fAppData(appData), fSelf(self), - fView(puglInit()), + fView(puglNewView(appData->world)), 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 + fModal() { - DBG("Creating window without parent..."); DBGF; + DGL_DBG("Creating window without parent..."); DGL_DBGF; init(); } - PrivateData(Application& app, Window* const self, Window& parent) - : fApp(app), + PrivateData(Application::PrivateData* const appData, Window* const self, Window& transientWindow) + : fAppData(appData), fSelf(self), - fView(puglInit()), + fView(puglNewView(appData->world)), 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 + fModal(transientWindow.pData) { - DBG("Creating window with parent..."); DBGF; + DGL_DBG("Creating window with parent..."); DGL_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 + puglSetTransientFor(fView, transientWindow.getNativeWindowHandle()); } - PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable) - : fApp(app), + PrivateData(Application::PrivateData* const appData, Window* const self, + const uintptr_t parentWindowHandle, + const double scaling, + const bool resizable) + : fAppData(appData), fSelf(self), - fView(puglInit()), + fView(puglNewView(appData->world)), fFirstInit(true), - fVisible(parentId != 0), - fResizable(resizable), - fUsingEmbed(parentId != 0), - fWidth(1), - fHeight(1), + fVisible(parentWindowHandle != 0), + fUsingEmbed(parentWindowHandle != 0), 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 + fModal() { if (fUsingEmbed) { - DBG("Creating embedded window..."); DBGF; - puglInitWindowParent(fView, parentId); + DGL_DBG("Creating embedded window..."); DGL_DBGF; + puglSetParentWindow(fView, parentWindowHandle); } else { - DBG("Creating window without parent..."); DBGF; + DGL_DBG("Creating window without parent..."); DGL_DBGF; } - init(); + init(resizable); if (fUsingEmbed) { - DBG("NOTE: Embed window is always visible and non-resizable\n"); - puglShowWindow(fView); - fApp.pData->oneShown(); - fFirstInit = false; + DGL_DBG("NOTE: Embed window is always visible and non-resizable\n"); +// puglShowWindow(fView); +// fAppData->oneWindowShown(); +// fFirstInit = false; } } - void init() - { - if (fSelf == nullptr || fView == nullptr) - { - DBG("Failed!\n"); - return; - } - - puglInitUserResizable(fView, fResizable); - puglInitWindowSize(fView, static_cast(fWidth), static_cast(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; + DGL_DBG("Destroying window..."); DGL_DBGF; +#if 0 if (fModal.enabled) { exec_fini(); close(); } +#endif fWidgets.clear(); if (fUsingEmbed) { - puglHideWindow(fView); - fApp.pData->oneHidden(); +// puglHideWindow(fView); +// fAppData->oneWindowHidden(); } 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 + fAppData->windows.remove(fSelf); #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) if (fOpenFilePanel) @@ -385,561 +242,194 @@ struct Window::PrivateData { } #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(width), static_cast(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(width); - sizeHints.height = static_cast(height); - sizeHints.min_width = static_cast(width); - sizeHints.min_height = static_cast(height); - sizeHints.max_width = static_cast(width); - sizeHints.max_height = static_cast(height); - - XSetWMNormalHints(xDisplay, xWindow, &sizeHints); - } - - XResizeWindow(xDisplay, xWindow, width, height); - - if (! forced) - XFlush(xDisplay); -#endif + if (fView != nullptr) + puglFreeView(fView); - puglPostRedisplay(fView); + DGL_DBG("Success!\n"); } // ------------------------------------------------------------------- + // stuff that uses pugl internals or build-specific things - const char* getTitle() const noexcept - { - static const char* const kFallback = ""; + void init(const bool resizable = false); + void setVisible(const bool visible); + void windowSpecificIdle(); + static PuglStatus puglEventCallback(PuglView* const view, const PuglEvent* const event); - return fTitle != nullptr ? fTitle : kFallback; - } + // ------------------------------------------------------------------- - void setTitle(const char* const title) + void close() { - DBGp("Window setTitle \"%s\"\n", title); + DGL_DBG("Window close\n"); - if (fTitle != nullptr) - std::free(fTitle); + if (fUsingEmbed) + return; - fTitle = strdup(title); + setVisible(false); -#if defined(DISTRHO_OS_HAIKU) - if (bWindow != nullptr&& bWindow->LockLooper()) - { - bWindow->SetTitle(title); - bWindow->UnlockLooper(); - } -#elif defined(DISTRHO_OS_MAC) - if (mWindow != nullptr) + if (! fFirstInit) { - NSString* titleString = [[NSString alloc] - initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; - - [mWindow setTitle:titleString]; + fAppData->oneWindowHidden(); + fFirstInit = true; } -#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 + void addWidget(Widget* const widget) { - return fScaling; + fWidgets.push_back(widget); } - void setAutoScaling(const double scaling) noexcept + void removeWidget(Widget* const widget) { - DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); - - fAutoScaling = scaling; + fWidgets.remove(widget); } // ------------------------------------------------------------------- - bool getIgnoringKeyRepeat() const noexcept + void onPuglClose() { - return fView->ignoreKeyRepeat; - } + DGL_DBG("PUGL: onClose\n"); - void setIgnoringKeyRepeat(bool ignore) noexcept - { - puglIgnoreKeyRepeat(fView, ignore); - } +// if (fModal.enabled) +// exec_fini(); - // ------------------------------------------------------------------- + fSelf->onClose(); - void addWidget(Widget* const widget) - { - fWidgets.push_back(widget); + if (fModal.childFocus != nullptr) + fModal.childFocus->fSelf->onClose(); + + close(); } - void removeWidget(Widget* const widget) + void onPuglDisplay() { - fWidgets.remove(widget); + fSelf->onDisplayBefore(); + + if (fWidgets.size() != 0) + { + const PuglRect rect = puglGetFrame(fView); + const int width = rect.width; + const int height = rect.height; + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->pData->display(width, height, fAutoScaling, false); + } + } + + fSelf->onDisplayAfter(); } - void idle() + void onPuglReshape(const int width, const int height) { - puglProcessEvents(fView); + DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); + + DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); -#ifdef DISTRHO_OS_HAIKU - if (bApplication != nullptr) + fSelf->onReshape(width, height); + + FOR_EACH_WIDGET(it) { - // bApplication->Lock(); - // bApplication->Loop(); - // bApplication->Unlock(); + Widget* const widget(*it); + + if (widget->pData->needsFullViewport) + widget->setSize(width, height); } + } + + // ------------------------------------------------------------------- + +#ifdef DISTRHO_DEFINES_H_INCLUDED + friend class DISTRHO_NAMESPACE::UI; #endif + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; -#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]; +END_NAMESPACE_DGL + + +#if 0 +// ----------------------------------------------------------------------- +// Window Private + +struct Window::PrivateData { + // ------------------------------------------------------------------- - if (event == nil) - break; + void exec(const bool lockWait) + { + DBG("Window exec\n"); + exec_init(); - [NSApp sendEvent: event]; + if (lockWait) + { + for (; fVisible && fModal.enabled;) + { + idle(); + d_msleep(10); } - [pool release]; + exec_fini(); } -#endif - -#if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) - if (fSelectedFile.isNotEmpty()) + else { - char* const buffer = fSelectedFile.getAndReleaseBuffer(); - fView->fileSelectedFunc(fView, buffer); - std::free(buffer); + idle(); } -#endif - - if (fModal.enabled && fModal.parent != nullptr) - fModal.parent->idle(); } // ------------------------------------------------------------------- - void onPuglDisplay() + void exec_init() { - fSelf->onDisplayBefore(); + DBG("Window modal loop starting..."); DBGF; + DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true)); - FOR_EACH_WIDGET(it) + 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) { - Widget* const widget(*it); - widget->pData->display(fWidth, fHeight, fAutoScaling, false); + 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 } - fSelf->onDisplayAfter(); + DBG("Ok\n"); } + // ------------------------------------------------------------------- + + // ------------------------------------------------------------------- + int onPuglKeyboard(const bool press, const uint key) { DBGp("PUGL: onKeyboard : %i %i\n", press, key); @@ -1077,42 +567,6 @@ struct Window::PrivateData { } } - 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(width); - fHeight = static_cast(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) @@ -1232,124 +686,11 @@ struct Window::PrivateData { // ------------------------------------------------------------------- - 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 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* 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)); - } - - 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 + +// #undef DGL_DBG +// #undef DGL_DBGF + +#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED diff --git a/dgl/src/pugl-extra/extras.c b/dgl/src/pugl-extra/extras.c new file mode 100644 index 00000000..01e90192 --- /dev/null +++ b/dgl/src/pugl-extra/extras.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2012-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file extras.c pugl extra implementations for DPF. +*/ + +#include "extras.h" + +#include "../pugl-upstream/src/implementation.h" + +const char* +puglGetWindowTitle(const PuglView* view) +{ + return view->title; +} diff --git a/dgl/src/pugl-extra/extras.h b/dgl/src/pugl-extra/extras.h new file mode 100644 index 00000000..f9ff4d61 --- /dev/null +++ b/dgl/src/pugl-extra/extras.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2012-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file pugl.h pugl extra API for DPF. +*/ + +#ifndef PUGL_EXTRAS_PUGL_H +#define PUGL_EXTRAS_PUGL_H + +#include "../pugl-upstream/include/pugl/pugl.h" + +PUGL_BEGIN_DECLS + +PUGL_API const char* +puglGetWindowTitle(const PuglView* view); + +PUGL_API int +puglGetViewHint(const PuglView* view, PuglViewHint hint); + +PUGL_API void +puglRaiseWindow(PuglView* view); + +PUGL_API void +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); + +PUGL_API void +puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); + +#ifdef DISTRHO_OS_WINDOWS +PUGL_API void +puglWin32SetWindowResizable(PuglView* view, bool resizable); +#endif + +PUGL_END_DECLS + +#endif // PUGL_EXTRAS_PUGL_H diff --git a/dgl/src/pugl-extra/haiku.cpp b/dgl/src/pugl-extra/haiku.cpp new file mode 100644 index 00000000..b01b6629 --- /dev/null +++ b/dgl/src/pugl-extra/haiku.cpp @@ -0,0 +1,81 @@ +/* + Copyright 2012-2019 David Robillard + Copyright 2019-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file mac.cpp HaikuOS implementation. +*/ + +#include "haiku.h" + +#include "pugl/detail/implementation.h" + +PuglStatus +puglGrabFocus(PuglView* view) +{ + view->impl->bView->MakeFocus(true); + return PUGL_SUCCESS; +} + +// extras follow + +void +puglRaiseWindow(PuglView* view) +{ +} + +void +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +{ + 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 +} + +void setVisible(const bool yesNo) +{ + 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(); + } +} diff --git a/dgl/src/pugl-extra/haiku.h b/dgl/src/pugl-extra/haiku.h new file mode 100644 index 00000000..9930a8df --- /dev/null +++ b/dgl/src/pugl-extra/haiku.h @@ -0,0 +1,35 @@ +/* + Copyright 2012-2019 David Robillard + Copyright 2019-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file haiku.h Shared definitions for HaikuOS implementation. +*/ + +#include "pugl/pugl.h" + +#include +#include +// using? interface/ + +struct PuglWorldInternalsImpl { + BApplication* app; +}; + +struct PuglInternalsImpl { + BViewType* view; + BWindow* window; +}; diff --git a/dgl/src/pugl-extra/mac.m b/dgl/src/pugl-extra/mac.m new file mode 100644 index 00000000..853ac246 --- /dev/null +++ b/dgl/src/pugl-extra/mac.m @@ -0,0 +1,48 @@ +/* + Copyright (C) 2012-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file mac.m MacOS extra implementation for DPF. +*/ + +#include "pugl/detail/mac.h" + +void +puglRaiseWindow(PuglView* view) +{ +} + +void +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +{ + // NOTE: pugl mac code does nothing with x and y + const PuglRect frame = { 0.0, 0.0, (double)width, (double)height }; + puglSetFrame(view, frame); +} + +void +puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) +{ + // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio + view->minWidth = width; + view->minHeight = height; + + [view->impl->window setContentMinSize:sizePoints(view, width, height)]; + + if (aspect) { + [view->impl->window setContentAspectRatio:sizePoints(view, width, height)]; + } +} diff --git a/dgl/src/pugl-extra/win.c b/dgl/src/pugl-extra/win.c new file mode 100644 index 00000000..dca0db02 --- /dev/null +++ b/dgl/src/pugl-extra/win.c @@ -0,0 +1,118 @@ +/* + Copyright (C) 2012-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file win.c Windows extra implementation for DPF. +*/ + +#include "pugl/detail/win.h" + +#include "pugl/detail/implementation.h" + +void +puglRaiseWindow(PuglView* view) +{ + SetForegroundWindow(view->impl->hwnd); + SetActiveWindow(view->impl->hwnd); + return PUGL_SUCCESS; +} + +void +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +{ + view->frame.width = width; + view->frame.height = height; + + // NOTE the following code matches upstream pugl, except we add SWP_NOMOVE flag + if (view->impl->hwnd) { + RECT rect = { (long)frame.x, + (long)frame.y, + (long)frame.x + (long)frame.width, + (long)frame.y + (long)frame.height }; + + AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), + FALSE, + puglWinGetWindowExFlags(view)); + + SetWindowPos(view->impl->hwnd, + HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); + } +} + +void +puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) +{ + // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio, but stilL TODO on pugl + Display* display = view->world->impl->display; + + view->minWidth = width; + view->minHeight = height; + + if (aspect) { + view->minAspectX = width; + view->minAspectY = height; + view->maxAspectX = width; + view->maxAspectY = height; + } +} + +void +puglWin32RestoreWindow(PuglView* view) +{ + PuglInternals* impl = view->impl; + + ShowWindow(impl->hwnd, SW_RESTORE); + SetFocus(impl->hwnd); +} + +void +puglWin32ShowWindowCentered(PuglView* view) +{ + PuglInternals* impl = view->impl; + + RECT rectChild, rectParent; + + if (impl->transientParent != 0 && + GetWindowRect(impl->hwnd, &rectChild) && + GetWindowRect(impl->transientParent, &rectParent)) + { + SetWindowPos(impl->hwnd, (HWND)impl->transientParent, + 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); + } + + SetFocus(impl->hwnd); +} + +void +puglWin32SetWindowResizable(PuglView* view, bool resizable) +{ + PuglInternals* impl = view->impl; + + const int winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX + : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; + SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); +} diff --git a/dgl/src/pugl-extra/x11.c b/dgl/src/pugl-extra/x11.c new file mode 100644 index 00000000..595473db --- /dev/null +++ b/dgl/src/pugl-extra/x11.c @@ -0,0 +1,111 @@ +/* + Copyright (C) 2012-2020 Filipe Coelho + + 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. + + THIS 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. +*/ + +/** + @file x11.c X11 extra implementation for DPF. +*/ + +// NOTE can't import this file twice! +#ifndef PUGL_DETAIL_X11_H_INCLUDED +#include "../pugl-upstream/src/x11.h" +#endif + +#include "../pugl-upstream/src/implementation.h" + +#include +#include + +void +puglRaiseWindow(PuglView* view) +{ + XRaiseWindow(view->impl->display, view->impl->win); +} + +void +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +{ + view->frame.width = width; + view->frame.height = height; + + if (view->impl->win) { +#if 0 + if (! fResizable) + { + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + + sizeHints.flags = PSize|PMinSize|PMaxSize; + sizeHints.width = static_cast(width); + sizeHints.height = static_cast(height); + sizeHints.min_width = static_cast(width); + sizeHints.min_height = static_cast(height); + sizeHints.max_width = static_cast(width); + sizeHints.max_height = static_cast(height); + + XSetWMNormalHints(xDisplay, xWindow, &sizeHints); + } +#endif + + XResizeWindow(view->world->impl->display, view->impl->win, width, height); + } +} + +void +puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) +{ + // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio + Display* display = view->world->impl->display; + + view->minWidth = width; + view->minHeight = height; + + if (aspect) { + view->minAspectX = width; + view->minAspectY = height; + view->maxAspectX = width; + view->maxAspectY = height; + } + +#if 0 + if (view->impl->win) { + XSizeHints sizeHints = getSizeHints(view); + XSetNormalHints(display, view->impl->win, &sizeHints); + // NOTE old code used this instead + // XSetWMNormalHints(display, view->impl->win, &sizeHints); + } +#endif +} + +void +puglExtraSetWindowTypeAndPID(PuglView* view) +{ + PuglInternals* const impl = view->impl; + + const pid_t pid = getpid(); + const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False); + XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); + + const Atom _wt = XInternAtom(impl->display, "_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(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False), + XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False) + }; + + XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); +} diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index b5501840..04ff34a8 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -22,6 +22,7 @@ #endif #if DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_UI_IS_STANDALONE 0 # include "DistrhoUIInternal.hpp" #endif diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 7bb52475..211cad44 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -38,7 +38,6 @@ Window* d_lastUiWindow = nullptr; // ----------------------------------------------------------------------------------------------------------- #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -static inline UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath) { d_lastUiDspPtr = dspPtr; @@ -53,7 +52,6 @@ UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scal return ret; } #else -static inline UI* createUiWrapper(void* const dspPtr, Window* const window) { d_lastUiDspPtr = dspPtr; diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index ef3fc365..41504af4 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -14,6 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define DISTRHO_UI_IS_STANDALONE 1 #include "DistrhoUIInternal.hpp" #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 25c066bd..819ddb68 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -203,7 +203,7 @@ public: return fUI->isRunning(); } - intptr_t getWindowId() const noexcept + uintptr_t getNativeWindowHandle() const noexcept { return 0; } @@ -223,9 +223,9 @@ public: return glWindow.isVisible(); } - intptr_t getWindowId() const noexcept + uintptr_t getNativeWindowHandle() const noexcept { - return glWindow.getWindowId(); + return glWindow.getNativeWindowHandle(); } #endif diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 2db10656..f1a11aba 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -14,6 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define DISTRHO_UI_IS_STANDALONE 0 #include "DistrhoUIInternal.hpp" #include "../extra/String.hpp" @@ -100,7 +101,7 @@ public: fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); if (widget != nullptr) - *widget = (LV2UI_Widget)fUI.getWindowId(); + *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); #if DISTRHO_PLUGIN_WANT_STATE // tell the DSP we're ready to receive msgs From 24a00afe654a843e52321968cad33516d2f0d115 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 20:09:03 +0000 Subject: [PATCH 010/159] Cleanup DGL Application class Signed-off-by: falkTX --- dgl/Application.hpp | 11 +-- dgl/Base.hpp | 2 +- dgl/Cairo.hpp | 6 +- dgl/Makefile | 1 + dgl/OpenGL.hpp | 4 +- dgl/src/Application.cpp | 14 ++-- dgl/src/ApplicationPrivateData.cpp | 110 +++++++++++++++++++++++++++ dgl/src/ApplicationPrivateData.hpp | 118 ++++++++++------------------- dgl/src/WidgetPrivateData.cpp | 2 +- distrho/DistrhoUtils.hpp | 2 + 10 files changed, 174 insertions(+), 96 deletions(-) create mode 100644 dgl/src/ApplicationPrivateData.cpp diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 87802973..2a561691 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -21,12 +21,12 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Forward class names class Window; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** Base DGL Application class. @@ -63,11 +63,12 @@ public: idle() is called at regular intervals. @note This function is meant for standalones only, *never* call this from plugins. */ - void exec(uint idleTime = 10); + void exec(uint idleTimeInMs = 10); /** Quit the application. This stops the event-loop and closes all Windows. + @note This function is meant for standalones only, *never* call this from plugins. */ void quit(); @@ -85,7 +86,7 @@ private: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/Base.hpp b/dgl/Base.hpp index 90377883..f8c0ba20 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -92,7 +92,7 @@ enum Key { /** Graphics context, definition depends on build type. */ -struct GraphicsContext; +struct GraphicsContext {}; /** Idle callback. diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index ce0c920b..2aa38408 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -26,9 +26,9 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- /** - Graphics context. + Cairo Graphics context. */ -struct GraphicsContext +struct CairoGraphicsContext : GraphicsContext { cairo_t* cairo; // FIXME proper name.. }; diff --git a/dgl/Makefile b/dgl/Makefile index 45f75f5b..221d8857 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -24,6 +24,7 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra -Wno-narrowing OBJS_common = \ ../build/dgl/Application.cpp.o \ + ../build/dgl/ApplicationPrivateData.cpp.o \ ../build/dgl/Color.cpp.o \ ../build/dgl/Geometry.cpp.o \ ../build/dgl/ImageBase.cpp.o \ diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 5466161c..622ec89d 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -108,9 +108,9 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- /** - Graphics context. + OpenGL Graphics context. */ -struct GraphicsContext +struct OpenGLGraphicsContext : GraphicsContext { }; diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 0ab17764..23db6a4f 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,7 +18,7 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- Application::Application(const bool isStandalone) : pData(new PrivateData(isStandalone)) {} @@ -33,14 +33,18 @@ void Application::idle() pData->idle(0); } -void Application::exec(const uint idleTime) +void Application::exec(const uint idleTimeInMs) { + DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); + while (! pData->isQuitting) - pData->idle(idleTime); + pData->idle(idleTimeInMs); } void Application::quit() { + DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); + pData->quit(); } @@ -49,6 +53,6 @@ bool Application::isQuiting() const noexcept return pData->isQuitting; } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp new file mode 100644 index 00000000..24a48def --- /dev/null +++ b/dgl/src/ApplicationPrivateData.cpp @@ -0,0 +1,110 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "ApplicationPrivateData.hpp" +#include "../Window.hpp" + +#include "pugl-upstream/include/pugl/pugl.h" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +Application::PrivateData::PrivateData(const bool standalone) + : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, + standalone ? PUGL_WORLD_THREADS : 0x0)), + isStandalone(standalone), + isStarting(true), + isQuitting(false), + visibleWindows(0), + windows(), + idleCallbacks() +{ + DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); + + puglSetWorldHandle(world, this); + puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); + + // puglSetLogLevel(world, PUGL_LOG_LEVEL_DEBUG); + +} + +Application::PrivateData::~PrivateData() +{ + DISTRHO_SAFE_ASSERT(!isStarting); + DISTRHO_SAFE_ASSERT(isQuitting); + DISTRHO_SAFE_ASSERT(visibleWindows == 0); + + windows.clear(); + idleCallbacks.clear(); + + if (world != nullptr) + puglFreeWorld(world); +} + +// -------------------------------------------------------------------------------------------------------------------- + +void Application::PrivateData::oneWindowShown() noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); + + if (++visibleWindows == 1) + { + isStarting = false; + isQuitting = false; + } +} + +void Application::PrivateData::oneWindowHidden() noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); + DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,); + + if (--visibleWindows == 0) + isQuitting = true; +} + +void Application::PrivateData::idle(const uint timeoutInMs) +{ + puglUpdate(world, timeoutInMs == 0 ? 0.0 : static_cast(timeoutInMs) / 1000.0); + + for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) + { + Window* const window(*it); + window->_idle(); + } + + for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) + { + IdleCallback* const idleCallback(*it); + idleCallback->idleCallback(); + } +} + +void Application::PrivateData::quit() +{ + isQuitting = true; + + for (std::list::reverse_iterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) + { + Window* const window(*rit); + window->close(); + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index ad64e4f9..3d6910d0 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -18,101 +18,61 @@ #define DGL_APP_PRIVATE_DATA_HPP_INCLUDED #include "../Application.hpp" -#include "../Window.hpp" - -#include "pugl-upstream/include/pugl/pugl.h" #include +typedef struct PuglWorldImpl PuglWorld; + START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +class Window; + +// -------------------------------------------------------------------------------------------------------------------- struct Application::PrivateData { + /** Pugl world instance. */ + PuglWorld* const world; + + /** Whether the application is running as standalone, otherwise it is part of a plugin. */ + const bool isStandalone; + + /** Whether the applicating is starting up, not yet fully initialized. Defaults to true. */ + bool isStarting; + + /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ bool isQuitting; - bool isStandalone; + + /** Counter of visible windows, only used in standalone mode. + If 0->1, application is starting. If 1->0, application is quitting/stopping. */ uint visibleWindows; + + /** List of windows for this application. Used as a way to call each window `idle`. */ std::list windows; + + /** List of idle callbacks for this application. Run after all windows `idle`. */ std::list idleCallbacks; - PuglWorld* const world; - PrivateData(const bool standalone) - : isQuitting(false), - isStandalone(standalone), - visibleWindows(0), - windows(), - idleCallbacks(), - world(puglNewWorld(isStandalone ? PUGL_PROGRAM : PUGL_MODULE, - isStandalone ? PUGL_WORLD_THREADS : 0x0)) - { - puglSetWorldHandle(world, this); - - // puglSetLogLevel(world, PUGL_LOG_LEVEL_DEBUG); - - // TODO puglSetClassName - } - - ~PrivateData() - { - DISTRHO_SAFE_ASSERT(isQuitting); - DISTRHO_SAFE_ASSERT(visibleWindows == 0); - - windows.clear(); - idleCallbacks.clear(); - - d_stdout("calling puglFreeWorld"); - puglFreeWorld(world); - } - - void oneWindowShown() noexcept - { - DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); - - if (++visibleWindows == 1) - isQuitting = false; - } - - void oneWindowHidden() noexcept - { - DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); - DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,); - - if (--visibleWindows == 0) - isQuitting = true; - } - - void idle(const uint timeout) - { - puglUpdate(world, timeout == 0 ? 0.0 : (1.0 / static_cast(timeout))); - - for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) - { - Window* const window(*it); - window->_idle(); - } - - for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) - { - IdleCallback* const idleCallback(*it); - idleCallback->idleCallback(); - } - } - - void quit() - { - isQuitting = true; - - for (std::list::reverse_iterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) - { - Window* const window(*rit); - window->close(); - } - } + /** Constructor and destructor */ + PrivateData(const bool standalone); + ~PrivateData(); + + /** Flag one window shown or hidden status, which modifies @a visibleWindows. + For standalone mode only. + Modifies @a isStarting and @a isQuitting under certain conditions */ + void oneWindowShown() noexcept; + void oneWindowHidden() noexcept; + + /** Run Pugl world update for @a timeoutInMs, and then the idle functions for each window and idle callback, + in order of registration. */ + void idle(const uint timeoutInMs); + + /** Set flag indicating application is quitting, and closes all windows in reverse order of registration. */ + void quit(); DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index cef082b1..b28bf176 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -77,7 +77,7 @@ void Widget::PrivateData::display(const uint width, #endif #ifdef DGL_CAIRO - cairo_t* cr = parent.getGraphicsContext().cairo; + cairo_t* cr = static_cast(parent.getGraphicsContext()).cairo; cairo_matrix_t matrix; cairo_get_matrix(cr, &matrix); cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp index 4cf8ac73..9a2e0248 100644 --- a/distrho/DistrhoUtils.hpp +++ b/distrho/DistrhoUtils.hpp @@ -50,6 +50,8 @@ inline float round(float __x) # define M_PI 3.14159265358979323846 #endif +#define DISTRHO_MACRO_AS_STRING(MACRO) #MACRO + // ----------------------------------------------------------------------- // misc functions From 439ef044f57c033e5ed2b7d102ece1cc8456492e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 20:34:17 +0000 Subject: [PATCH 011/159] Update Base DGL header to be in sync with pugl Signed-off-by: falkTX --- dgl/Base.hpp | 74 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index f8c0ba20..292831ad 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,7 +20,7 @@ #include "../distrho/extra/LeakDetector.hpp" #include "../distrho/extra/ScopedPointer.hpp" -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Define namespace #ifndef DGL_NAMESPACE @@ -33,33 +33,46 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Base DGL enums -/** - Convenience symbols for ASCII control characters. - */ -enum Char { - kCharBackspace = 0x08, - kCharEscape = 0x1B, - kCharDelete = 0x7F -}; - /** Keyboard modifier flags. */ enum Modifier { - kModifierShift = 1 << 0, /**< Shift key */ - kModifierControl = 1 << 1, /**< Control key */ - kModifierAlt = 1 << 2, /**< Alt/Option key */ - kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */ + kModifierShift = 1u << 0u, /**< Shift key */ + kModifierControl = 1u << 1u, /**< Control key */ + kModifierAlt = 1u << 2u, /**< Alt/Option key */ + kModifierSuper = 1u << 3u /**< Mod4/Command/Windows key */ }; /** - Special (non-Unicode) keyboard keys. + Keyboard key codepoints. + + All keys are identified by a Unicode code point in PuglEventKey::key. This + enumeration defines constants for special keys that do not have a standard + code point, and some convenience constants for control characters. Note + that all keys are handled in the same way, this enumeration is just for + convenience when writing hard-coded key bindings. + + Keys that do not have a standard code point use values in the Private Use + Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications + must take care to not interpret these values beyond key detection, the + mapping used here is arbitrary and specific to DPF. */ enum Key { - kKeyF1 = 1, + // Convenience symbols for ASCII control characters + kKeyBackspace = 0x08, + kKeyEscape = 0x1B, + kKeyDelete = 0x7F, + + // Backwards compatibility with old DPF + kCharBackspace = kKeyBackspace, + kCharEscape = kKeyEscape, + kCharDelete = kKeyDelete, + + // Unicode Private Use Area + kKeyF1 = 0xE000, kKeyF2, kKeyF3, kKeyF4, @@ -81,12 +94,26 @@ enum Key { kKeyEnd, kKeyInsert, kKeyShift, + kKeyShiftL = kKeyShift, + kKeyShiftR, kKeyControl, + kKeyControlL = kKeyControl, + kKeyControlR, kKeyAlt, - kKeySuper + kKeyAltL = kKeyAlt, + kKeyAltR, + kKeySuper, + kKeySuperL = kKeySuper, + kKeySuperR, + kKeyMenu, + kKeyCapsLock, + kKeyScrollLock, + kKeyNumLock, + kKeyPrintScreen, + kKeyPause }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Base DGL classes /** @@ -97,14 +124,13 @@ struct GraphicsContext {}; /** Idle callback. */ -class IdleCallback +struct IdleCallback { -public: virtual ~IdleCallback() {} virtual void idleCallback() = 0; }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL @@ -114,6 +140,6 @@ END_NAMESPACE_DGL using namespace DGL_NAMESPACE; #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- #endif // DGL_BASE_HPP_INCLUDED From 5539e161653af7e59f12633f36e319ce2b8d94df Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 22:05:52 +0000 Subject: [PATCH 012/159] Update base events to match latest pugl Signed-off-by: falkTX --- dgl/Base.hpp | 25 ++++- dgl/ImageWidgets.hpp | 2 +- dgl/Widget.hpp | 104 ++++++++++++++++---- dgl/Window.hpp | 5 + dgl/src/ImageWidgets.cpp | 16 +-- dgl/src/Widget.cpp | 6 +- examples/Parameters/ExampleUIParameters.cpp | 4 +- examples/States/ExampleUIStates.cpp | 4 +- 8 files changed, 126 insertions(+), 40 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index 292831ad..0229512c 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -40,10 +40,10 @@ START_NAMESPACE_DGL Keyboard modifier flags. */ enum Modifier { - kModifierShift = 1u << 0u, /**< Shift key */ - kModifierControl = 1u << 1u, /**< Control key */ - kModifierAlt = 1u << 2u, /**< Alt/Option key */ - kModifierSuper = 1u << 3u /**< Mod4/Command/Windows key */ + kModifierShift = 1u << 0u, ///< Shift key + kModifierControl = 1u << 1u, ///< Control key + kModifierAlt = 1u << 2u, ///< Alt/Option key + kModifierSuper = 1u << 3u ///< Mod4/Command/Windows key }; /** @@ -113,6 +113,23 @@ enum Key { kKeyPause }; +/** + Common flags for all events. + */ +enum Flag { + kFlagSendEvent = 1, ///< Event is synthetic + kFlagIsHint = 2 ///< Event is a hint (not direct user input) +}; + +/** + Reason for a crossing event. + */ +enum CrossingMode { + kCrossingNormal, ///< Crossing due to pointer motion + kCrossingGrab, ///< Crossing due to a grab + kCrossingUngrab ///< Crossing due to a grab release +}; + // -------------------------------------------------------------------------------------------------------------------- // Base DGL classes diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index ac8a3abb..a88bc3ac 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -227,7 +227,7 @@ private: Point fStartPos; Point fEndPos; - Rectangle fSliderArea; + Rectangle fSliderArea; void _recheckArea() noexcept; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index a54c3de0..dc4cea56 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -63,38 +63,61 @@ class Widget public: /** Base event data. - @a mod The currently active keyboard modifiers, @see Modifier. - @a time The timestamp (if any). + These are the fields present on all Widget events. + + @a mod Currently active keyboard modifiers, @see Modifier. + @a mod Event flags, @see Flag. + @a time Event timestamp (if any). */ struct BaseEvent { - uint mod; - uint32_t time; + uint mod; + uint flags; + uint time; /** Constuctor */ - BaseEvent() noexcept : mod(0x0), time(0) {} + BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} /** Destuctor */ virtual ~BaseEvent() noexcept {} }; /** Keyboard event. - @a press True if the key was pressed, false if released. - @a key Unicode point of the key pressed. + + This event represents low-level key presses and releases. + This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. + + Keys are represented portably as Unicode code points, using the "natural" code point for the key. + The @a key field is the code for the pressed key, without any modifiers applied. + For example, a press or release of the 'A' key will have `key` 97 ('a') + regardless of whether shift or control are being held. + + Alternatively, the raw @a keycode can be used to work directly with physical keys, + but note that this value is not portable and differs between platforms and hardware. + + @a press True if the key was pressed, false if released. + @a key Unicode point of the key pressed. + @a keycode Raw keycode. @see onKeyboard */ struct KeyboardEvent : BaseEvent { bool press; uint key; + uint keycode; /** Constuctor */ KeyboardEvent() noexcept : BaseEvent(), press(false), - key(0) {} + key(0), + keycode(0) {} }; /** Special keyboard event. + + This event allows the use of keys that do not have unicode points. + Note that some are non-printable keys. + @a press True if the key was pressed, false if released. @a key The key pressed. @see onSpecial @@ -111,54 +134,91 @@ public: }; /** - Mouse event. - @a button The button number (1 = left, 2 = middle, 3 = right). + Character input event. + + This event represents text input, usually as the result of a key press. + The text is given both as a Unicode character code and a UTF-8 string. + + Note that this event is generated by the platform's input system, + so there is not necessarily a direct correspondence between text events and physical key presses. + For example, with some input methods a sequence of several key presses will generate a single character. + + @a keycode Raw key code. + @a character Unicode character code. + @a string UTF-8 string. + @see onCharacterInput + */ + struct CharacterInputEvent : BaseEvent { + uint keycode; + uint character; + char string[8]; + + /** Constuctor */ + CharacterInputEvent() noexcept + : BaseEvent(), + keycode(0), + character(0), + string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} + }; + + /** + Mouse press or release event. + + @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). @a press True if the button was pressed, false if released. @a pos The widget-relative coordinates of the pointer. @see onMouse */ struct MouseEvent : BaseEvent { - int button; + uint button; bool press; - Point pos; + Point pos; /** Constuctor */ MouseEvent() noexcept : BaseEvent(), button(0), press(false), - pos(0, 0) {} + pos(0.0, 0.0) {} }; /** Mouse motion event. + @a pos The widget-relative coordinates of the pointer. @see onMotion */ struct MotionEvent : BaseEvent { - Point pos; + Point pos; /** Constuctor */ MotionEvent() noexcept : BaseEvent(), - pos(0, 0) {} + pos(0.0, 0.0) {} }; /** Mouse scroll event. + + The scroll distance is expressed in "lines", + an arbitrary unit that corresponds to a single tick of a detented mouse wheel. + For example, `delta.y` = 1.0 scrolls 1 line up. + Some systems and devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + @a pos The widget-relative coordinates of the pointer. @a delta The scroll distance. @see onScroll */ struct ScrollEvent : BaseEvent { - Point pos; - Point delta; + Point pos; + Point delta; /** Constuctor */ ScrollEvent() noexcept : BaseEvent(), - pos(0, 0), - delta(0.0f, 0.0f) {} + pos(0.0, 0.0), + delta(0.0, 0.0) {} }; /** @@ -318,12 +378,14 @@ public: /** Check if this widget contains the point defined by @a x and @a y. */ - bool contains(int x, int y) const noexcept; + template + bool contains(T x, T y) const noexcept; /** Check if this widget contains the point @a pos. */ - bool contains(const Point& pos) const noexcept; + template + bool contains(const Point& pos) const noexcept; /** Tell this widget's window to repaint itself. diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 1fa7f819..44eda0c6 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -34,6 +34,11 @@ class Application; class Widget; class StandaloneWindow; +/* TODO + * add focusEvent with CrossingMode arg + * add eventcrossing/enter-leave event + */ + class Window { public: diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index f42f066d..48b85dc2 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -991,18 +991,18 @@ void ImageSlider::_recheckArea() noexcept if (fStartPos.getY() == fEndPos.getY()) { // horizontal - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), - static_cast(fImage.getHeight())); + fSliderArea = Rectangle(fStartPos.getX(), + fStartPos.getY(), + fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), + static_cast(fImage.getHeight())); } else { // vertical - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - static_cast(fImage.getWidth()), - fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); + fSliderArea = Rectangle(fStartPos.getX(), + fStartPos.getY(), + static_cast(fImage.getWidth()), + fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); } } diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 89083cab..ec6db8c5 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -191,12 +191,14 @@ Window& Widget::getParentWindow() const noexcept return pData->parent; } -bool Widget::contains(int x, int y) const noexcept +template +bool Widget::contains(T x, T y) const noexcept { return (x >= 0 && y >= 0 && static_cast(x) < pData->size.getWidth() && static_cast(y) < pData->size.getHeight()); } -bool Widget::contains(const Point& pos) const noexcept +template +bool Widget::contains(const Point& pos) const noexcept { return contains(pos.getX(), pos.getY()); } diff --git a/examples/Parameters/ExampleUIParameters.cpp b/examples/Parameters/ExampleUIParameters.cpp index aa0a0b3a..8e69e2c4 100644 --- a/examples/Parameters/ExampleUIParameters.cpp +++ b/examples/Parameters/ExampleUIParameters.cpp @@ -107,7 +107,7 @@ protected: const uint minwh = std::min(width, height); const uint bgColor = getBackgroundColor(); - Rectangle r; + Rectangle r; // if host doesn't respect aspect-ratio but supports ui background, draw out-of-bounds color from it if (width != height && bgColor != 0) @@ -185,7 +185,7 @@ protected: const uint height = getHeight(); const uint minwh = std::min(width, height); - Rectangle r; + Rectangle r; r.setWidth(minwh/3 - 6); r.setHeight(minwh/3 - 6); diff --git a/examples/States/ExampleUIStates.cpp b/examples/States/ExampleUIStates.cpp index 1f197f75..2e0ae031 100644 --- a/examples/States/ExampleUIStates.cpp +++ b/examples/States/ExampleUIStates.cpp @@ -149,7 +149,7 @@ protected: const uint width = getWidth(); const uint height = getHeight(); - Rectangle r; + Rectangle r; r.setWidth(width/3 - 6); r.setHeight(height/3 - 6); @@ -204,7 +204,7 @@ protected: const uint width = getWidth(); const uint height = getHeight(); - Rectangle r; + Rectangle r; r.setWidth(width/3 - 6); r.setHeight(height/3 - 6); From bc66137eb26c2668b9b75b2dda1816f064a93796 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 23:01:28 +0000 Subject: [PATCH 013/159] Cleanup Signed-off-by: falkTX --- dgl/Widget.hpp | 12 +++++++++--- dgl/src/ImageWidgets.cpp | 4 ++-- dgl/src/Widget.cpp | 14 ++++++++++---- dgl/src/WidgetPrivateData.hpp | 3 +++ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index dc4cea56..e863dd97 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -32,8 +32,7 @@ END_NAMESPACE_DISTRHO START_NAMESPACE_DGL -class Application; -class ImageSlider; +// class Application; class NanoWidget; class Window; class StandaloneWindow; @@ -422,6 +421,12 @@ protected: */ virtual bool onSpecial(const SpecialEvent&); + /** + A function called when an UTF-8 character is received. + @return True to stop event propagation, false otherwise. + */ + virtual bool onCharacterInput(const CharacterInputEvent&); + /** A function called when a mouse button is pressed or released. @return True to stop event propagation, false otherwise. @@ -450,6 +455,8 @@ protected: */ virtual void onPositionChanged(const PositionChangedEvent&); + void setNeedsFullViewport(); + private: struct PrivateData; PrivateData* const pData; @@ -457,7 +464,6 @@ private: /** @internal */ explicit Widget(Widget* groupWidget, bool addToSubWidgets); - friend class ImageSlider; friend class NanoWidget; friend class Window; friend class StandaloneWindow; diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index 48b85dc2..b7c2f0db 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -675,7 +675,7 @@ ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept fEndPos(), fSliderArea() { - pData->needsFullViewport = true; + setNeedsFullViewport(); } ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept @@ -698,7 +698,7 @@ ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept fEndPos(), fSliderArea() { - pData->needsFullViewport = true; + setNeedsFullViewport(); } float ImageSlider::getValue() const noexcept diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index ec6db8c5..b23058e8 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -24,24 +24,20 @@ START_NAMESPACE_DGL Widget::Widget(Window& parent) : pData(new PrivateData(this, parent, nullptr, false)) { - parent._addWidget(this); } Widget::Widget(Widget* groupWidget) : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)) { - pData->parent._addWidget(this); } Widget::Widget(Widget* groupWidget, bool addToSubWidgets) : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)) { - pData->parent._addWidget(this); } Widget::~Widget() { - pData->parent._removeWidget(this); delete pData; } @@ -228,6 +224,11 @@ bool Widget::onSpecial(const SpecialEvent&) return false; } +bool Widget::onCharacterInput(const CharacterInputEvent&) +{ + return false; +} + bool Widget::onMouse(const MouseEvent&) { return false; @@ -251,6 +252,11 @@ void Widget::onPositionChanged(const PositionChangedEvent&) { } +void Widget::setNeedsFullViewport() +{ + pData->needsFullViewport = true; +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index f48a33bc..c968054d 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -56,10 +56,13 @@ struct Widget::PrivateData { skipDisplay = true; groupWidget->pData->subWidgets.push_back(self); } + + parent._addWidget(self); } ~PrivateData() { + parent._removeWidget(self); subWidgets.clear(); } From 91d4212e930f2ec5f3416e3a78ad3fa9e7ac6d9b Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Mar 2021 23:39:22 +0000 Subject: [PATCH 014/159] Get mouse button event working at least Signed-off-by: falkTX --- dgl/NanoVG.hpp | 2 ++ dgl/src/NanoVG.cpp | 15 +++++----- dgl/src/WindowPrivateData.cpp | 26 +++++++++++++---- dgl/src/WindowPrivateData.hpp | 53 +++++++++++++++-------------------- 4 files changed, 53 insertions(+), 43 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index ea07df28..27a38d45 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -305,7 +305,9 @@ public: /** Constructor reusing a NanoVG context, used for subwidgets. */ + /* NanoVG(NanoWidget* groupWidget); + */ /** Destructor. diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index b62bc4c1..5e2a93c3 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -240,10 +240,10 @@ NanoVG::NanoVG(int flags) fInFrame(false), fIsSubWidget(false) {} -NanoVG::NanoVG(NanoWidget* groupWidget) - : fContext(groupWidget->fContext), - fInFrame(false), - fIsSubWidget(true) {} +// NanoVG::NanoVG(NanoWidget* groupWidget) +// : fContext(groupWidget->fContext), +// fInFrame(false), +// fIsSubWidget(true) {} NanoVG::~NanoVG() { @@ -257,21 +257,20 @@ NanoVG::~NanoVG() void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor) { + fInFrame = true; if (fContext == nullptr) return; DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); - fInFrame = true; nvgBeginFrame(fContext, static_cast(width), static_cast(height), scaleFactor); } void NanoVG::beginFrame(Widget* const widget) { + fInFrame = true; DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); - fInFrame = true; - if (fContext == nullptr) return; @@ -958,6 +957,7 @@ NanoWidget::NanoWidget(Widget* groupWidget, int flags) pData->needsScaling = true; } +/* NanoWidget::NanoWidget(NanoWidget* groupWidget) : Widget(groupWidget, false), NanoVG(groupWidget), @@ -967,6 +967,7 @@ NanoWidget::NanoWidget(NanoWidget* groupWidget) pData->skipDisplay = true; groupWidget->nData->subWidgets.push_back(this); } +*/ NanoWidget::~NanoWidget() { diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 08ef6333..297d35c3 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -129,7 +129,7 @@ void Window::PrivateData::setVisible(const bool visible) #ifdef DISTRHO_OS_WINDOWS puglShowWindowCentered(fView); #else - puglShowWindow(fView); + puglShow(fView); #endif fAppData->oneWindowShown(); fFirstInit = false; @@ -139,7 +139,7 @@ void Window::PrivateData::setVisible(const bool visible) #ifdef DISTRHO_OS_WINDOWS puglWin32RestoreWindow(fView); #else - puglShowWindow(fView); + puglShow(fView); #endif } } @@ -153,7 +153,7 @@ void Window::PrivateData::setVisible(const bool visible) } #endif - puglHideWindow(fView); + puglHide(fView); // if (fModal.enabled) // exec_fini(); @@ -270,7 +270,7 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose) case PUGL_UNMAP: return fprintf(stderr, "%sUnmap\n", prefix); case PUGL_UPDATE: - return fprintf(stderr, "%sUpdate\n", prefix); + return 0; // fprintf(stderr, "%sUpdate\n", prefix); case PUGL_CONFIGURE: return PRINT("%sConfigure " PFMT " " PFMT "\n", prefix, @@ -350,6 +350,20 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu pData->onPuglClose(); break; + case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton + case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton + { + Widget::MouseEvent ev; + ev.mod = event->button.state; + ev.flags = event->button.flags; + ev.time = static_cast(event->button.time * 1000.0 + 0.5); + ev.button = event->button.button; + ev.press = event->type == PUGL_BUTTON_PRESS; + ev.pos = Point(event->button.x, event->button.y); + pData->onPuglMouse(ev); + break; + } + case PUGL_FOCUS_IN: ///< Keyboard focus entered view, a #PuglEventFocus case PUGL_FOCUS_OUT: ///< Keyboard focus left view, a #PuglEventFocus case PUGL_KEY_PRESS: ///< Key pressed, a #PuglEventKey @@ -357,12 +371,12 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_TEXT: ///< Character entered, a #PuglEventText case PUGL_POINTER_IN: ///< Pointer entered view, a #PuglEventCrossing case PUGL_POINTER_OUT: ///< Pointer left view, a #PuglEventCrossing - case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton - case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton case PUGL_MOTION: ///< Pointer moved, a #PuglEventMotion case PUGL_SCROLL: ///< Scrolled, a #PuglEventScroll case PUGL_CLIENT: ///< Custom client message, a #PuglEventClient case PUGL_TIMER: ///< Timer triggered, a #PuglEventTimer + case PUGL_LOOP_ENTER: ///< Recursive loop entered, a #PuglEventLoopEnter + case PUGL_LOOP_LEAVE: ///< Recursive loop left, a #PuglEventLoopLeave break; } diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 004875e7..228dab4b 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -340,6 +340,29 @@ struct Window::PrivateData { } } + void onPuglMouse(const Widget::MouseEvent& ev) + { + DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + + Widget::MouseEvent rev = ev; + double x = ev.pos.getX() / fAutoScaling; + double y = ev.pos.getY() / fAutoScaling; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + rev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onMouse(rev)) + break; + } + } + // ------------------------------------------------------------------- #ifdef DISTRHO_DEFINES_H_INCLUDED @@ -484,36 +507,6 @@ struct Window::PrivateData { 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(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(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); From 9c5317c8548109447a6863d7d5e457978036230f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 28 Mar 2021 17:43:06 +0100 Subject: [PATCH 015/159] Start splitting some code Signed-off-by: falkTX --- dgl/Events.hpp | 225 +++++++++++++++++++ dgl/StandaloneWindow.hpp | 10 +- dgl/SubWidget.hpp | 103 +++++++++ dgl/TopLevelWidget.hpp | 50 +++++ dgl/Widget.hpp | 310 +++----------------------- dgl/src/StandaloneWindow.cpp | 6 +- dgl/src/SubWidget.cpp | 79 +++++++ dgl/src/SubWidgetPrivateData.hpp | 49 ++++ dgl/src/TopLevelWidget.cpp | 17 ++ dgl/src/TopLevelWidgetPrivateData.hpp | 42 ++++ dgl/src/Widget.cpp | 109 +++------ dgl/src/WidgetPrivateData.hpp | 24 +- 12 files changed, 644 insertions(+), 380 deletions(-) create mode 100644 dgl/Events.hpp create mode 100644 dgl/SubWidget.hpp create mode 100644 dgl/TopLevelWidget.hpp create mode 100644 dgl/src/SubWidget.cpp create mode 100644 dgl/src/SubWidgetPrivateData.hpp create mode 100644 dgl/src/TopLevelWidget.cpp create mode 100644 dgl/src/TopLevelWidgetPrivateData.hpp diff --git a/dgl/Events.hpp b/dgl/Events.hpp new file mode 100644 index 00000000..b34b5807 --- /dev/null +++ b/dgl/Events.hpp @@ -0,0 +1,225 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_EVENTS_HPP_INCLUDED +#define DGL_EVENTS_HPP_INCLUDED + +#include "Geometry.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +namespace Events +{ + /** + Base event data. + These are the fields present on all Widget events. + + @a mod Currently active keyboard modifiers, @see Modifier. + @a mod Event flags, @see Flag. + @a time Event timestamp (if any). + */ + struct BaseEvent { + uint mod; + uint flags; + uint time; + + /** Constuctor */ + BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} + /** Destuctor */ + virtual ~BaseEvent() noexcept {} + }; + + /** + Keyboard event. + + This event represents low-level key presses and releases. + This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. + + Keys are represented portably as Unicode code points, using the "natural" code point for the key. + The @a key field is the code for the pressed key, without any modifiers applied. + For example, a press or release of the 'A' key will have `key` 97 ('a') + regardless of whether shift or control are being held. + + Alternatively, the raw @a keycode can be used to work directly with physical keys, + but note that this value is not portable and differs between platforms and hardware. + + @a press True if the key was pressed, false if released. + @a key Unicode point of the key pressed. + @a keycode Raw keycode. + @see onKeyboard + */ + struct KeyboardEvent : BaseEvent { + bool press; + uint key; + uint keycode; + + /** Constuctor */ + KeyboardEvent() noexcept + : BaseEvent(), + press(false), + key(0), + keycode(0) {} + }; + + /** + Special keyboard event. + + This event allows the use of keys that do not have unicode points. + Note that some are non-printable keys. + + @a press True if the key was pressed, false if released. + @a key The key pressed. + @see onSpecial + */ + struct SpecialEvent : BaseEvent { + bool press; + Key key; + + /** Constuctor */ + SpecialEvent() noexcept + : BaseEvent(), + press(false), + key(Key(0)) {} + }; + + /** + Character input event. + + This event represents text input, usually as the result of a key press. + The text is given both as a Unicode character code and a UTF-8 string. + + Note that this event is generated by the platform's input system, + so there is not necessarily a direct correspondence between text events and physical key presses. + For example, with some input methods a sequence of several key presses will generate a single character. + + @a keycode Raw key code. + @a character Unicode character code. + @a string UTF-8 string. + @see onCharacterInput + */ + struct CharacterInputEvent : BaseEvent { + uint keycode; + uint character; + char string[8]; + + /** Constuctor */ + CharacterInputEvent() noexcept + : BaseEvent(), + keycode(0), + character(0), + string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} + }; + + /** + Mouse press or release event. + + @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). + @a press True if the button was pressed, false if released. + @a pos The widget-relative coordinates of the pointer. + @see onMouse + */ + struct MouseEvent : BaseEvent { + uint button; + bool press; + Point pos; + + /** Constuctor */ + MouseEvent() noexcept + : BaseEvent(), + button(0), + press(false), + pos(0.0, 0.0) {} + }; + + /** + Mouse motion event. + + @a pos The widget-relative coordinates of the pointer. + @see onMotion + */ + struct MotionEvent : BaseEvent { + Point pos; + + /** Constuctor */ + MotionEvent() noexcept + : BaseEvent(), + pos(0.0, 0.0) {} + }; + + /** + Mouse scroll event. + + The scroll distance is expressed in "lines", + an arbitrary unit that corresponds to a single tick of a detented mouse wheel. + For example, `delta.y` = 1.0 scrolls 1 line up. + Some systems and devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + + @a pos The widget-relative coordinates of the pointer. + @a delta The scroll distance. + @see onScroll + */ + struct ScrollEvent : BaseEvent { + Point pos; + Point delta; + + /** Constuctor */ + ScrollEvent() noexcept + : BaseEvent(), + pos(0.0, 0.0), + delta(0.0, 0.0) {} + }; + + /** + Resize event. + @a size The new widget size. + @a oldSize The previous size, may be null. + @see onResize + */ + struct ResizeEvent { + Size size; + Size oldSize; + + /** Constuctor */ + ResizeEvent() noexcept + : size(0, 0), + oldSize(0, 0) {} + }; + + /** + Widget position changed event. + @a pos The new absolute position of the widget. + @a oldPos The previous absolute position of the widget. + @see onPositionChanged + */ + struct PositionChangedEvent { + Point pos; + Point oldPos; + + /** Constuctor */ + PositionChangedEvent() noexcept + : pos(0, 0), + oldPos(0, 0) {} + }; +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_EVENTS_HPP_INCLUDED diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 01ffa7db..3bdfec5a 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -18,7 +18,7 @@ #define DGL_STANDALONE_WINDOW_HPP_INCLUDED #include "Application.hpp" -#include "Widget.hpp" +#include "TopLevelWidget.hpp" #include "Window.hpp" START_NAMESPACE_DGL @@ -40,16 +40,18 @@ public: void exec(); private: - Widget* fWidget; + TopLevelWidget* fWidget; /** @internal */ void onReshape(uint width, uint height) override; +#if 0 /** @internal */ - void _addWidget(Widget* widget) override; + void _addWidget(TopLevelWidget* widget) override; /** @internal */ - void _removeWidget(Widget* widget) override; + void _removeWidget(TopLevelWidget* widget) override; +#endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) }; diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp new file mode 100644 index 00000000..81c8ad59 --- /dev/null +++ b/dgl/SubWidget.hpp @@ -0,0 +1,103 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_SUBWIDGET_HPP_INCLUDED +#define DGL_SUBWIDGET_HPP_INCLUDED + +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +class SubWidget : public Widget +{ +public: + /** + Constructor. + */ + explicit SubWidget(Widget* widgetToGroupTo); + + /** + Destructor. + */ + virtual ~SubWidget(); + + /** + Check if this widget contains the point defined by @a x and @a y. + */ + template + bool contains(T x, T y) const noexcept; + + /** + Check if this widget contains the point @a pos. + */ + template + bool contains(const Point& pos) const noexcept; + + /** + Get absolute X. + */ + int getAbsoluteX() const noexcept; + + /** + Get absolute Y. + */ + int getAbsoluteY() const noexcept; + + /** + Get absolute position. + */ + const Point& getAbsolutePos() const noexcept; + + /** + Set absolute X. + */ + void setAbsoluteX(int x) noexcept; + + /** + Set absolute Y. + */ + void setAbsoluteY(int y) noexcept; + + /** + Set absolute position using @a x and @a y values. + */ + void setAbsolutePos(int x, int y) noexcept; + + /** + Set absolute position. + */ + void setAbsolutePos(const Point& pos) noexcept; + +protected: + /** + A function called when the subwidget's absolute position is changed. + */ + virtual void onPositionChanged(const PositionChangedEvent&); + +private: + struct PrivateData; + PrivateData* const pData; + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_SUBWIDGET_HPP_INCLUDED + diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp new file mode 100644 index 00000000..5b3fdb5f --- /dev/null +++ b/dgl/TopLevelWidget.hpp @@ -0,0 +1,50 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_TOP_LEVEL_WIDGET_HPP_INCLUDED +#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED + +#include "Widget.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +/** + Top-Level Widget class. + + This is the only Widget class that is allowed to be used directly on a Window. + + This widget takes the full size of the Window it is mapped to. + Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent. + Doing so allows for custom position and sizes. + */ +class TopLevelWidget : public Widget +{ +public: + explicit TopLevelWidget(Window& windowToMapTo); + +private: + struct PrivateData; + PrivateData* const pData; + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index e863dd97..9a993cbd 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -17,9 +17,7 @@ #ifndef DGL_WIDGET_HPP_INCLUDED #define DGL_WIDGET_HPP_INCLUDED -#include "Geometry.hpp" - -#include +#include "Events.hpp" // ----------------------------------------------------------------------- // Forward class names @@ -33,9 +31,13 @@ END_NAMESPACE_DISTRHO START_NAMESPACE_DGL // class Application; -class NanoWidget; +// class NanoWidget; class Window; -class StandaloneWindow; +// class StandaloneWindow; +class SubWidget; +class TopLevelWidget; + +using namespace Events; // ----------------------------------------------------------------------- @@ -59,209 +61,13 @@ class StandaloneWindow; */ class Widget { -public: - /** - Base event data. - These are the fields present on all Widget events. - - @a mod Currently active keyboard modifiers, @see Modifier. - @a mod Event flags, @see Flag. - @a time Event timestamp (if any). - */ - struct BaseEvent { - uint mod; - uint flags; - uint time; - - /** Constuctor */ - BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} - /** Destuctor */ - virtual ~BaseEvent() noexcept {} - }; - - /** - Keyboard event. - - This event represents low-level key presses and releases. - This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. - - Keys are represented portably as Unicode code points, using the "natural" code point for the key. - The @a key field is the code for the pressed key, without any modifiers applied. - For example, a press or release of the 'A' key will have `key` 97 ('a') - regardless of whether shift or control are being held. - - Alternatively, the raw @a keycode can be used to work directly with physical keys, - but note that this value is not portable and differs between platforms and hardware. - - @a press True if the key was pressed, false if released. - @a key Unicode point of the key pressed. - @a keycode Raw keycode. - @see onKeyboard - */ - struct KeyboardEvent : BaseEvent { - bool press; - uint key; - uint keycode; - - /** Constuctor */ - KeyboardEvent() noexcept - : BaseEvent(), - press(false), - key(0), - keycode(0) {} - }; - - /** - Special keyboard event. - - This event allows the use of keys that do not have unicode points. - Note that some are non-printable keys. - - @a press True if the key was pressed, false if released. - @a key The key pressed. - @see onSpecial - */ - struct SpecialEvent : BaseEvent { - bool press; - Key key; - - /** Constuctor */ - SpecialEvent() noexcept - : BaseEvent(), - press(false), - key(Key(0)) {} - }; - - /** - Character input event. - - This event represents text input, usually as the result of a key press. - The text is given both as a Unicode character code and a UTF-8 string. - - Note that this event is generated by the platform's input system, - so there is not necessarily a direct correspondence between text events and physical key presses. - For example, with some input methods a sequence of several key presses will generate a single character. - - @a keycode Raw key code. - @a character Unicode character code. - @a string UTF-8 string. - @see onCharacterInput - */ - struct CharacterInputEvent : BaseEvent { - uint keycode; - uint character; - char string[8]; - - /** Constuctor */ - CharacterInputEvent() noexcept - : BaseEvent(), - keycode(0), - character(0), - string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} - }; - - /** - Mouse press or release event. - - @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). - @a press True if the button was pressed, false if released. - @a pos The widget-relative coordinates of the pointer. - @see onMouse - */ - struct MouseEvent : BaseEvent { - uint button; - bool press; - Point pos; - - /** Constuctor */ - MouseEvent() noexcept - : BaseEvent(), - button(0), - press(false), - pos(0.0, 0.0) {} - }; - - /** - Mouse motion event. - - @a pos The widget-relative coordinates of the pointer. - @see onMotion - */ - struct MotionEvent : BaseEvent { - Point pos; - - /** Constuctor */ - MotionEvent() noexcept - : BaseEvent(), - pos(0.0, 0.0) {} - }; - - /** - Mouse scroll event. - - The scroll distance is expressed in "lines", - an arbitrary unit that corresponds to a single tick of a detented mouse wheel. - For example, `delta.y` = 1.0 scrolls 1 line up. - Some systems and devices support finer resolution and/or higher values for fast scrolls, - so programs should handle any value gracefully. - - @a pos The widget-relative coordinates of the pointer. - @a delta The scroll distance. - @see onScroll - */ - struct ScrollEvent : BaseEvent { - Point pos; - Point delta; - - /** Constuctor */ - ScrollEvent() noexcept - : BaseEvent(), - pos(0.0, 0.0), - delta(0.0, 0.0) {} - }; - /** - Resize event. - @a size The new widget size. - @a oldSize The previous size, may be null. - @see onResize + Constructor, reserved for DGL .. + use TopLevelWidget or SubWidget instead */ - struct ResizeEvent { - Size size; - Size oldSize; - - /** Constuctor */ - ResizeEvent() noexcept - : size(0, 0), - oldSize(0, 0) {} - }; - - /** - Widget position changed event. - @a pos The new absolute position of the widget. - @a oldPos The previous absolute position of the widget. - @see onPositionChanged - */ - struct PositionChangedEvent { - Point pos; - Point oldPos; - - /** Constuctor */ - PositionChangedEvent() noexcept - : pos(0, 0), - oldPos(0, 0) {} - }; - - /** - Constructor. - */ - explicit Widget(Window& parent); - - /** - Constructor for a subwidget. - */ - explicit Widget(Widget* groupWidget); + explicit Widget(TopLevelWidget& topLevelWidget); +public: /** Destructor. */ @@ -326,68 +132,13 @@ public: void setSize(const Size& size) noexcept; /** - Get absolute X. + Get top-level widget, as passed in the constructor. */ - int getAbsoluteX() const noexcept; - - /** - Get absolute Y. - */ - int getAbsoluteY() const noexcept; - - /** - Get absolute position. - */ - const Point& getAbsolutePos() const noexcept; - - /** - Set absolute X. - */ - void setAbsoluteX(int x) noexcept; - - /** - Set absolute Y. - */ - void setAbsoluteY(int y) noexcept; - - /** - Set absolute position using @a x and @a y values. - */ - void setAbsolutePos(int x, int y) noexcept; - - /** - Set absolute position. - */ - void setAbsolutePos(const Point& pos) noexcept; - -#if 0 - // TODO: should we remove this? - /** - Get this widget's window application. - Same as calling getParentWindow().getApp(). - */ - Application& getParentApp() const noexcept; -#endif - - /** - Get parent window, as passed in the constructor. - */ - Window& getParentWindow() const noexcept; - - /** - Check if this widget contains the point defined by @a x and @a y. - */ - template - bool contains(T x, T y) const noexcept; - - /** - Check if this widget contains the point @a pos. - */ - template - bool contains(const Point& pos) const noexcept; + TopLevelWidget& getTopLevelWidget() const noexcept; /** Tell this widget's window to repaint itself. + FIXME better description, partial redraw */ void repaint() noexcept; @@ -450,23 +201,14 @@ protected: */ virtual void onResize(const ResizeEvent&); - /** - A function called when the widget's absolute position is changed. - */ - virtual void onPositionChanged(const PositionChangedEvent&); - - void setNeedsFullViewport(); - private: struct PrivateData; PrivateData* const pData; - /** @internal */ - explicit Widget(Widget* groupWidget, bool addToSubWidgets); - - friend class NanoWidget; - friend class Window; - friend class StandaloneWindow; +// friend class NanoWidget; +// friend class Window; +// friend class StandaloneWindow; + friend class SubWidget; #ifdef DISTRHO_DEFINES_H_INCLUDED friend class DISTRHO_NAMESPACE::UI; #endif @@ -474,6 +216,20 @@ private: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) }; +#if 0 + // TODO: should we remove this? + /** + Get this widget's window application. + Same as calling getParentWindow().getApp(). + */ + Application& getParentApp() const noexcept; + + /** + Get parent window, as passed in the constructor. + */ + Window& getParentWindow() const noexcept; +#endif + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/StandaloneWindow.cpp b/dgl/src/StandaloneWindow.cpp index 71dc9a27..735acd04 100644 --- a/dgl/src/StandaloneWindow.cpp +++ b/dgl/src/StandaloneWindow.cpp @@ -39,7 +39,8 @@ void StandaloneWindow::onReshape(uint width, uint height) Window::onReshape(width, height); } -void StandaloneWindow::_addWidget(Widget* widget) +#if 0 +void StandaloneWindow::_addWidget(TopLevelWidget* widget) { if (fWidget == nullptr) { @@ -49,7 +50,7 @@ void StandaloneWindow::_addWidget(Widget* widget) Window::_addWidget(widget); } -void StandaloneWindow::_removeWidget(Widget* widget) +void StandaloneWindow::_removeWidget(TopLevelWidget* widget) { if (fWidget == widget) { @@ -58,6 +59,7 @@ void StandaloneWindow::_removeWidget(Widget* widget) } Window::_removeWidget(widget); } +#endif // ----------------------------------------------------------------------- diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp new file mode 100644 index 00000000..b987c518 --- /dev/null +++ b/dgl/src/SubWidget.cpp @@ -0,0 +1,79 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "SubWidgetPrivateData.hpp" + +template +bool SubWidget::contains(T x, T y) const noexcept +{ + const Size& size(getSize()); + return (x >= 0 && y >= 0 && static_cast(x) < size.getWidth() && static_cast(y) < size.getHeight()); +} + +template +bool SubWidget::contains(const Point& pos) const noexcept +{ + return contains(pos.getX(), pos.getY()); +} + +int SubWidget::getAbsoluteX() const noexcept +{ + return pData->absolutePos.getX(); +} + +int SubWidget::getAbsoluteY() const noexcept +{ + return pData->absolutePos.getY(); +} + +const Point& SubWidget::getAbsolutePos() const noexcept +{ + return pData->absolutePos; +} + +void SubWidget::setAbsoluteX(int x) noexcept +{ + setAbsolutePos(Point(x, getAbsoluteY())); +} + +void SubWidget::setAbsoluteY(int y) noexcept +{ + setAbsolutePos(Point(getAbsoluteX(), y)); +} + +void SubWidget::setAbsolutePos(int x, int y) noexcept +{ + setAbsolutePos(Point(x, y)); +} + +void SubWidget::setAbsolutePos(const Point& pos) noexcept +{ + if (pData->absolutePos == pos) + return; + + PositionChangedEvent ev; + ev.oldPos = pData->absolutePos; + ev.pos = pos; + + pData->absolutePos = pos; + onPositionChanged(ev); + + getTopLevelWidget().repaint(); +} + +void SubWidget::onPositionChanged(const PositionChangedEvent&) +{ +} diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp new file mode 100644 index 00000000..fb9d490b --- /dev/null +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -0,0 +1,49 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED +#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED + +#include "../SubWidget.hpp" +#include "../WidgetPrivateData.hpp" + +#include + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +struct SubWidget::PrivateData { + SubWidget* const self; + Widget* const groupWidget; + Point absolutePos; + + PrivateData(SubWidget* const s, Widget* const g) + : self(s), + groupWidget(g), + absolutePos() + { + groupWidget->pData->subWidgets.push_back(self); + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp new file mode 100644 index 00000000..782bd6af --- /dev/null +++ b/dgl/src/TopLevelWidget.cpp @@ -0,0 +1,17 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "TopLevelWidgetPrivateData.hpp" diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp new file mode 100644 index 00000000..7f394c8b --- /dev/null +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -0,0 +1,42 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED +#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED + +#include "../TopLevelWidget.hpp" +// #include "../WidgetPrivateData.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +struct TopLevelWidget::PrivateData { + TopLevelWidget* const self; + Window& window; + + PrivateData(TopLevelWidget* const s, Window& w) + : self(s), + window(w) {} + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index b23058e8..3ad64d70 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -21,20 +21,8 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Widget -Widget::Widget(Window& parent) - : pData(new PrivateData(this, parent, nullptr, false)) -{ -} - -Widget::Widget(Widget* groupWidget) - : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)) -{ -} - -Widget::Widget(Widget* groupWidget, bool addToSubWidgets) - : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)) -{ -} +Widget::Widget(TopLevelWidget& tlw) + : pData(new PrivateData(this, tlw)) {} Widget::~Widget() { @@ -52,7 +40,7 @@ void Widget::setVisible(bool yesNo) return; pData->visible = yesNo; - pData->parent.repaint(); + pData->topLevelWidget.repaint(); } void Widget::show() @@ -92,7 +80,7 @@ void Widget::setWidth(uint width) noexcept pData->size.setWidth(width); onResize(ev); - pData->parent.repaint(); + pData->topLevelWidget.repaint(); } void Widget::setHeight(uint height) noexcept @@ -107,7 +95,7 @@ void Widget::setHeight(uint height) noexcept pData->size.setHeight(height); onResize(ev); - pData->parent.repaint(); + pData->topLevelWidget.repaint(); } void Widget::setSize(uint width, uint height) noexcept @@ -127,52 +115,7 @@ void Widget::setSize(const Size& size) noexcept pData->size = size; onResize(ev); - pData->parent.repaint(); -} - -int Widget::getAbsoluteX() const noexcept -{ - return pData->absolutePos.getX(); -} - -int Widget::getAbsoluteY() const noexcept -{ - return pData->absolutePos.getY(); -} - -const Point& Widget::getAbsolutePos() const noexcept -{ - return pData->absolutePos; -} - -void Widget::setAbsoluteX(int x) noexcept -{ - setAbsolutePos(Point(x, getAbsoluteY())); -} - -void Widget::setAbsoluteY(int y) noexcept -{ - setAbsolutePos(Point(getAbsoluteX(), y)); -} - -void Widget::setAbsolutePos(int x, int y) noexcept -{ - setAbsolutePos(Point(x, y)); -} - -void Widget::setAbsolutePos(const Point& pos) noexcept -{ - if (pData->absolutePos == pos) - return; - - PositionChangedEvent ev; - ev.oldPos = pData->absolutePos; - ev.pos = pos; - - pData->absolutePos = pos; - onPositionChanged(ev); - - pData->parent.repaint(); + pData->topLevelWidget.repaint(); } #if 0 @@ -182,26 +125,15 @@ Application& Widget::getParentApp() const noexcept } #endif -Window& Widget::getParentWindow() const noexcept -{ - return pData->parent; -} - -template -bool Widget::contains(T x, T y) const noexcept -{ - return (x >= 0 && y >= 0 && static_cast(x) < pData->size.getWidth() && static_cast(y) < pData->size.getHeight()); -} - -template -bool Widget::contains(const Point& pos) const noexcept +TopLevelWidget& Widget::getTopLevelWidget() const noexcept { - return contains(pos.getX(), pos.getY()); + return pData->topLevelWidget; } void Widget::repaint() noexcept { - pData->parent.repaint(); + // FIXME partial repaint + // pData->topLevelWidget.repaint(); } uint Widget::getId() const noexcept @@ -248,14 +180,33 @@ void Widget::onResize(const ResizeEvent&) { } -void Widget::onPositionChanged(const PositionChangedEvent&) +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- + +#if 0 +Widget::Widget(Widget* groupWidget) + : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)) +{ +} + +Widget::Widget(Widget* groupWidget, bool addToSubWidgets) + : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)) +{ +} + +Window& Widget::getParentWindow() const noexcept { + return pData->parent; } void Widget::setNeedsFullViewport() { pData->needsFullViewport = true; } +#endif // ----------------------------------------------------------------------- diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index c968054d..1007eafd 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -17,7 +17,7 @@ #ifndef DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED #define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED -#include "../Widget.hpp" +#include "../TopLevelWidget.hpp" #include "../Window.hpp" #include @@ -28,41 +28,29 @@ START_NAMESPACE_DGL struct Widget::PrivateData { Widget* const self; - Window& parent; - Point absolutePos; Size size; std::vector subWidgets; + TopLevelWidget& topLevelWidget; uint id; - bool needsFullViewport; bool needsScaling; - bool skipDisplay; bool visible; - PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets) + PrivateData(Widget* const s, TopLevelWidget& tlw) : self(s), - parent(p), - absolutePos(0, 0), size(0, 0), subWidgets(), + topLevelWidget(tlw), id(0), - needsFullViewport(false), needsScaling(false), - skipDisplay(false), visible(true) { - if (addToSubWidgets && groupWidget != nullptr) - { - skipDisplay = true; - groupWidget->pData->subWidgets.push_back(self); - } - - parent._addWidget(self); +// parent._addWidget(self); } ~PrivateData() { - parent._removeWidget(self); +// parent._removeWidget(self); subWidgets.clear(); } From 291c88576afb2e2ac71ea05fe2d3069aebc9ed55 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 29 Mar 2021 10:25:18 +0100 Subject: [PATCH 016/159] Continue split implementation --- dgl/SubWidget.hpp | 9 +++++++++ dgl/TopLevelWidget.hpp | 8 ++++++++ dgl/src/SubWidget.cpp | 8 ++++++++ dgl/src/TopLevelWidget.cpp | 8 ++++++++ 4 files changed, 33 insertions(+) diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 81c8ad59..45778703 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -23,6 +23,15 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- +/** + Sub-Widget class. + + This is a handy Widget class that can be freely positioned to be used directly on a Window. + + This widget takes the full size of the Window it is mapped to. + Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent. + Doing so allows for custom position and sizes. + */ class SubWidget : public Widget { public: diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 5b3fdb5f..056cbac8 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -35,8 +35,16 @@ START_NAMESPACE_DGL class TopLevelWidget : public Widget { public: + /** + Constructor. + */ explicit TopLevelWidget(Window& windowToMapTo); + /** + Destructor. + */ + virtual ~TopLevelWidget(); + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index b987c518..f7934d75 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -16,6 +16,14 @@ #include "SubWidgetPrivateData.hpp" +SubWidget::SubWidget(Widget* const widgetToGroupTo) + : pData(new PrivateData(this, widgetToGroupTo)) {} + +SubWidget::~SubWidget() +{ + delete pData; +} + template bool SubWidget::contains(T x, T y) const noexcept { diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 782bd6af..903ccdca 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -15,3 +15,11 @@ */ #include "TopLevelWidgetPrivateData.hpp" + +TopLevelWidget::TopLevelWidget(Window& windowToMapTo) + : pData(new PrivateData(this, windowToMapTo)) {} + +TopLevelWidget::~TopLevelWidget() +{ + delete pData; +} From 60f8c9d985a4f38bb645efa26484e50f8fbc8920 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 30 Apr 2021 20:36:23 +0100 Subject: [PATCH 017/159] Only build stuff that works for now Signed-off-by: falkTX --- Makefile | 3 +- dgl/Makefile | 96 ++++++++++++++++++++++++++-------------------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index f57cc1e9..4819c9dc 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ include Makefile.base.mk -all: dgl examples gen +all: dgl +# examples gen # -------------------------------------------------------------- diff --git a/dgl/Makefile b/dgl/Makefile index 221d8857..1aee6ebc 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -24,24 +24,24 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra -Wno-narrowing OBJS_common = \ ../build/dgl/Application.cpp.o \ - ../build/dgl/ApplicationPrivateData.cpp.o \ - ../build/dgl/Color.cpp.o \ - ../build/dgl/Geometry.cpp.o \ - ../build/dgl/ImageBase.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 + ../build/dgl/ApplicationPrivateData.cpp.o +# ../build/dgl/Color.cpp.o \ +# ../build/dgl/Geometry.cpp.o \ +# ../build/dgl/ImageBase.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 # --------------------------------------------------------------------------------------------------------------------- -OBJS_cairo = $(OBJS_common) \ - ../build/dgl/Cairo.cpp.cairo.o \ - ../build/dgl/WidgetPrivateData.cpp.cairo.o \ - ../build/dgl/WindowPrivateData.cpp.cairo.o +OBJS_cairo = $(OBJS_common) +# ../build/dgl/Cairo.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 @@ -51,13 +51,13 @@ OBJS_cairo = $(OBJS_common) \ # --------------------------------------------------------------------------------------------------------------------- -OBJS_opengl = $(OBJS_common) \ - ../build/dgl/OpenGL.cpp.opengl.o \ - ../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/WindowPrivateData.cpp.opengl.o +OBJS_opengl = $(OBJS_common) +# ../build/dgl/OpenGL.cpp.opengl.o \ +# ../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/WindowPrivateData.cpp.opengl.o # ifeq ($(MACOS),true) # OBJS_opengl += ../build/dgl/Window.mm.opengl.o @@ -114,37 +114,37 @@ all: $(TARGETS) # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.cairo.o: src/%.cpp - -@mkdir -p ../build/dgl - @echo "Compiling $< (Cairo variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ - -../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* - -@mkdir -p ../build/dgl - @echo "Compiling $< (Cairo variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ - -../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* - -@mkdir -p ../build/dgl - @echo "Compiling $< (Cairo variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@ +# ../build/dgl/%.cpp.cairo.o: src/%.cpp +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (Cairo variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ + +# ../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (Cairo variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ +# +# ../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (Cairo variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.opengl.o: src/%.cpp - -@mkdir -p ../build/dgl - @echo "Compiling $< (OpenGL variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ - -../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* - -@mkdir -p ../build/dgl - @echo "Compiling $< (OpenGL variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ - -../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* - -@mkdir -p ../build/dgl - @echo "Compiling $< (OpenGL variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@ +# ../build/dgl/%.cpp.opengl.o: src/%.cpp +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (OpenGL variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ + +# ../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (OpenGL variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ +# +# ../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* +# -@mkdir -p ../build/dgl +# @echo "Compiling $< (OpenGL variant)" +# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@ # --------------------------------------------------------------------------------------------------------------------- From cbb95d5855b97321370f11cd0f03279bc5d2acdb Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 30 Apr 2021 21:53:47 +0100 Subject: [PATCH 018/159] Wrap the entire pugl in a cpp file so we can namespace it Signed-off-by: falkTX --- dgl/Makefile | 3 +- dgl/src/ApplicationPrivateData.cpp | 15 ++++--- dgl/src/ApplicationPrivateData.hpp | 5 +-- dgl/src/pugl.cpp | 65 ++++++++++++++++++++++++++++++ dgl/src/pugl.hpp | 37 +++++++++++++++++ 5 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 dgl/src/pugl.cpp create mode 100644 dgl/src/pugl.hpp diff --git a/dgl/Makefile b/dgl/Makefile index 1aee6ebc..a76490a4 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -9,7 +9,8 @@ include ../Makefile.base.mk # --------------------------------------------------------------------------------------------------------------------- BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc -BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -Isrc/pugl-upstream/include -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter +BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter +# -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) # TODO fix these after pugl-upstream is done diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 24a48def..83bed9e2 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -17,7 +17,7 @@ #include "ApplicationPrivateData.hpp" #include "../Window.hpp" -#include "pugl-upstream/include/pugl/pugl.h" +#include "pugl.hpp" START_NAMESPACE_DGL @@ -27,10 +27,11 @@ Application::PrivateData::PrivateData(const bool standalone) : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, standalone ? PUGL_WORLD_THREADS : 0x0)), isStandalone(standalone), - isStarting(true), isQuitting(false), visibleWindows(0), +#ifndef DPF_TEST_APPLICATION_CPP windows(), +#endif idleCallbacks() { DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); @@ -44,11 +45,12 @@ Application::PrivateData::PrivateData(const bool standalone) Application::PrivateData::~PrivateData() { - DISTRHO_SAFE_ASSERT(!isStarting); DISTRHO_SAFE_ASSERT(isQuitting); DISTRHO_SAFE_ASSERT(visibleWindows == 0); +#ifndef DPF_TEST_APPLICATION_CPP windows.clear(); +#endif idleCallbacks.clear(); if (world != nullptr) @@ -62,10 +64,7 @@ void Application::PrivateData::oneWindowShown() noexcept DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); if (++visibleWindows == 1) - { - isStarting = false; isQuitting = false; - } } void Application::PrivateData::oneWindowHidden() noexcept @@ -81,11 +80,13 @@ void Application::PrivateData::idle(const uint timeoutInMs) { puglUpdate(world, timeoutInMs == 0 ? 0.0 : static_cast(timeoutInMs) / 1000.0); +#ifndef DPF_TEST_APPLICATION_CPP for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) { Window* const window(*it); window->_idle(); } +#endif for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) { @@ -98,11 +99,13 @@ void Application::PrivateData::quit() { isQuitting = true; +#ifndef DPF_TEST_APPLICATION_CPP for (std::list::reverse_iterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) { Window* const window(*rit); window->close(); } +#endif } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 3d6910d0..8aa858c0 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -36,9 +36,6 @@ struct Application::PrivateData { /** Whether the application is running as standalone, otherwise it is part of a plugin. */ const bool isStandalone; - /** Whether the applicating is starting up, not yet fully initialized. Defaults to true. */ - bool isStarting; - /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ bool isQuitting; @@ -46,8 +43,10 @@ struct Application::PrivateData { If 0->1, application is starting. If 1->0, application is quitting/stopping. */ uint visibleWindows; +#ifndef DPF_TEST_APPLICATION_CPP /** List of windows for this application. Used as a way to call each window `idle`. */ std::list windows; +#endif /** List of idle callbacks for this application. Run after all windows `idle`. */ std::list idleCallbacks; diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp new file mode 100644 index 00000000..a43e626c --- /dev/null +++ b/dgl/src/pugl.cpp @@ -0,0 +1,65 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "pugl.hpp" + +/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ +#include +#include +#include +#include +#include + +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else +# include +# include +# include +# include +# include +# include +# include +# ifdef HAVE_XRANDR +# include +# endif +# ifdef HAVE_XSYNC +# include +# include +# endif +# ifdef HAVE_XCURSOR +# include +# include +# endif +#endif + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else +# include "pugl-upstream/src/x11.c" +#endif + +#include "pugl-upstream/src/implementation.c" + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp new file mode 100644 index 00000000..1fb58a34 --- /dev/null +++ b/dgl/src/pugl.hpp @@ -0,0 +1,37 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_PUGL_HPP_INCLUDED +#define DGL_PUGL_HPP_INCLUDED + +#include "../Base.hpp" + +/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ +#include +#include +#include + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +#include "pugl-upstream/include/pugl/pugl.h" + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_PUGL_HPP_INCLUDED From 1f2f2e5c4e2bf146af73fc1f28a8c253a516fa9f Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 30 Apr 2021 21:54:40 +0100 Subject: [PATCH 019/159] Start of DPF tests Signed-off-by: falkTX --- tests/Application.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 73 +++++++++++++++++++++++++++++++++++++++ tests/README.txt | 6 ++++ 3 files changed, 159 insertions(+) create mode 100644 tests/Application.cpp create mode 100644 tests/Makefile create mode 100644 tests/README.txt diff --git a/tests/Application.cpp b/tests/Application.cpp new file mode 100644 index 00000000..6a43b6cf --- /dev/null +++ b/tests/Application.cpp @@ -0,0 +1,80 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +#define DPF_TEST_APPLICATION_CPP +#include "dgl/src/pugl.cpp" +#include "dgl/src/Application.cpp" +#include "dgl/src/ApplicationPrivateData.cpp" + +#include "distrho/extra/Thread.hpp" + +#define DISTRHO_ASSERT_EQUAL(v1, v2, msg) \ + if (v1 != v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +class ApplicationQuitter : public Thread +{ + Application& app; + +public: + ApplicationQuitter(Application& a) + : Thread("ApplicationQuitter"), + app(a) + { + startThread(); + } + +private: + void run() override + { + d_sleep(5); + app.quit(); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +int main() +{ + USE_NAMESPACE_DGL; + + // regular usage + { + Application app(true); + DISTRHO_ASSERT_EQUAL(app.isQuiting(), false, "app MUST NOT be set as quitting during init"); + app.idle(); + DISTRHO_ASSERT_EQUAL(app.isQuiting(), false, "app MUST NOT be set as quitting after idle()"); + app.quit(); + DISTRHO_ASSERT_EQUAL(app.isQuiting(), true, "app MUST be set as quitting after quit()"); + } + + // standalone exec, must not block forever due to quit() called from another thread + { + Application app(true); + ApplicationQuitter appQuitter(app); + app.exec(); + DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..0174d33f --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,73 @@ +#!/usr/bin/make -f +# Makefile for DGL # +# ---------------- # +# Created by falkTX +# + +# debug mode by default +DEBUG=true + +include ../Makefile.base.mk + +# --------------------------------------------------------------------------------------------------------------------- + +BUILD_C_FLAGS += $(DGL_FLAGS) -I.. +BUILD_CXX_FLAGS += $(DGL_FLAGS) -I.. -I../dgl/src/pugl-upstream/include -DDONT_SET_USING_DGL_NAMESPACE +LINK_FLAGS += -lpthread + +# TODO fix within pugl +BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra + +# --------------------------------------------------------------------------------------------------------------------- + +# TODO +src=Application.cpp + +TESTS = $(src:%.cpp=../build/tests/%) +OBJS = $(TESTS:%=%.o) + +# --------------------------------------------------------------------------------------------------------------------- + +ifeq ($(HAVE_CAIRO),true) +endif + +ifeq ($(HAVE_OPENGL),true) +endif + +# --------------------------------------------------------------------------------------------------------------------- + +all: $(TESTS) + +# --------------------------------------------------------------------------------------------------------------------- + +../build/tests/%: ../build/tests/%.cpp.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ + $@ + valgrind --leak-check=full $@ + +# --------------------------------------------------------------------------------------------------------------------- + +../build/tests/%.c.o: %.c + -@mkdir -p ../build/tests + @echo "Compiling $<" + $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ + +../build/tests/%.cpp.o: %.cpp + -@mkdir -p ../build/tests + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + +# --------------------------------------------------------------------------------------------------------------------- + +clean: + rm -rf ../build/tests + +debug: + $(MAKE) DEBUG=true + +# --------------------------------------------------------------------------------------------------------------------- + +-include $(OBJS:%.o=%.d) + +# --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/README.txt b/tests/README.txt new file mode 100644 index 00000000..4d597ac6 --- /dev/null +++ b/tests/README.txt @@ -0,0 +1,6 @@ +This directory contains several tests for DPF related things, from graphics to plugin stuff to utilities. +Each *.cpp file is meant to be its own test. + +In order to test DPF components individually, these tests do not link against DGL directly, +but will directly import the needed source code. +All files must be self-contained, in order to prevent surprises in regards global state and initialization stuff. From d5c8ba4af3e74367808cae3c9b4f68adb960550f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 13:52:41 +0100 Subject: [PATCH 020/159] Add placeholder files for tests --- tests/Application.cpp | 3 +++ tests/Circle.cpp | 28 ++++++++++++++++++++++++++++ tests/Color.cpp | 28 ++++++++++++++++++++++++++++ tests/Line.cpp | 28 ++++++++++++++++++++++++++++ tests/Point.cpp | 28 ++++++++++++++++++++++++++++ tests/Rectangle.cpp | 28 ++++++++++++++++++++++++++++ tests/Triangle.cpp | 28 ++++++++++++++++++++++++++++ 7 files changed, 171 insertions(+) create mode 100644 tests/Circle.cpp create mode 100644 tests/Color.cpp create mode 100644 tests/Line.cpp create mode 100644 tests/Point.cpp create mode 100644 tests/Rectangle.cpp create mode 100644 tests/Triangle.cpp diff --git a/tests/Application.cpp b/tests/Application.cpp index 6a43b6cf..e549cfd0 100644 --- a/tests/Application.cpp +++ b/tests/Application.cpp @@ -74,6 +74,9 @@ int main() DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); } + // TODO test idle callback + // TODO test idle is called when exec(0) is used + return 0; } diff --git a/tests/Circle.cpp b/tests/Circle.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Circle.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Color.cpp b/tests/Color.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Color.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Line.cpp b/tests/Line.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Line.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Point.cpp b/tests/Point.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Point.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Rectangle.cpp b/tests/Rectangle.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Rectangle.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Triangle.cpp b/tests/Triangle.cpp new file mode 100644 index 00000000..578688c6 --- /dev/null +++ b/tests/Triangle.cpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- From 9c2f68f6bfa3de2e73080fa665c7f88b7debdb67 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 14:31:41 +0100 Subject: [PATCH 021/159] Increase line length Signed-off-by: falkTX --- dgl/Application.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 2a561691..074a29bf 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -32,11 +32,9 @@ class Window; Base DGL Application class. One application instance is required for creating a window. - There's no single/global application instance in DGL, and multiple - windows can share the same app instance. + There's no single/global application instance in DGL, and multiple windows can share the same app instance. - In standalone mode an application will automatically quit its - event-loop when all its windows are closed. + In standalone mode an application will automatically quit its event-loop when all its windows are closed. */ class Application { From 8ba6a786aa8472ad948fd865e2977ad4041a52a1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 14:54:33 +0100 Subject: [PATCH 022/159] Put IdleCallback stuff directly in Application class Signed-off-by: falkTX --- dgl/Application.hpp | 13 +++++++++++++ dgl/src/Application.cpp | 14 ++++++++++++++ dgl/src/ApplicationPrivateData.cpp | 6 +++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 074a29bf..cf634b3a 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -76,6 +76,19 @@ public: */ bool isQuiting() const noexcept; + /** + Add a callback function to be triggered on every idle cycle. + You can add more than one, and remove them at anytime with removeIdleCallback(). + Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle). + There are no guarantees in terms of timing. + */ + void addIdleCallback(IdleCallback* const callback); + + /** + Remove an idle callback previously added via addIdleCallback(). + */ + void removeIdleCallback(IdleCallback* const callback); + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 23db6a4f..cd6e8a34 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -53,6 +53,20 @@ bool Application::isQuiting() const noexcept return pData->isQuitting; } +void Application::addIdleCallback(IdleCallback* const callback) +{ + DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) + + pData->idleCallbacks.push_back(callback); +} + +void Application::removeIdleCallback(IdleCallback* const callback) +{ + DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) + + pData->idleCallbacks.remove(callback); +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 83bed9e2..bd246cbb 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -78,7 +78,11 @@ void Application::PrivateData::oneWindowHidden() noexcept void Application::PrivateData::idle(const uint timeoutInMs) { - puglUpdate(world, timeoutInMs == 0 ? 0.0 : static_cast(timeoutInMs) / 1000.0); + const double timeoutInSeconds = timeoutInMs != 0 + ? static_cast(timeoutInMs) / 1000.0 + : 0.0; + + puglUpdate(world, timeoutInSeconds); #ifndef DPF_TEST_APPLICATION_CPP for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) From 7f721e08e15a21cd9799e038d6cc11e3d9d604bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 14:55:07 +0100 Subject: [PATCH 023/159] Complete application tests Signed-off-by: falkTX --- tests/Application.cpp | 44 +++++++++++++++++++++++++++++++++++++------ tests/Makefile | 2 +- tests/tests.hpp | 23 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 tests/tests.hpp diff --git a/tests/Application.cpp b/tests/Application.cpp index e549cfd0..2bb911ab 100644 --- a/tests/Application.cpp +++ b/tests/Application.cpp @@ -14,6 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + #define DPF_TEST_APPLICATION_CPP #include "dgl/src/pugl.cpp" #include "dgl/src/Application.cpp" @@ -21,9 +23,6 @@ #include "distrho/extra/Thread.hpp" -#define DISTRHO_ASSERT_EQUAL(v1, v2, msg) \ - if (v1 != v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } - START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- @@ -43,13 +42,28 @@ public: private: void run() override { - d_sleep(5); + d_sleep(2); app.quit(); } }; // -------------------------------------------------------------------------------------------------------------------- +struct IdleCallbackCounter : IdleCallback +{ + int counter; + + IdleCallbackCounter() + : counter(0) {} + + void idleCallback() override + { + ++counter; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL int main() @@ -59,23 +73,41 @@ int main() // regular usage { Application app(true); + IdleCallbackCounter idleCounter; + app.addIdleCallback(&idleCounter); DISTRHO_ASSERT_EQUAL(app.isQuiting(), false, "app MUST NOT be set as quitting during init"); + DISTRHO_ASSERT_EQUAL(idleCounter.counter, 0, "app MUST NOT have triggered idle callbacks yet"); app.idle(); DISTRHO_ASSERT_EQUAL(app.isQuiting(), false, "app MUST NOT be set as quitting after idle()"); + DISTRHO_ASSERT_EQUAL(idleCounter.counter, 1, "app MUST have triggered 1 idle callback"); + app.idle(); + DISTRHO_ASSERT_EQUAL(idleCounter.counter, 2, "app MUST have triggered 2 idle callbacks"); app.quit(); DISTRHO_ASSERT_EQUAL(app.isQuiting(), true, "app MUST be set as quitting after quit()"); + DISTRHO_ASSERT_EQUAL(idleCounter.counter, 2, "app MUST have triggered only 2 idle callbacks in its lifetime"); } // standalone exec, must not block forever due to quit() called from another thread { Application app(true); ApplicationQuitter appQuitter(app); + IdleCallbackCounter idleCounter; + app.addIdleCallback(&idleCounter); app.exec(); DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); + DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks not triggered"); } - // TODO test idle callback - // TODO test idle is called when exec(0) is used + // standalone exec, but with 0 as timeout + { + Application app(true); + ApplicationQuitter appQuitter(app); + IdleCallbackCounter idleCounter; + app.addIdleCallback(&idleCounter); + app.exec(0); + DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); + DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks not triggered"); + } return 0; } diff --git a/tests/Makefile b/tests/Makefile index 0174d33f..c9150549 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -44,7 +44,7 @@ all: $(TESTS) @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ $@ - valgrind --leak-check=full $@ +# valgrind --leak-check=full $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/tests.hpp b/tests/tests.hpp new file mode 100644 index 00000000..f3aeae85 --- /dev/null +++ b/tests/tests.hpp @@ -0,0 +1,23 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "dgl/Base.hpp" + +#define DISTRHO_ASSERT_EQUAL(v1, v2, msg) \ + if (v1 != v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } + +#define DISTRHO_ASSERT_NOT_EQUAL(v1, v2, msg) \ + if (v1 == v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } From 717c641f3225c9d29345f3855c1f9428e77f1ae5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 14:57:37 +0100 Subject: [PATCH 024/159] Use https for pugl as git submodule Signed-off-by: falkTX --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 2d4619a5..7469d8ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "dgl/src/pugl-upstream"] path = dgl/src/pugl-upstream - url = git@github.com:lv2/pugl.git + url = https://github.com/lv2/pugl.git From 277a5dfe18d8ca479947eea1e6d106955934536c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:23:05 +0100 Subject: [PATCH 025/159] Fix Color::fromHTML, cleanup Signed-off-by: falkTX --- dgl/Color.hpp | 20 ++++----- dgl/src/ApplicationPrivateData.hpp | 2 +- dgl/src/Color.cpp | 71 ++++++++++++++---------------- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/dgl/Color.hpp b/dgl/Color.hpp index a874f660..1b611805 100644 --- a/dgl/Color.hpp +++ b/dgl/Color.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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,11 +19,11 @@ #include "Base.hpp" -struct NVGcolor; - START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +struct NVGcolor; + +// -------------------------------------------------------------------------------------------------------------------- /** A color made from red, green, blue and alpha floating-point values in [0..1] range. @@ -44,13 +44,13 @@ struct Color { /** Create a color from red, green, blue and alpha numeric values. - Values must be in [0..255] range. + All values except alpha must be in [0..255] range, with alpha in [0..1] range. */ - Color(int red, int green, int blue, int alpha = 255) noexcept; + Color(int red, int green, int blue, float alpha = 1.0f) noexcept; /** Create a color from red, green, blue and alpha floating-point values. - Values must in [0..1] range. + All values must in [0..1] range. */ Color(float red, float green, float blue, float alpha = 1.0f) noexcept; @@ -74,7 +74,7 @@ struct Color { /** Create a color from a HTML string like "#333" or "#112233". */ - static Color fromHTML(const char* rgb, float alpha = 1.0f); + static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; /** Linearly interpolate this color against another. @@ -83,7 +83,7 @@ struct Color { /** Check if this color matches another. - @note Comparison is forced within 8-bit color values. + @note Comparison is done within 8-bit color space. */ bool isEqual(const Color& color, bool withAlpha = true) noexcept; bool isNotEqual(const Color& color, bool withAlpha = true) noexcept; @@ -103,7 +103,7 @@ struct Color { operator NVGcolor() const noexcept; }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 8aa858c0..98e65dcd 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -57,7 +57,7 @@ struct Application::PrivateData { /** Flag one window shown or hidden status, which modifies @a visibleWindows. For standalone mode only. - Modifies @a isStarting and @a isQuitting under certain conditions */ + Modifies @a isQuitting under certain conditions */ void oneWindowShown() noexcept; void oneWindowHidden() noexcept; diff --git a/dgl/src/Color.cpp b/dgl/src/Color.cpp index 3fc570ca..07036490 100644 --- a/dgl/src/Color.cpp +++ b/dgl/src/Color.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -16,10 +16,6 @@ #include "../Color.hpp" -#ifndef HAVE_DCAIRO -#include "nanovg/nanovg.h" -#endif - START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -61,27 +57,27 @@ static uchar getFixedRange2(const float& value) return 0; if (value2 >= 255.0f) return 255; - return static_cast(value2); + return static_cast(value2 + 0.5f); } // ----------------------------------------------------------------------- Color::Color() noexcept - : red(1.0f), - green(1.0f), - blue(1.0f), + : red(0.0f), + green(0.0f), + blue(0.0f), alpha(1.0f) {} -Color::Color(int r, int g, int b, int a) noexcept +Color::Color(const int r, const int g, const int b, const float a) noexcept : red(static_cast(r)/255.0f), green(static_cast(g)/255.0f), blue(static_cast(b)/255.0f), - alpha(static_cast(a)/255.0f) + alpha(a) { fixBounds(); } -Color::Color(float r, float g, float b, float a) noexcept +Color::Color(const float r, const float g, const float b, const float a) noexcept : red(r), green(g), blue(b), @@ -109,7 +105,7 @@ Color& Color::operator=(const Color& color) noexcept return *this; } -Color::Color(const Color& color1, const Color& color2, float u) noexcept +Color::Color(const Color& color1, const Color& color2, const float u) noexcept : red(color1.red), green(color1.green), blue(color1.blue), @@ -136,65 +132,66 @@ Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) return col; } -Color Color::fromHTML(const char* rgb, float alpha) +Color Color::fromHTML(const char* rgb, const float alpha) noexcept { Color fallback; DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback); - if (rgb[0] == '#') ++rgb; + if (rgb[0] == '#') + ++rgb; DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback); - std::size_t rgblen(std::strlen(rgb)); + std::size_t rgblen = std::strlen(rgb); DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback); - char rgbtmp[3] = { '\0', '\0', '\0' }; + char rgbtmp[5] = { '0', 'x', '\0', '\0', '\0' }; int r, g, b; if (rgblen == 3) { - rgbtmp[0] = rgb[0]; - r = static_cast(std::strtol(rgbtmp, nullptr, 16)); + rgbtmp[2] = rgb[0]; + r = static_cast(std::strtol(rgbtmp, nullptr, 16)) * 17; - rgbtmp[0] = rgb[1]; - g = static_cast(std::strtol(rgbtmp, nullptr, 16)); + rgbtmp[2] = rgb[1]; + g = static_cast(std::strtol(rgbtmp, nullptr, 16)) * 17; - rgbtmp[0] = rgb[2]; - b = static_cast(std::strtol(rgbtmp, nullptr, 16)); + rgbtmp[2] = rgb[2]; + b = static_cast(std::strtol(rgbtmp, nullptr, 16)) * 17; } else { - rgbtmp[0] = rgb[0]; - rgbtmp[1] = rgb[1]; + rgbtmp[2] = rgb[0]; + rgbtmp[3] = rgb[1]; r = static_cast(std::strtol(rgbtmp, nullptr, 16)); - rgbtmp[0] = rgb[2]; - rgbtmp[1] = rgb[3]; + rgbtmp[2] = rgb[2]; + rgbtmp[3] = rgb[3]; g = static_cast(std::strtol(rgbtmp, nullptr, 16)); - rgbtmp[0] = rgb[4]; - rgbtmp[1] = rgb[5]; + rgbtmp[2] = rgb[4]; + rgbtmp[3] = rgb[5]; b = static_cast(std::strtol(rgbtmp, nullptr, 16)); } - return Color(r, g, b, static_cast(getFixedRange(alpha)*255.0f)); + return Color(r, g, b, alpha); } void Color::interpolate(const Color& other, float u) noexcept { fixRange(u); - const float oneMinusU(1.0f - u); + const float oneMinusU = 1.0f - u; - red = red * oneMinusU + other.red * u; - green = green * oneMinusU + other.green * u; - blue = blue * oneMinusU + other.blue * u; - alpha = alpha * oneMinusU + other.alpha * u; + red = (red * oneMinusU) + (other.red * u); + green = (green * oneMinusU) + (other.green * u); + blue = (blue * oneMinusU) + (other.blue * u); + alpha = (alpha * oneMinusU) + (other.alpha * u); fixBounds(); } // ----------------------------------------------------------------------- -bool Color::isEqual(const Color& color, bool withAlpha) noexcept +bool Color::isEqual(const Color& color, const bool withAlpha) noexcept { const uchar r1 = getFixedRange2(rgba[0]); const uchar g1 = getFixedRange2(rgba[1]); @@ -212,7 +209,7 @@ bool Color::isEqual(const Color& color, bool withAlpha) noexcept return (r1 == r2 && g1 == g2 && b1 == b2); } -bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept +bool Color::isNotEqual(const Color& color, const bool withAlpha) noexcept { const uchar r1 = getFixedRange2(rgba[0]); const uchar g1 = getFixedRange2(rgba[1]); From f2e9c9a8747ac15c7994954255f72811e9f2ca2a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:23:30 +0100 Subject: [PATCH 026/159] Add color tests, WIP Signed-off-by: falkTX --- tests/Color.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++++++- tests/Makefile | 13 ++-- 2 files changed, 198 insertions(+), 8 deletions(-) diff --git a/tests/Color.cpp b/tests/Color.cpp index 578688c6..0e572a40 100644 --- a/tests/Color.cpp +++ b/tests/Color.cpp @@ -14,13 +14,204 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + +#define DPF_TEST_COLOR_CPP +#include "dgl/src/Color.cpp" + // -------------------------------------------------------------------------------------------------------------------- int main() { USE_NAMESPACE_DGL; - // TODO + // constructor with no arguments, must give solid black + { + Color c; + DISTRHO_ASSERT_EQUAL(c.red, 0.0f, "red value is 0"); + DISTRHO_ASSERT_EQUAL(c.green, 0.0f, "green value is 0"); + DISTRHO_ASSERT_EQUAL(c.blue, 0.0f, "blue value is 0"); + DISTRHO_ASSERT_EQUAL(c.alpha, 1.0f, "alpha value is 1"); + } + + // constructor gives correct floating-point values (arguments are r, g, b, a; in order) + { + Color c(0.1f, 0.2f, 0.3f, 0.4f); + DISTRHO_ASSERT_EQUAL(c.red, 0.1f, "red value is 0.1"); + DISTRHO_ASSERT_EQUAL(c.green, 0.2f, "green value is 0.2"); + DISTRHO_ASSERT_EQUAL(c.blue, 0.3f, "blue value is 0.3"); + DISTRHO_ASSERT_EQUAL(c.alpha, 0.4f, "alpha value is 0.4"); + } + + // constructor gives correct integer values normalized to float (arguments are r, g, b, a; in order) + { + Color c(51, 102, 153); + DISTRHO_ASSERT_EQUAL(c.red, 0.2f, "red value is 0.2 (integer 51)"); + DISTRHO_ASSERT_EQUAL(c.green, 0.4f, "green value is 0.4 (integer 102)"); + DISTRHO_ASSERT_EQUAL(c.blue, 0.6f, "blue value is 0.6 (integer 153)"); + DISTRHO_ASSERT_EQUAL(c.alpha, 1.0f, "alpha value is 1"); + + Color white(255, 255, 255); + DISTRHO_ASSERT_EQUAL(white.red, 1.0f, "white's red value is 1"); + DISTRHO_ASSERT_EQUAL(white.green, 1.0f, "white's green value is 1"); + DISTRHO_ASSERT_EQUAL(white.blue, 1.0f, "white's blue value is 1"); + DISTRHO_ASSERT_EQUAL(white.alpha, 1.0f, "white alpha value is 1"); + } + + // copy colors around + { + Color black; + Color halfTransparentWhite(1.0f, 1.0f, 1.0f, 0.5f); + + // constructor copy + Color test(halfTransparentWhite); + DISTRHO_ASSERT_EQUAL(test.red, 1.0f, "copied white's red value is 1.0"); + DISTRHO_ASSERT_EQUAL(test.green, 1.0f, "copied white's green value is 1"); + DISTRHO_ASSERT_EQUAL(test.blue, 1.0f, "copied white's blue value is 1"); + DISTRHO_ASSERT_EQUAL(test.alpha, 0.5f, "copied white's alpha value is 0.5"); + + // assign operator + test = black; + DISTRHO_ASSERT_EQUAL(test.red, 0.0f, "assigned black's red value is 0"); + DISTRHO_ASSERT_EQUAL(test.green, 0.0f, "assigned black's green value is 0"); + DISTRHO_ASSERT_EQUAL(test.blue, 0.0f, "assigned black's blue value is 0"); + DISTRHO_ASSERT_EQUAL(test.alpha, 1.0f, "assigned black's alpha value is 1"); + } + + // simple color comparisons + { + Color black1, black2; + Color white(1.0f, 1.0f, 1.0f); + Color halfTransparentWhite(1.0f, 1.0f, 1.0f, 0.5f); + + // logic operators + DISTRHO_ASSERT_EQUAL(black1, black1, "color equals itself"); + DISTRHO_ASSERT_EQUAL(black1, black2, "black equals black"); + DISTRHO_ASSERT_NOT_EQUAL(black1, white, "black is not white"); + DISTRHO_ASSERT_NOT_EQUAL(black1, halfTransparentWhite, "black is not half-transparent white"); + DISTRHO_ASSERT_NOT_EQUAL(white, halfTransparentWhite, "white is not half-transparent white"); + + // class functions (truthful) + DISTRHO_ASSERT_EQUAL(black1.isEqual(black1), true, "color equals itself"); + DISTRHO_ASSERT_EQUAL(black1.isEqual(black2), true, "black equals black"); + DISTRHO_ASSERT_EQUAL(black1.isNotEqual(white), true, "black is not white"); + DISTRHO_ASSERT_EQUAL(white.isNotEqual(halfTransparentWhite), true, "white is not half-transparent white"); + + // class functions (inverted) + DISTRHO_ASSERT_EQUAL(black1.isNotEqual(black1), false, "color equals itself"); + DISTRHO_ASSERT_EQUAL(black1.isNotEqual(black2), false, "black equals black"); + DISTRHO_ASSERT_EQUAL(black1.isEqual(white), false, "black is not white"); + DISTRHO_ASSERT_EQUAL(white.isEqual(halfTransparentWhite), false, "white is not half-transparent white"); + + // class functions ignoring alpha + DISTRHO_ASSERT_EQUAL(black1.isEqual(black1, false), true, "color equals itself"); + DISTRHO_ASSERT_EQUAL(black1.isEqual(black2, false), true, "black equals black"); + DISTRHO_ASSERT_EQUAL(black1.isNotEqual(white, false), true, "black is not white"); + DISTRHO_ASSERT_EQUAL(white.isEqual(halfTransparentWhite, false), true, + "white is half-transparent white if we ignore alpha"); + } + + // TODO advanced color comparisons + { + } + + // TODO fromHSL + { + } + + // create colors from html strings + { + Color c000 = Color::fromHTML("#000"); + DISTRHO_ASSERT_EQUAL(c000.red, 0.0f, "#000 red value is 0"); + DISTRHO_ASSERT_EQUAL(c000.green, 0.0f, "#000 green value is 0"); + DISTRHO_ASSERT_EQUAL(c000.blue, 0.0f, "#000 blue value is 0"); + DISTRHO_ASSERT_EQUAL(c000.alpha, 1.0f, "#000 alpha value is 1"); + + Color c000000 = Color::fromHTML("#000000"); + DISTRHO_ASSERT_EQUAL(c000000.red, 0.0f, "#000000 red value is 0"); + DISTRHO_ASSERT_EQUAL(c000000.green, 0.0f, "#000000 green value is 0"); + DISTRHO_ASSERT_EQUAL(c000000.blue, 0.0f, "#000000 blue value is 0"); + DISTRHO_ASSERT_EQUAL(c000000.alpha, 1.0f, "#000000 alpha value is 1"); + + Color cfff = Color::fromHTML("#fff"); + DISTRHO_ASSERT_EQUAL(cfff.red, 1.0f, "#fff red value is 1"); + DISTRHO_ASSERT_EQUAL(cfff.green, 1.0f, "#fff green value is 1"); + DISTRHO_ASSERT_EQUAL(cfff.blue, 1.0f, "#fff blue value is 1"); + DISTRHO_ASSERT_EQUAL(cfff.alpha, 1.0f, "#fff alpha value is 1"); + + Color cffffff = Color::fromHTML("#ffffff"); + DISTRHO_ASSERT_EQUAL(cffffff.red, 1.0f, "#ffffff red value is 1"); + DISTRHO_ASSERT_EQUAL(cffffff.green, 1.0f, "#ffffff green value is 1"); + DISTRHO_ASSERT_EQUAL(cffffff.blue, 1.0f, "#ffffff blue value is 1"); + DISTRHO_ASSERT_EQUAL(cffffff.alpha, 1.0f, "#ffffff alpha value is 1"); + + Color cf00 = Color::fromHTML("#f00"); + DISTRHO_ASSERT_EQUAL(cf00.red, 1.0f, "#f00 red value is 1"); + DISTRHO_ASSERT_EQUAL(cf00.green, 0.0f, "#f00 green value is 0"); + DISTRHO_ASSERT_EQUAL(cf00.blue, 0.0f, "#f00 blue value is 0"); + + Color cff0000 = Color::fromHTML("#ff0000"); + DISTRHO_ASSERT_EQUAL(cff0000.red, 1.0f, "#ff0000 red value is 1"); + DISTRHO_ASSERT_EQUAL(cff0000.green, 0.0f, "#ff0000 green value is 0"); + DISTRHO_ASSERT_EQUAL(cff0000.blue, 0.0f, "#ff0000 blue value is 0"); + + Color c0f0 = Color::fromHTML("#0f0"); + DISTRHO_ASSERT_EQUAL(c0f0.red, 0.0f, "#0f0 red value is 0"); + DISTRHO_ASSERT_EQUAL(c0f0.green, 1.0f, "#0f0 green value is 1"); + DISTRHO_ASSERT_EQUAL(c0f0.blue, 0.0f, "#0f0 blue value is 0"); + + Color c00ff00 = Color::fromHTML("#00ff00"); + DISTRHO_ASSERT_EQUAL(c00ff00.red, 0.0f, "#00ff00 red value is 0"); + DISTRHO_ASSERT_EQUAL(c00ff00.green, 1.0f, "#00ff00 green value is 1"); + DISTRHO_ASSERT_EQUAL(c00ff00.blue, 0.0f, "#00ff00 blue value is 0"); + + Color c00f = Color::fromHTML("#00f"); + DISTRHO_ASSERT_EQUAL(c00f.red, 0.0f, "#00f red value is 0"); + DISTRHO_ASSERT_EQUAL(c00f.green, 0.0f, "#00f green value is 0"); + DISTRHO_ASSERT_EQUAL(c00f.blue, 1.0f, "#00f blue value is 1"); + + Color c0000ff = Color::fromHTML("#0000ff"); + DISTRHO_ASSERT_EQUAL(c0000ff.red, 0.0f, "#0000ff red value is 0"); + DISTRHO_ASSERT_EQUAL(c0000ff.green, 0.0f, "#0000ff green value is 0"); + DISTRHO_ASSERT_EQUAL(c0000ff.blue, 1.0f, "#0000ff blue value is 1"); + + // half point, round to 1 decimal point due to precision loss + Color grey = Color::fromHTML("#7b7b7b"); + DISTRHO_ASSERT_EQUAL(std::round(grey.red*10)/10, 0.5f, "grey's rounded red value is 0.5"); + DISTRHO_ASSERT_EQUAL(std::round(grey.green*10)/10, 0.5f, "grey's rounded green value is 0.5"); + DISTRHO_ASSERT_EQUAL(std::round(grey.blue*10)/10, 0.5f, "grey's rounded blue value is 0.5"); + } + + // check bounds + { + Color negativeInteger(-1, -1, -1, -1.0f); + DISTRHO_ASSERT_EQUAL(negativeInteger.red, 0.0f, "red value is 0"); + DISTRHO_ASSERT_EQUAL(negativeInteger.green, 0.0f, "green value is 0"); + DISTRHO_ASSERT_EQUAL(negativeInteger.blue, 0.0f, "blue value is 0"); + DISTRHO_ASSERT_EQUAL(negativeInteger.alpha, 0.0f, "alpha value is 0"); + + Color negativeFloat(-1.0f, -1.0f, -1.0f, -1.0f); + DISTRHO_ASSERT_EQUAL(negativeFloat.red, 0.0f, "red value is 0"); + DISTRHO_ASSERT_EQUAL(negativeFloat.green, 0.0f, "green value is 0"); + DISTRHO_ASSERT_EQUAL(negativeFloat.blue, 0.0f, "blue value is 0"); + DISTRHO_ASSERT_EQUAL(negativeFloat.alpha, 0.0f, "alpha value is 0"); + + Color overflowInteger(0xfff, 0xfff, 0xfff, 0xfff); + DISTRHO_ASSERT_EQUAL(overflowInteger.red, 1.0f, "red value is 1"); + DISTRHO_ASSERT_EQUAL(overflowInteger.green, 1.0f, "green value is 1"); + DISTRHO_ASSERT_EQUAL(overflowInteger.blue, 1.0f, "blue value is 1"); + DISTRHO_ASSERT_EQUAL(overflowInteger.alpha, 1.0f, "alpha value is 1"); + + Color overflowFloat(2.0f, 2.0f, 2.0f, 2.0f); + DISTRHO_ASSERT_EQUAL(overflowFloat.red, 1.0f, "red value is 1"); + DISTRHO_ASSERT_EQUAL(overflowFloat.green, 1.0f, "green value is 1"); + DISTRHO_ASSERT_EQUAL(overflowFloat.blue, 1.0f, "blue value is 1"); + DISTRHO_ASSERT_EQUAL(overflowFloat.alpha, 1.0f, "alpha value is 1"); + } + + // TODO interpolation + { + } return 0; } diff --git a/tests/Makefile b/tests/Makefile index c9150549..e9eea49f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,11 +20,9 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra # --------------------------------------------------------------------------------------------------------------------- -# TODO -src=Application.cpp - -TESTS = $(src:%.cpp=../build/tests/%) -OBJS = $(TESTS:%=%.o) +TESTS = Application Color +TARGETS = $(TESTS:%=../build/tests/%) +OBJS = $(TARGETS:%=%.cpp.o) # --------------------------------------------------------------------------------------------------------------------- @@ -36,14 +34,15 @@ endif # --------------------------------------------------------------------------------------------------------------------- -all: $(TESTS) +all: $(TARGETS) # --------------------------------------------------------------------------------------------------------------------- ../build/tests/%: ../build/tests/%.cpp.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ - $@ + @echo "Running test for $*" + $(SILENT)$@ # valgrind --leak-check=full $@ # --------------------------------------------------------------------------------------------------------------------- From ea2f9fa567653fb803b5830085aad0fb3496e0da Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:36:04 +0100 Subject: [PATCH 027/159] Start geometry tests Signed-off-by: falkTX --- dgl/Geometry.hpp | 20 ++++++++++++++++---- dgl/src/Geometry.cpp | 6 ++++++ tests/Makefile | 2 +- tests/Point.cpp | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index f7fe31ec..e922d683 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -21,7 +21,7 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Forward class names template class Line; @@ -29,7 +29,7 @@ template class Circle; template class Triangle; template class Rectangle; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** DGL Point class. @@ -121,7 +121,7 @@ private: template friend class Rectangle; }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** DGL Size class. @@ -346,10 +346,12 @@ public: */ void moveBy(const Point& pos) noexcept; +#ifndef DPF_TEST_POINT_CPP /** Draw this line using the current OpenGL state. */ void draw(); +#endif /** Return true if line is null (start and end pos are equal). @@ -460,6 +462,7 @@ public: */ void setNumSegments(const uint num); +#ifndef DPF_TEST_POINT_CPP /** Draw this circle using the current OpenGL state. */ @@ -469,6 +472,7 @@ public: Draw lines (outline of this circle) using the current OpenGL state. */ void drawOutline(); +#endif Circle& operator=(const Circle& cir) noexcept; bool operator==(const Circle& cir) const noexcept; @@ -540,6 +544,7 @@ public: */ bool isInvalid() const noexcept; +#ifndef DPF_TEST_POINT_CPP /** Draw this triangle using the current OpenGL state. */ @@ -549,6 +554,7 @@ public: Draw lines (outline of this triangle) using the current OpenGL state. */ void drawOutline(); +#endif Triangle& operator=(const Triangle& tri) noexcept; bool operator==(const Triangle& tri) const noexcept; @@ -557,8 +563,10 @@ public: private: Point fPos1, fPos2, fPos3; +#ifndef DPF_TEST_POINT_CPP /** @internal */ void _draw(const bool outline); +#endif }; // ----------------------------------------------------------------------- @@ -722,6 +730,7 @@ public: */ bool containsY(const T& y) const noexcept; +#ifndef DPF_TEST_POINT_CPP /** Draw this rectangle using the current OpenGL state. */ @@ -731,6 +740,7 @@ public: Draw lines (outline of this rectangle) using the current OpenGL state. */ void drawOutline(); +#endif Rectangle& operator=(const Rectangle& rect) noexcept; Rectangle& operator*=(double m) noexcept; @@ -742,8 +752,10 @@ private: Point fPos; Size fSize; +#ifndef DPF_TEST_POINT_CPP /** @internal */ void _draw(const bool outline); +#endif }; // ----------------------------------------------------------------------- diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index d133985f..01af9620 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -599,6 +599,7 @@ void Circle::setNumSegments(const uint num) fSin = std::sin(fTheta); } +#ifndef DPF_TEST_POINT_CPP template void Circle::draw() { @@ -610,6 +611,7 @@ void Circle::drawOutline() { _draw(true); } +#endif template Circle& Circle::operator=(const Circle& cir) noexcept @@ -686,6 +688,7 @@ bool Triangle::isInvalid() const noexcept return fPos1 == fPos2 || fPos1 == fPos3; } +#ifndef DPF_TEST_POINT_CPP template void Triangle::draw() { @@ -697,6 +700,7 @@ void Triangle::drawOutline() { _draw(true); } +#endif template Triangle& Triangle::operator=(const Triangle& tri) noexcept @@ -900,6 +904,7 @@ bool Rectangle::containsY(const T& y) const noexcept return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight); } +#ifndef DPF_TEST_POINT_CPP template void Rectangle::draw() { @@ -911,6 +916,7 @@ void Rectangle::drawOutline() { _draw(true); } +#endif template Rectangle& Rectangle::operator=(const Rectangle& rect) noexcept diff --git a/tests/Makefile b/tests/Makefile index e9eea49f..e3b88087 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,7 +20,7 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra # --------------------------------------------------------------------------------------------------------------------- -TESTS = Application Color +TESTS = Application Color Point TARGETS = $(TESTS:%=../build/tests/%) OBJS = $(TARGETS:%=%.cpp.o) diff --git a/tests/Point.cpp b/tests/Point.cpp index 578688c6..742ba29c 100644 --- a/tests/Point.cpp +++ b/tests/Point.cpp @@ -14,12 +14,32 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + +#define DPF_TEST_POINT_CPP +#include "dgl/src/Geometry.cpp" + // -------------------------------------------------------------------------------------------------------------------- int main() { USE_NAMESPACE_DGL; + // basic usage + { + Point p; + DISTRHO_ASSERT_EQUAL(p.getX(), 0, "point start X value is 0"); + DISTRHO_ASSERT_EQUAL(p.getY(), 0, "point start Y value is 0"); + + p.setX(5); + DISTRHO_ASSERT_EQUAL(p.getX(), 5, "point X value changed to 5"); + DISTRHO_ASSERT_EQUAL(p.getY(), 0, "point start Y value remains 0"); + + p.setY(7); + DISTRHO_ASSERT_EQUAL(p.getX(), 5, "point X value remains 5"); + DISTRHO_ASSERT_EQUAL(p.getY(), 7, "point Y value changed to 7"); + } + // TODO return 0; From ec35e8e4523117c763d23bd376079048b2b5f4e0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:43:26 +0100 Subject: [PATCH 028/159] Rework CI setup Signed-off-by: falkTX --- .travis.yml | 31 +++++++++++++++++++------------ .travis/before_install.sh | 18 +++++++++++++----- .travis/install.sh | 38 +++++++++++++++++++++++++------------- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 873bf5bd..b379e0bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,25 @@ -os: - - linux +language: cpp -sudo: required -dist: trusty +cache: + directories: + - ${HOME}/debs -languages: c++ -compiler: gcc +jobs: + include: + # linux native build + - name: "Linux native" + os: linux + compiler: gcc + dist: focal + env: + - TARGET="linux" before_install: - - sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh + - bash ${TRAVIS_BUILD_DIR}/.travis/before_install.sh + install: - - sh ${TRAVIS_BUILD_DIR}/.travis/install.sh + - bash ${TRAVIS_BUILD_DIR}/.travis/install.sh + script: - - sh ${TRAVIS_BUILD_DIR}/.travis/script-linux.sh - - sh ${TRAVIS_BUILD_DIR}/.travis/script-macos.sh - - sh ${TRAVIS_BUILD_DIR}/.travis/script-win32.sh - - sh ${TRAVIS_BUILD_DIR}/.travis/script-win64.sh + - make + - make -C tests diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 73c1026a..93e5da90 100755 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -2,9 +2,17 @@ set -e -sudo add-apt-repository ppa:kxstudio-debian/kxstudio -y -sudo add-apt-repository ppa:kxstudio-debian/mingw -y -sudo add-apt-repository ppa:kxstudio-debian/toolchain -y -sudo apt-get update -qq -sudo apt-get install kxstudio-repos +# Special macOS native handling +if [ "${TARGET}" = "macos" ] || [ "${TARGET}" = "macos-universal" ]; then + exit 0 +fi + +if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then + wget -qO- https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add - + sudo apt-add-repository -y 'deb https://dl.winehq.org/wine-builds/ubuntu/ focal main' + sudo dpkg --add-architecture i386 +fi + sudo apt-get update -qq +sudo apt-get install -y -o APT::Immediate-Configure=false libc6 libc6:i386 libgcc-s1:i386 +sudo apt-get install -y -f diff --git a/.travis/install.sh b/.travis/install.sh index 71d51529..fbca5d6c 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -2,16 +2,28 @@ set -e -sudo apt-get install -y \ - g++ \ - pkg-config \ - libcairo2-dev \ - libjack-jackd2-dev \ - liblo-dev \ - libgl1-mesa-dev \ - libx11-dev \ - apple-x86-setup \ - mingw32-x-gcc \ - mingw32-x-pkgconfig \ - mingw64-x-gcc \ - mingw64-x-pkgconfig +# Special macOS native handling +if [ "${TARGET}" = "macos" ] || [ "${TARGET}" = "macos-universal" ]; then + HOMEBREW_NO_AUTO_UPDATE=1 brew install cairo jack2 liblo + exit 0 +fi + +# Special handling for caching deb archives +if [ "$(ls ${HOME}/debs | wc -l)" -ne 0 ]; then + sudo cp ${HOME}/debs/*.deb /var/cache/apt/archives/ +fi + +# common +sudo apt-get install -y build-essential pkg-config + +# specific +if [ "${TARGET}" = "win32" ]; then + sudo apt-get install -y binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 winehq-stable +elif [ "${TARGET}" = "win64" ]; then + sudo apt-get install -y binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 winehq-stable +else + sudo apt-get install -y libcairo2-dev libgl1-mesa-dev libjack-jackd2-dev liblo-dev libx11-dev +fi + +# Special handling for caching deb archives +sudo mv /var/cache/apt/archives/*.deb ${HOME}/debs/ From 815aa367310bba54e560fb7116a59fe936a31d9f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:48:51 +0100 Subject: [PATCH 029/159] More CI tweaks Signed-off-by: falkTX --- .travis/before_install.sh | 4 +++- tests/Application.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 93e5da90..50e55cb3 100755 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -14,5 +14,7 @@ if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then fi sudo apt-get update -qq -sudo apt-get install -y -o APT::Immediate-Configure=false libc6 libc6:i386 libgcc-s1:i386 +if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then + sudo apt-get install -y -o APT::Immediate-Configure=false libc6 libc6:i386 libgcc-s1:i386 +fi sudo apt-get install -y -f diff --git a/tests/Application.cpp b/tests/Application.cpp index 2bb911ab..95c08e61 100644 --- a/tests/Application.cpp +++ b/tests/Application.cpp @@ -95,7 +95,7 @@ int main() app.addIdleCallback(&idleCounter); app.exec(); DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); - DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks not triggered"); + DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks MUST have been triggered"); } // standalone exec, but with 0 as timeout @@ -106,7 +106,7 @@ int main() app.addIdleCallback(&idleCounter); app.exec(0); DISTRHO_ASSERT_EQUAL(appQuitter.isThreadRunning(), false, "app quit triggered because we told it so"); - DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks not triggered"); + DISTRHO_ASSERT_NOT_EQUAL(idleCounter.counter, 0, "app idle callbacks MUST have been triggered"); } return 0; From a53248a652eb340154c9d8bbc2c9dfbe28b3b0b2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:52:40 +0100 Subject: [PATCH 030/159] Do not crash if puglNewWorld fails Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index bd246cbb..e3ae472f 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -78,11 +78,14 @@ void Application::PrivateData::oneWindowHidden() noexcept void Application::PrivateData::idle(const uint timeoutInMs) { - const double timeoutInSeconds = timeoutInMs != 0 - ? static_cast(timeoutInMs) / 1000.0 - : 0.0; + if (world != nullptr) + { + const double timeoutInSeconds = timeoutInMs != 0 + ? static_cast(timeoutInMs) / 1000.0 + : 0.0; - puglUpdate(world, timeoutInSeconds); + puglUpdate(world, timeoutInSeconds); + } #ifndef DPF_TEST_APPLICATION_CPP for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) From a33e83ff5706251ac102ed16dcf9a3d2546b5607 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 16:59:06 +0100 Subject: [PATCH 031/159] Use xvfb for linux tests Signed-off-by: falkTX --- .travis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index b379e0bb..be81d32e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,15 @@ cache: - ${HOME}/debs jobs: - include: - # linux native build - - name: "Linux native" - os: linux - compiler: gcc - dist: focal - env: - - TARGET="linux" + # linux native build + - name: "Linux native" + os: linux + compiler: gcc + dist: focal + env: + - TARGET="linux" + services: + - xvfb before_install: - bash ${TRAVIS_BUILD_DIR}/.travis/before_install.sh From 711a316571bee235c611c5d2f33676907e645f77 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 17:19:05 +0100 Subject: [PATCH 032/159] run geometry tests with all data types Signed-off-by: falkTX --- tests/Point.cpp | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/tests/Point.cpp b/tests/Point.cpp index 742ba29c..f3e5f687 100644 --- a/tests/Point.cpp +++ b/tests/Point.cpp @@ -21,13 +21,14 @@ // -------------------------------------------------------------------------------------------------------------------- -int main() +template +static int runTestsPerType() { USE_NAMESPACE_DGL; // basic usage { - Point p; + Point p; DISTRHO_ASSERT_EQUAL(p.getX(), 0, "point start X value is 0"); DISTRHO_ASSERT_EQUAL(p.getY(), 0, "point start Y value is 0"); @@ -40,7 +41,40 @@ int main() DISTRHO_ASSERT_EQUAL(p.getY(), 7, "point Y value changed to 7"); } - // TODO + return 0; +} + +int main() +{ + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; + + if (const int ret = runTestsPerType()) + return ret; return 0; } From 3b1e5d698a3f435e4b508ff3d6d27e7c7ec32f5d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 1 May 2021 23:25:12 +0100 Subject: [PATCH 033/159] Start Window related tests; comment out all code Signed-off-by: falkTX --- dgl/Makefile | 6 +- dgl/Window.hpp | 110 +++++---- dgl/src/ApplicationPrivateData.cpp | 14 +- dgl/src/ApplicationPrivateData.hpp | 2 +- dgl/src/Window.cpp | 32 +-- dgl/src/WindowPrivateData.cpp | 356 +++++++++++++++++++++++++++-- dgl/src/WindowPrivateData.hpp | 307 +++---------------------- dgl/src/pugl.cpp | 23 ++ dgl/src/pugl.hpp | 1 + tests/Makefile | 53 ++++- tests/Point.cpp | 8 + tests/Window.cpp | 49 ++++ 12 files changed, 589 insertions(+), 372 deletions(-) create mode 100644 tests/Window.cpp diff --git a/dgl/Makefile b/dgl/Makefile index a76490a4..e75d88d4 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -25,9 +25,9 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra -Wno-narrowing OBJS_common = \ ../build/dgl/Application.cpp.o \ - ../build/dgl/ApplicationPrivateData.cpp.o -# ../build/dgl/Color.cpp.o \ -# ../build/dgl/Geometry.cpp.o \ + ../build/dgl/ApplicationPrivateData.cpp.o \ + ../build/dgl/Color.cpp.o \ + ../build/dgl/Geometry.cpp.o # ../build/dgl/ImageBase.cpp.o \ # ../build/dgl/Resources.cpp.o\ # ../build/dgl/StandaloneWindow.cpp.o \ diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 44eda0c6..d297c16c 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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,29 +19,59 @@ #include "Geometry.hpp" -#ifdef DISTRHO_DEFINES_H_INCLUDED -START_NAMESPACE_DISTRHO -class UI; -class UIExporter; -END_NAMESPACE_DISTRHO -#endif - START_NAMESPACE_DGL // ----------------------------------------------------------------------- class Application; -class Widget; -class StandaloneWindow; + +class Window +{ +public: + explicit Window(Application& app); + explicit Window(Application& app, uintptr_t parentWindowHandle, double scaling, bool resizable); + virtual ~Window(); + + void close(); + + /** + Get the "native" window handle. + Returned value depends on the platform: + - HaikuOS: This is a pointer to a `BView`. + - MacOS: This is a pointer to an `NSView*`. + - Windows: This is a `HWND`. + - Everything else: This is an [X11] `Window`. + */ + uintptr_t getNativeWindowHandle() const noexcept; + +private: + struct PrivateData; + PrivateData* const pData; + friend class Application; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL /* TODO * add focusEvent with CrossingMode arg * add eventcrossing/enter-leave event */ -class Window -{ -public: +// class StandaloneWindow; +// class Widget; + +// #ifdef DISTRHO_DEFINES_H_INCLUDED +// START_NAMESPACE_DISTRHO +// class UI; +// class UIExporter; +// END_NAMESPACE_DISTRHO +// #endif + +#if 0 #ifndef DGL_FILE_BROWSER_DISABLED /** File browser options. @@ -81,14 +111,10 @@ public: }; #endif // DGL_FILE_BROWSER_DISABLED - explicit Window(Application& app); - explicit Window(Window& transientParentWindow); - explicit Window(Application& app, uintptr_t parentWindowHandle, double scaling, bool resizable); - virtual ~Window(); + static Window& withTransientParentWindow(Window& transientParentWindow); void show(); void hide(); - void close(); void exec(bool lockWait = false); void focus(); @@ -129,21 +155,12 @@ public: Application& getApp() const noexcept; #endif - /** - Get the "native" window handle. - Returned value depends on the platform: - - HaikuOS: This is a pointer to a `BView`. - - MacOS: This is a pointer to an `NSView*`. - - Windows: This is a `HWND`. - - Everything else: This is an [X11] `Window`. - */ - uintptr_t getNativeWindowHandle() const noexcept; - const GraphicsContext& getGraphicsContext() const noexcept; void addIdleCallback(IdleCallback* const callback); void removeIdleCallback(IdleCallback* const callback); + protected: virtual void onDisplayBefore(); virtual void onDisplayAfter(); @@ -157,17 +174,6 @@ protected: // internal void _setAutoScaling(double scaling) noexcept; -private: - struct PrivateData; - PrivateData* const pData; - friend class Application; - friend class Widget; - friend class StandaloneWindow; -#ifdef DISTRHO_DEFINES_H_INCLUDED - friend class DISTRHO_NAMESPACE::UI; - friend class DISTRHO_NAMESPACE::UIExporter; -#endif - virtual void _addWidget(Widget* const widget); virtual void _removeWidget(Widget* const widget); void _idle(); @@ -175,20 +181,24 @@ private: bool handlePluginKeyboard(const bool press, const uint key); bool handlePluginSpecial(const bool press, const Key key); +// friend class Widget; +// friend class StandaloneWindow; +// #ifdef DISTRHO_DEFINES_H_INCLUDED +// friend class DISTRHO_NAMESPACE::UI; +// friend class DISTRHO_NAMESPACE::UIExporter; +// #endif + // Prevent copies -#ifdef DISTRHO_PROPER_CPP11_SUPPORT - Window& operator=(Window&) = delete; - Window& operator=(const Window&) = delete; -#else - Window& operator=(Window&); - Window& operator=(const Window&); -#endif +// #ifdef DISTRHO_PROPER_CPP11_SUPPORT +// Window& operator=(Window&) = delete; +// Window& operator=(const Window&) = delete; +// #else +// Window& operator=(Window&); +// Window& operator=(const Window&); +// #endif - DISTRHO_LEAK_DETECTOR(Window); -}; +#endif // ----------------------------------------------------------------------- -END_NAMESPACE_DGL - #endif // DGL_WINDOW_HPP_INCLUDED diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index e3ae472f..953b4797 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -87,13 +87,13 @@ void Application::PrivateData::idle(const uint timeoutInMs) puglUpdate(world, timeoutInSeconds); } -#ifndef DPF_TEST_APPLICATION_CPP - for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) - { - Window* const window(*it); - window->_idle(); - } -#endif +// #ifndef DPF_TEST_APPLICATION_CPP +// for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) +// { +// Window* const window(*it); +// window->_idle(); +// } +// #endif for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) { diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 98e65dcd..6057b6dc 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -68,7 +68,7 @@ struct Application::PrivateData { /** Set flag indicating application is quitting, and closes all windows in reverse order of registration. */ void quit(); - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index f424be99..eae1f2ad 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -16,17 +16,19 @@ #include "WindowPrivateData.hpp" +#include "pugl.hpp" + START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Window +// Window::Window(Window& transientParentWindow) +// : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} + Window::Window(Application& app) : pData(new PrivateData(app.pData, this)) {} -Window::Window(Window& transientParentWindow) - : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} - Window::Window(Application& app, const uintptr_t parentWindowHandle, const double scaling, const bool resizable) : pData(new PrivateData(app.pData, this, parentWindowHandle, scaling, resizable)) {} @@ -35,6 +37,17 @@ Window::~Window() delete pData; } +void Window::close() +{ + pData->close(); +} + +uintptr_t Window::getNativeWindowHandle() const noexcept +{ + return puglGetNativeWindow(pData->view); +} + +#if 0 void Window::show() { pData->setVisible(true); @@ -45,11 +58,6 @@ void Window::hide() pData->setVisible(false); } -void Window::close() -{ - pData->close(); -} - #if 0 void Window::exec(bool lockWait) { @@ -191,11 +199,6 @@ Application& Window::getApp() const noexcept } #endif -uintptr_t Window::getNativeWindowHandle() const noexcept -{ - return puglGetNativeWindow(pData->fView); -} - #if 0 const GraphicsContext& Window::getGraphicsContext() const noexcept { @@ -285,6 +288,7 @@ bool Window::handlePluginSpecial(const bool press, const Key key) return false; // return pData->handlePluginSpecial(press, key); } +#endif // ----------------------------------------------------------------------- diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 297d35c3..4e2dfa64 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -15,7 +15,116 @@ */ #include "WindowPrivateData.hpp" +#include "../Widget.hpp" +#include "pugl.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s) + : appData(a), + self(s), + view(puglNewView(appData->world)) +{ + init(true); +} + +Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, Window& transientWindow) + : appData(a), + self(s), + view(puglNewView(appData->world)) +{ + puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); + init(true); +} + +Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, + const uintptr_t parentWindowHandle, const double scaling, const bool resizable) + : appData(a), + self(s), + view(puglNewView(appData->world)) +{ + // TODO view parent + init(resizable); +} + +Window::PrivateData::~PrivateData() +{ + if (self != nullptr) + appData->windows.remove(self); + + if (view != nullptr) + puglFreeView(view); +} + +// ----------------------------------------------------------------------- + +void Window::PrivateData::init(const bool resizable) +{ + if (self == nullptr || view == nullptr) + { + /* + DGL_DBG("Failed!\n"); + */ + return; + } + +#ifdef DGL_CAIRO + puglSetBackend(view, puglCairoBackend()); +#endif +#ifdef DGL_OPENGL + puglSetBackend(view, puglGlBackend()); +#endif +#ifdef DGL_Vulkan + puglSetBackend(view, puglVulkanBackend()); +#endif + + puglSetHandle(view, this); + puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); + puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); + puglSetEventFunc(view, puglEventCallback); +// #ifndef DGL_FILE_BROWSER_DISABLED +// puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); +// #endif + + appData->windows.push_back(self); + + // DGL_DBG("Success!\n"); +} + +void Window::PrivateData::close() +{ + /* + DGL_DBG("Window close\n"); + + if (fUsingEmbed) + return; + + setVisible(false); + + if (! fFirstInit) + { + fAppData->oneWindowHidden(); + fFirstInit = true; + } + */ +} + +// ----------------------------------------------------------------------- + +PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) +{ + Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); + return PUGL_SUCCESS; +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#if 0 #ifdef DGL_CAIRO # define PUGL_CAIRO # include "../Cairo.hpp" @@ -25,6 +134,12 @@ # include "../OpenGL.hpp" #endif +#ifndef DPF_TEST_WINDOW_CPP +#include "WidgetPrivateData.hpp" +#include "pugl-upstream/include/pugl/pugl.h" +#include "pugl-extra/extras.h" +#endif + extern "C" { #include "pugl-upstream/src/implementation.c" #include "pugl-extra/extras.c" @@ -63,38 +178,151 @@ extern "C" { #include #include +#define FOR_EACH_WIDGET(it) \ + for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + +#define DGL_DEBUG_EVENTS + +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) +# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); +# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); +# define DGL_DBGF std::fflush(stderr); +#else +# define DGL_DBG(msg) +# define DGL_DBGp(...) +# define DGL_DBGF +#endif + START_NAMESPACE_DGL +// Fallback build-specific Window functions +struct Fallback { + static void onDisplayBefore(); + static void onDisplayAfter(); + static void onReshape(uint width, uint height); +}; + // ----------------------------------------------------------------------- -void Window::PrivateData::init(const bool resizable) +Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self) + : fAppData(appData), + fSelf(self), + fView(puglNewView(appData->world)), + fFirstInit(true), + fVisible(false), + fUsingEmbed(false), + fScaling(1.0), + fAutoScaling(1.0), + fWidgets(), + fModal() { - if (fSelf == nullptr || fView == nullptr) + DGL_DBG("Creating window without parent..."); DGL_DBGF; + init(); +} + +#ifndef DPF_TEST_WINDOW_CPP +Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self, Window& transientWindow) + : fAppData(appData), + fSelf(self), + fView(puglNewView(appData->world)), + fFirstInit(true), + fVisible(false), + fUsingEmbed(false), + fScaling(1.0), + fAutoScaling(1.0), + fWidgets(), + fModal(transientWindow.pData) +{ + DGL_DBG("Creating window with parent..."); DGL_DBGF; + init(); + + puglSetTransientFor(fView, transientWindow.getNativeWindowHandle()); +} +#endif + +Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self, + const uintptr_t parentWindowHandle, + const double scaling, + const bool resizable) + : fAppData(appData), + fSelf(self), + fView(puglNewView(appData->world)), + fFirstInit(true), + fVisible(parentWindowHandle != 0), + fUsingEmbed(parentWindowHandle != 0), + fScaling(scaling), + fAutoScaling(1.0), + fWidgets(), + fModal() +{ + if (fUsingEmbed) { - DGL_DBG("Failed!\n"); - return; + DGL_DBG("Creating embedded window..."); DGL_DBGF; + puglSetParentWindow(fView, parentWindowHandle); + } + else + { + DGL_DBG("Creating window without parent..."); DGL_DBGF; } -// #ifdef DGL_CAIRO -// puglSetBackend(fView, puglCairoBackend()); -// #endif -#ifdef DGL_OPENGL - puglSetBackend(fView, puglGlBackend()); + init(resizable); + + if (fUsingEmbed) + { + DGL_DBG("NOTE: Embed window is always visible and non-resizable\n"); +// puglShowWindow(fView); +// fAppData->oneWindowShown(); +// fFirstInit = false; + } +} + +Window::PrivateData::~PrivateData() +{ + DGL_DBG("Destroying window..."); DGL_DBGF; + +#if 0 + if (fModal.enabled) + { + exec_fini(); + close(); + } #endif - puglSetHandle(fView, this); - puglSetViewHint(fView, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); - puglSetViewHint(fView, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); - puglSetEventFunc(fView, puglEventCallback); -// #ifndef DGL_FILE_BROWSER_DISABLED -// puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); -// #endif + fWidgets.clear(); - fAppData->windows.push_back(fSelf); + if (fUsingEmbed) + { +// puglHideWindow(fView); +// fAppData->oneWindowHidden(); + } + + if (fSelf != nullptr) + fAppData->windows.remove(fSelf); + +#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) + if (fOpenFilePanel) + { + [fOpenFilePanel release]; + fOpenFilePanel = nullptr; + } + if (fFilePanelDelegate) + { + [fFilePanelDelegate release]; + fFilePanelDelegate = nullptr; + } +#endif + + if (fView != nullptr) + puglFreeView(fView); DGL_DBG("Success!\n"); } +// ----------------------------------------------------------------------- + void Window::PrivateData::setVisible(const bool visible) { if (fVisible == visible) @@ -160,6 +388,97 @@ void Window::PrivateData::setVisible(const bool visible) } } +// ----------------------------------------------------------------------- + +void Window::PrivateData::addWidget(Widget* const widget) +{ + fWidgets.push_back(widget); +} + +void Window::PrivateData::removeWidget(Widget* const widget) +{ + fWidgets.remove(widget); +} + +// ----------------------------------------------------------------------- + +void Window::PrivateData::onPuglClose() +{ + DGL_DBG("PUGL: onClose\n"); + +// if (fModal.enabled) +// exec_fini(); + + fSelf->onClose(); + + if (fModal.childFocus != nullptr) + fModal.childFocus->fSelf->onClose(); + + close(); +} + +void Window::PrivateData::onPuglDisplay() +{ + fSelf->onDisplayBefore(); + + if (fWidgets.size() != 0) + { + const PuglRect rect = puglGetFrame(fView); + const int width = rect.width; + const int height = rect.height; + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->pData->display(width, height, fAutoScaling, false); + } + } + + fSelf->onDisplayAfter(); +} + +void Window::PrivateData::onPuglReshape(const int width, const int height) +{ + DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); + + DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); + + fSelf->onReshape(width, height); + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + + if (widget->pData->needsFullViewport) + widget->setSize(width, height); + } +} + +void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) +{ + DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + + Widget::MouseEvent rev = ev; + double x = ev.pos.getX() / fAutoScaling; + double y = ev.pos.getY() / fAutoScaling; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + rev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onMouse(rev)) + break; + } +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::windowSpecificIdle() { #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) @@ -418,3 +737,4 @@ void Window::PrivateData::Fallback::onReshape(const uint width, const uint heigh // ----------------------------------------------------------------------- END_NAMESPACE_DGL +#endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 228dab4b..5353002d 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,44 +18,41 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" - #include "ApplicationPrivateData.hpp" -#include "WidgetPrivateData.hpp" -#include "pugl-upstream/include/pugl/pugl.h" -#include "pugl-extra/extras.h" +#include "pugl.hpp" -#include +START_NAMESPACE_DGL -#define FOR_EACH_WIDGET(it) \ - for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) +class Widget; -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) +// ----------------------------------------------------------------------- -#define DGL_DEBUG_EVENTS +struct Window::PrivateData { + typedef Application::PrivateData AppData; -#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) -# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); -# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); -# define DGL_DBGF std::fflush(stderr); -#else -# define DGL_DBG(msg) -# define DGL_DBGp(...) -# define DGL_DBGF -#endif + AppData* const appData; + Window* const self; + PuglView* const view; -START_NAMESPACE_DGL + PrivateData(AppData* appData, Window* self); + PrivateData(AppData* appData, Window* self, Window& transientWindow); + PrivateData(AppData* appData, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); + ~PrivateData(); -// ----------------------------------------------------------------------- + void init(bool resizable); + void close(); -struct Window::PrivateData { - typedef Application::PrivateData AppData; + static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) +}; + +// ----------------------------------------------------------------------- - AppData* const fAppData; - Window* const fSelf; - PuglView* const fView; +END_NAMESPACE_DGL +#if 0 // this one depends on build type // GraphicsContext fContext; @@ -109,272 +106,36 @@ struct Window::PrivateData { String fSelectedFile; # endif #endif - - // 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) - { - } - */ - PrivateData(Application::PrivateData* const appData, Window* const self) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(false), - fUsingEmbed(false), - fScaling(1.0), - fAutoScaling(1.0), - fWidgets(), - fModal() - { - DGL_DBG("Creating window without parent..."); DGL_DBGF; - init(); - } - - PrivateData(Application::PrivateData* const appData, Window* const self, Window& transientWindow) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(false), - fUsingEmbed(false), - fScaling(1.0), - fAutoScaling(1.0), - fWidgets(), - fModal(transientWindow.pData) - { - DGL_DBG("Creating window with parent..."); DGL_DBGF; - init(); - - puglSetTransientFor(fView, transientWindow.getNativeWindowHandle()); - } - - PrivateData(Application::PrivateData* const appData, Window* const self, - const uintptr_t parentWindowHandle, - const double scaling, - const bool resizable) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(parentWindowHandle != 0), - fUsingEmbed(parentWindowHandle != 0), - fScaling(scaling), - fAutoScaling(1.0), - fWidgets(), - fModal() - { - if (fUsingEmbed) - { - DGL_DBG("Creating embedded window..."); DGL_DBGF; - puglSetParentWindow(fView, parentWindowHandle); - } - else - { - DGL_DBG("Creating window without parent..."); DGL_DBGF; - } - - init(resizable); - - if (fUsingEmbed) - { - DGL_DBG("NOTE: Embed window is always visible and non-resizable\n"); -// puglShowWindow(fView); -// fAppData->oneWindowShown(); -// fFirstInit = false; - } - } - - ~PrivateData() - { - DGL_DBG("Destroying window..."); DGL_DBGF; - -#if 0 - if (fModal.enabled) - { - exec_fini(); - close(); - } -#endif - - fWidgets.clear(); - - if (fUsingEmbed) - { -// puglHideWindow(fView); -// fAppData->oneWindowHidden(); - } - - if (fSelf != nullptr) - fAppData->windows.remove(fSelf); - -#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) - if (fOpenFilePanel) - { - [fOpenFilePanel release]; - fOpenFilePanel = nullptr; - } - if (fFilePanelDelegate) - { - [fFilePanelDelegate release]; - fFilePanelDelegate = nullptr; - } #endif - if (fView != nullptr) - puglFreeView(fView); - - DGL_DBG("Success!\n"); - } - +#if 0 // ndef DPF_TEST_WINDOW_CPP // ------------------------------------------------------------------- // stuff that uses pugl internals or build-specific things void init(const bool resizable = false); void setVisible(const bool visible); void windowSpecificIdle(); - static PuglStatus puglEventCallback(PuglView* const view, const PuglEvent* const event); // ------------------------------------------------------------------- - void close() - { - DGL_DBG("Window close\n"); - - if (fUsingEmbed) - return; - - setVisible(false); - - if (! fFirstInit) - { - fAppData->oneWindowHidden(); - fFirstInit = true; - } - } - // ------------------------------------------------------------------- - void addWidget(Widget* const widget) - { - fWidgets.push_back(widget); - } - - void removeWidget(Widget* const widget) - { - fWidgets.remove(widget); - } + void addWidget(Widget* const widget); + void removeWidget(Widget* const widget); // ------------------------------------------------------------------- - void onPuglClose() - { - DGL_DBG("PUGL: onClose\n"); - -// if (fModal.enabled) -// exec_fini(); - - fSelf->onClose(); - - if (fModal.childFocus != nullptr) - fModal.childFocus->fSelf->onClose(); - - close(); - } - - void onPuglDisplay() - { - fSelf->onDisplayBefore(); - - if (fWidgets.size() != 0) - { - const PuglRect rect = puglGetFrame(fView); - const int width = rect.width; - const int height = rect.height; - - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - widget->pData->display(width, height, fAutoScaling, false); - } - } - - fSelf->onDisplayAfter(); - } - - void onPuglReshape(const int width, const int height) - { - DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); - - DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); - - fSelf->onReshape(width, height); - - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - - if (widget->pData->needsFullViewport) - widget->setSize(width, height); - } - } - - void onPuglMouse(const Widget::MouseEvent& ev) - { - DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); - -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); - - Widget::MouseEvent rev = ev; - double x = ev.pos.getX() / fAutoScaling; - double y = ev.pos.getY() / fAutoScaling; - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - rev.pos = Point(x - widget->getAbsoluteX(), - y - widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onMouse(rev)) - break; - } - } + void onPuglClose(); + void onPuglDisplay(); + void onPuglReshape(const int width, const int height); + void onPuglMouse(const Widget::MouseEvent& ev); // ------------------------------------------------------------------- - -#ifdef DISTRHO_DEFINES_H_INCLUDED - friend class DISTRHO_NAMESPACE::UI; #endif - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) -}; - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL +// #ifdef DISTRHO_DEFINES_H_INCLUDED +// friend class DISTRHO_NAMESPACE::UI; +// #endif #if 0 // ----------------------------------------------------------------------- diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index a43e626c..d31a8467 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -27,6 +27,7 @@ #elif defined(DISTRHO_OS_MAC) #elif defined(DISTRHO_OS_WINDOWS) #else +# include # include # include # include @@ -45,17 +46,39 @@ # include # include # endif +# ifdef DGL_CAIRO +# include +# include +# endif +# ifdef DGL_OPENGL +# include +# endif +# ifdef DGL_VULKAN +# include +# include +# endif #endif START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +#define PUGL_DISABLE_DEPRECATED + #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) #elif defined(DISTRHO_OS_WINDOWS) #else # include "pugl-upstream/src/x11.c" +# ifdef DGL_CAIRO +# include "pugl-upstream/src/x11_cairo.c" +# endif +# ifdef DGL_OPENGL +# include "pugl-upstream/src/x11_gl.c" +# endif +# ifdef DGL_VULKAN +# include "pugl-upstream/src/x11_vulkan.c" +# endif #endif #include "pugl-upstream/src/implementation.c" diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 1fb58a34..3d5374e5 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -28,6 +28,7 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +#define PUGL_DISABLE_DEPRECATED #include "pugl-upstream/include/pugl/pugl.h" // -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Makefile b/tests/Makefile index e3b88087..0b50d12f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,9 +20,19 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra # --------------------------------------------------------------------------------------------------------------------- -TESTS = Application Color Point -TARGETS = $(TESTS:%=../build/tests/%) -OBJS = $(TARGETS:%=%.cpp.o) +TESTS = Application Color Point +ifeq ($(HAVE_CAIRO),true) +WTESTS = Window.cairo +endif +ifeq ($(HAVE_OPENGL),true) +WTESTS = Window.opengl +endif +ifeq ($(HAVE_VULKAN),true) +WTESTS = Window.vulkan +endif +TARGETS = $(TESTS:%=../build/tests/%) +TARGETS += $(WTESTS:Window.%=../build/tests/Window.%) +OBJS = $(TARGETS:%=%.cpp.o) # --------------------------------------------------------------------------------------------------------------------- @@ -45,6 +55,25 @@ all: $(TARGETS) $(SILENT)$@ # valgrind --leak-check=full $@ +../build/tests/%.cairo: ../build/tests/%.cpp.cairo.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ + @echo "Running test for $*" + $(SILENT)$@ + +../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + @echo "Running test for $*" + $(SILENT) + gdb -ex run $@ + +../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ + @echo "Running test for $*" + $(SILENT)$@ + # --------------------------------------------------------------------------------------------------------------------- ../build/tests/%.c.o: %.c @@ -57,14 +86,26 @@ all: $(TARGETS) @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ +../build/tests/%.cpp.cairo.o: %.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (Cairo)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ + +../build/tests/%.cpp.opengl.o: %.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (OpenGL)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ + +../build/tests/%.cpp.vulkan.o: Window.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (Vulkan)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_VULKAN -c -o $@ + # --------------------------------------------------------------------------------------------------------------------- clean: rm -rf ../build/tests -debug: - $(MAKE) DEBUG=true - # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS:%.o=%.d) diff --git a/tests/Point.cpp b/tests/Point.cpp index f3e5f687..f51e5552 100644 --- a/tests/Point.cpp +++ b/tests/Point.cpp @@ -31,14 +31,22 @@ static int runTestsPerType() Point p; DISTRHO_ASSERT_EQUAL(p.getX(), 0, "point start X value is 0"); DISTRHO_ASSERT_EQUAL(p.getY(), 0, "point start Y value is 0"); + DISTRHO_ASSERT_EQUAL(p.isZero(), true, "point start is zero"); + DISTRHO_ASSERT_EQUAL(p.isNotZero(), false, "point start is for sure zero"); p.setX(5); DISTRHO_ASSERT_EQUAL(p.getX(), 5, "point X value changed to 5"); DISTRHO_ASSERT_EQUAL(p.getY(), 0, "point start Y value remains 0"); + DISTRHO_ASSERT_EQUAL(p.isZero(), false, "point after custom X is not zero"); + DISTRHO_ASSERT_EQUAL(p.isNotZero(), true, "point after custom X is for sure not zero"); p.setY(7); DISTRHO_ASSERT_EQUAL(p.getX(), 5, "point X value remains 5"); DISTRHO_ASSERT_EQUAL(p.getY(), 7, "point Y value changed to 7"); + DISTRHO_ASSERT_EQUAL(p.isZero(), false, "point after custom X and Y is not zero"); + DISTRHO_ASSERT_EQUAL(p.isNotZero(), true, "point after custom X and Y is for sure not zero"); + + // TODO everything else } return 0; diff --git a/tests/Window.cpp b/tests/Window.cpp new file mode 100644 index 00000000..de5d6d00 --- /dev/null +++ b/tests/Window.cpp @@ -0,0 +1,49 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +#if !(defined(DGL_CAIRO) || defined(DGL_OPENGL) || defined(DGL_VULKAN)) +# error test setup failed, must be for cairo, opengl or vulkan +#endif + +#include "tests.hpp" + +#define DPF_TEST_WINDOW_CPP +#include "dgl/src/pugl.cpp" +#include "dgl/src/Application.cpp" +#include "dgl/src/ApplicationPrivateData.cpp" +#include "dgl/src/Window.cpp" +#include "dgl/src/WindowPrivateData.cpp" + +// -------------------------------------------------------------------------------------------------------------------- + +int main() +{ + USE_NAMESPACE_DGL; + + using DGL_NAMESPACE::Window; + + // creating simple window + { + Application app(true); + Window win(app); + } + + // TODO + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- From 9edea25fab9c0e4b02ea3f2e508542f18b7d3f4c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 2 May 2021 18:57:38 +0100 Subject: [PATCH 034/159] Make Window::PrivateData an IdleCallback, add a few comments Signed-off-by: falkTX --- dgl/Window.hpp | 33 ++++++++++++++++++++++++++++- dgl/src/ApplicationPrivateData.cpp | 12 ----------- dgl/src/ApplicationPrivateData.hpp | 2 -- dgl/src/WindowPrivateData.cpp | 17 ++++++++++----- dgl/src/WindowPrivateData.hpp | 34 +++++++++++++++++++++++++----- tests/Makefile | 10 ++++++--- 6 files changed, 80 insertions(+), 28 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index d297c16c..efff9968 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -21,15 +21,46 @@ START_NAMESPACE_DGL +class Application; + // ----------------------------------------------------------------------- -class Application; +/** + DGL Window class. + + This is the where all OS-related events initially happen, before being propagated to any widgets. + + A Window MUST have an Application instance tied to it. + It is not possible to swap Application instances from within the lifetime of a Window. + But it is possible to completely change the Widgets that a Window contains during its lifetime. + Typically the event handling functions as following: + Application -> Window -> Top-Level-Widget -> SubWidgets + + ... + + Please note that, unlike many other graphical toolkits out there, + DGL makes a clear distinction between a Window and a Widget. + You cannot directly draw in a Window, you need to create a Widget for that. + + ... + */ class Window { public: + /** + Constructor for a regular, standalone window. + */ explicit Window(Application& app); + + /** + Constructor for an embed Window, typically used in modules or plugins that run inside another host. + */ explicit Window(Application& app, uintptr_t parentWindowHandle, double scaling, bool resizable); + + /** + Destructor. + */ virtual ~Window(); void close(); diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 953b4797..400c07b1 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -29,9 +29,7 @@ Application::PrivateData::PrivateData(const bool standalone) isStandalone(standalone), isQuitting(false), visibleWindows(0), -#ifndef DPF_TEST_APPLICATION_CPP windows(), -#endif idleCallbacks() { DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); @@ -48,9 +46,7 @@ Application::PrivateData::~PrivateData() DISTRHO_SAFE_ASSERT(isQuitting); DISTRHO_SAFE_ASSERT(visibleWindows == 0); -#ifndef DPF_TEST_APPLICATION_CPP windows.clear(); -#endif idleCallbacks.clear(); if (world != nullptr) @@ -87,14 +83,6 @@ void Application::PrivateData::idle(const uint timeoutInMs) puglUpdate(world, timeoutInSeconds); } -// #ifndef DPF_TEST_APPLICATION_CPP -// for (std::list::iterator it = windows.begin(), ite = windows.end(); it != ite; ++it) -// { -// Window* const window(*it); -// window->_idle(); -// } -// #endif - for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) { IdleCallback* const idleCallback(*it); diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 6057b6dc..e202143d 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -43,10 +43,8 @@ struct Application::PrivateData { If 0->1, application is starting. If 1->0, application is quitting/stopping. */ uint visibleWindows; -#ifndef DPF_TEST_APPLICATION_CPP /** List of windows for this application. Used as a way to call each window `idle`. */ std::list windows; -#endif /** List of idle callbacks for this application. Run after all windows `idle`. */ std::list idleCallbacks; diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 4e2dfa64..ee32c55d 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -52,8 +52,8 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons Window::PrivateData::~PrivateData() { - if (self != nullptr) - appData->windows.remove(self); + appData->idleCallbacks.remove(this); + appData->windows.remove(self); if (view != nullptr) puglFreeView(view); @@ -63,7 +63,10 @@ Window::PrivateData::~PrivateData() void Window::PrivateData::init(const bool resizable) { - if (self == nullptr || view == nullptr) + appData->windows.push_back(self); + appData->idleCallbacks.push_back(this); + + if (view == nullptr) { /* DGL_DBG("Failed!\n"); @@ -89,8 +92,6 @@ void Window::PrivateData::init(const bool resizable) // puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); // #endif - appData->windows.push_back(self); - // DGL_DBG("Success!\n"); } @@ -114,6 +115,12 @@ void Window::PrivateData::close() // ----------------------------------------------------------------------- +void Window::PrivateData::idleCallback() +{ +} + +// ----------------------------------------------------------------------- + PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) { Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 5353002d..499a94bd 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,7 +18,7 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" -#include "ApplicationPrivateData.hpp" +// #include "ApplicationPrivateData.hpp" #include "pugl.hpp" @@ -28,21 +28,45 @@ class Widget; // ----------------------------------------------------------------------- -struct Window::PrivateData { +struct Window::PrivateData : IdleCallback { + /** Handy typedef for ... */ typedef Application::PrivateData AppData; - AppData* const appData; - Window* const self; + /** Direct access to DGL Application private data where we registers ourselves in. */ + AppData* const appData; + + /** Pointer to the DGL Window class that this private data belongs to. */ + Window* const self; + + /** Pugl view instance. */ PuglView* const view; + /** Constructor for a regular, standalone window. */ PrivateData(AppData* appData, Window* self); + + /** Constructor for a regular, standalone window with a transient parent. */ PrivateData(AppData* appData, Window* self, Window& transientWindow); + + /** Constructor for an embed Window, with a few extra hints from the host side. */ PrivateData(AppData* appData, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); - ~PrivateData(); + /** Destructor. */ + ~PrivateData() override; + + /** Helper initialization function called at the end of all this class constructors. */ void init(bool resizable); + + /** Hide window and notify application of a window close event. + * Does nothing if window is embed (that is, not standalone). + * The application event-loop will stop if all windows have been closed. + * + * @note It is possible to hide the window while not stopping event-loop. + * A closed window is always hidden, but the reverse is not always true. + */ void close(); + void idleCallback() override; + static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) diff --git a/tests/Makefile b/tests/Makefile index 0b50d12f..f4126348 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -30,9 +30,12 @@ endif ifeq ($(HAVE_VULKAN),true) WTESTS = Window.vulkan endif + TARGETS = $(TESTS:%=../build/tests/%) TARGETS += $(WTESTS:Window.%=../build/tests/Window.%) -OBJS = $(TARGETS:%=%.cpp.o) + +OBJS = $(TESTS:%=../build/tests/%.cpp.o) +OBJS += $(WTESTS:Window.%=../build/tests/Window.%.cpp.o) # --------------------------------------------------------------------------------------------------------------------- @@ -65,8 +68,9 @@ all: $(TARGETS) @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ @echo "Running test for $*" - $(SILENT) - gdb -ex run $@ +# $(SILENT) + $@ +# gdb -ex run ../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o @echo "Linking $*" From 97f90a612854a1f01acb861b49408701233b980c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 May 2021 16:43:44 +0100 Subject: [PATCH 035/159] Start adding some actual implementation to Window class Signed-off-by: falkTX --- .gitmodules | 2 +- dgl/Window.hpp | 27 ++- dgl/src/ApplicationPrivateData.cpp | 18 +- dgl/src/ApplicationPrivateData.hpp | 25 ++- dgl/src/Window.cpp | 38 ++-- dgl/src/WindowPrivateData.cpp | 326 ++++++++++------------------- dgl/src/WindowPrivateData.hpp | 17 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 2 + tests/Makefile | 5 +- tests/Window.cpp | 1 + 11 files changed, 192 insertions(+), 271 deletions(-) diff --git a/.gitmodules b/.gitmodules index 7469d8ed..e04dd974 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "dgl/src/pugl-upstream"] path = dgl/src/pugl-upstream - url = https://github.com/lv2/pugl.git + url = https://github.com/DISTRHO/pugl.git diff --git a/dgl/Window.hpp b/dgl/Window.hpp index efff9968..bd6f17f3 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -63,6 +63,26 @@ public: */ virtual ~Window(); + /** + Whether this Window is embed into another (usually not DGL-controlled) Window. + */ + bool isEmbed() const noexcept; + + bool isVisible() const noexcept; + void setVisible(bool visible); + + inline void show() { setVisible(true); } + inline void hide() { setVisible(true); } + + /** + Hide window and notify application of a window close event. + The application event-loop will stop when all windows have been closed. + For standalone windows only, has no effect if window is embed. + @see isEmbed() + + @note It is possible to hide the window while not stopping the event-loop. + A closed window is always hidden, but the reverse is not always true. + */ void close(); /** @@ -144,8 +164,6 @@ END_NAMESPACE_DGL static Window& withTransientParentWindow(Window& transientParentWindow); - void show(); - void hide(); void exec(bool lockWait = false); void focus(); @@ -156,11 +174,6 @@ END_NAMESPACE_DGL bool openFileBrowser(const FileBrowserOptions& options); #endif - bool isEmbed() const noexcept; - - bool isVisible() const noexcept; - void setVisible(bool visible); - bool isResizable() const noexcept; void setResizable(bool resizable); diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 400c07b1..d92fe1a2 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -28,6 +28,7 @@ Application::PrivateData::PrivateData(const bool standalone) standalone ? PUGL_WORLD_THREADS : 0x0)), isStandalone(standalone), isQuitting(false), + isStarting(true), visibleWindows(0), windows(), idleCallbacks() @@ -36,14 +37,11 @@ Application::PrivateData::PrivateData(const bool standalone) puglSetWorldHandle(world, this); puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); - - // puglSetLogLevel(world, PUGL_LOG_LEVEL_DEBUG); - } Application::PrivateData::~PrivateData() { - DISTRHO_SAFE_ASSERT(isQuitting); + DISTRHO_SAFE_ASSERT(isStarting || isQuitting); DISTRHO_SAFE_ASSERT(visibleWindows == 0); windows.clear(); @@ -57,21 +55,23 @@ Application::PrivateData::~PrivateData() void Application::PrivateData::oneWindowShown() noexcept { - DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); - if (++visibleWindows == 1) + { isQuitting = false; + isStarting = false; + } } -void Application::PrivateData::oneWindowHidden() noexcept +void Application::PrivateData::oneWindowClosed() noexcept { - DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,); if (--visibleWindows == 0) isQuitting = true; } +// -------------------------------------------------------------------------------------------------------------------- + void Application::PrivateData::idle(const uint timeoutInMs) { if (world != nullptr) @@ -92,6 +92,8 @@ void Application::PrivateData::idle(const uint timeoutInMs) void Application::PrivateData::quit() { + DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); + isQuitting = true; #ifndef DPF_TEST_APPLICATION_CPP diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index e202143d..0074755c 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -39,31 +39,38 @@ struct Application::PrivateData { /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ bool isQuitting; + /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ + bool isStarting; + /** Counter of visible windows, only used in standalone mode. If 0->1, application is starting. If 1->0, application is quitting/stopping. */ uint visibleWindows; - /** List of windows for this application. Used as a way to call each window `idle`. */ + /** List of windows for this application. Only used during `close`. */ std::list windows; - /** List of idle callbacks for this application. Run after all windows `idle`. */ + /** List of idle callbacks for this application. */ std::list idleCallbacks; /** Constructor and destructor */ PrivateData(const bool standalone); ~PrivateData(); - /** Flag one window shown or hidden status, which modifies @a visibleWindows. - For standalone mode only. - Modifies @a isQuitting under certain conditions */ + /** Flag one window as shown, which increments @a visibleWindows. + Sets @a isQuitting and @a isStarting as false if this is the first window. + For standalone mode only. */ void oneWindowShown() noexcept; - void oneWindowHidden() noexcept; - /** Run Pugl world update for @a timeoutInMs, and then the idle functions for each window and idle callback, - in order of registration. */ + /** Flag one window as closed, which decrements @a visibleWindows. + Sets @a isQuitting as true if this is the last window. + For standalone mode only. */ + void oneWindowClosed() noexcept; + + /** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */ void idle(const uint timeoutInMs); - /** Set flag indicating application is quitting, and closes all windows in reverse order of registration. */ + /** Set flag indicating application is quitting, and close all windows in reverse order of registration. + For standalone mode only. */ void quit(); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index eae1f2ad..040d6f66 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -37,27 +37,32 @@ Window::~Window() delete pData; } -void Window::close() +bool Window::isEmbed() const noexcept { - pData->close(); + return pData->isEmbed; } -uintptr_t Window::getNativeWindowHandle() const noexcept +bool Window::isVisible() const noexcept { - return puglGetNativeWindow(pData->view); + return pData->isVisible; } -#if 0 -void Window::show() +void Window::setVisible(const bool visible) { - pData->setVisible(true); + pData->setVisible(visible); } -void Window::hide() +void Window::close() +{ + pData->close(); +} + +uintptr_t Window::getNativeWindowHandle() const noexcept { - pData->setVisible(false); + return puglGetNativeWindow(pData->view); } +#if 0 #if 0 void Window::exec(bool lockWait) { @@ -89,21 +94,6 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->fView, prect); } -bool Window::isEmbed() const noexcept -{ - return pData->fUsingEmbed; -} - -bool Window::isVisible() const noexcept -{ - return pData->fVisible; -} - -void Window::setVisible(const bool visible) -{ - pData->setVisible(visible); -} - bool Window::isResizable() const noexcept { return puglGetViewHint(pData->fView, PUGL_RESIZABLE) == PUGL_TRUE; diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index ee32c55d..9e07004d 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -26,32 +26,56 @@ START_NAMESPACE_DGL Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s) : appData(a), self(s), - view(puglNewView(appData->world)) + view(puglNewView(appData->world)), + isClosed(true), + isVisible(false), + isEmbed(false) { - init(true); + init(false); } Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, Window& transientWindow) : appData(a), self(s), - view(puglNewView(appData->world)) + view(puglNewView(appData->world)), + isClosed(true), + isVisible(false), + isEmbed(false) { + init(false); + puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); - init(true); } Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, const uintptr_t parentWindowHandle, const double scaling, const bool resizable) : appData(a), self(s), - view(puglNewView(appData->world)) + view(puglNewView(appData->world)), + isClosed(parentWindowHandle == 0), + isVisible(parentWindowHandle != 0), + isEmbed(parentWindowHandle != 0) { - // TODO view parent init(resizable); + + if (isEmbed) + { + appData->oneWindowShown(); + puglSetParentWindow(view, parentWindowHandle); + puglShow(view); + } } Window::PrivateData::~PrivateData() { + if (isEmbed) + { + puglHide(view); + appData->oneWindowClosed(); + isClosed = true; + isVisible = false; + } + appData->idleCallbacks.remove(this); appData->windows.remove(self); @@ -95,28 +119,102 @@ void Window::PrivateData::init(const bool resizable) // DGL_DBG("Success!\n"); } +// ----------------------------------------------------------------------- + void Window::PrivateData::close() { - /* - DGL_DBG("Window close\n"); +// DGL_DBG("Window close\n"); - if (fUsingEmbed) + if (isEmbed || isClosed) return; + isClosed = true; setVisible(false); + appData->oneWindowClosed(); +} + +// ----------------------------------------------------------------------- + +void Window::PrivateData::setVisible(const bool visible) +{ + if (isVisible == visible) + { +// DGL_DBG("Window setVisible matches current state, ignoring request\n"); + return; + } + if (isEmbed) + { +// DGL_DBG("Window setVisible cannot be called when embedded\n"); + return; + } + +// DGL_DBG("Window setVisible called\n"); + + isVisible = visible; + + if (visible) + { +// #if 0 && defined(DISTRHO_OS_MAC) +// if (mWindow != nullptr) +// { +// if (mParentWindow != nullptr) +// [mParentWindow addChildWindow:mWindow +// ordered:NSWindowAbove]; +// } +// #endif - if (! fFirstInit) + if (isClosed) + { + puglRealize(view); +#ifdef DISTRHO_OS_WINDOWS + puglWin32ShowWindowCentered(view); +#else + puglShow(view); +#endif + appData->oneWindowShown(); + isClosed = false; + } + else + { +#ifdef DISTRHO_OS_WINDOWS + puglWin32RestoreWindow(view); +#else + puglShow(view); +#endif + } + } + else { - fAppData->oneWindowHidden(); - fFirstInit = true; +// #if 0 && defined(DISTRHO_OS_MAC) +// if (mWindow != nullptr) +// { +// if (mParentWindow != nullptr) +// [mParentWindow removeChildWindow:mWindow]; +// } +// #endif + + puglHide(view); + +// if (fModal.enabled) +// exec_fini(); } - */ } // ----------------------------------------------------------------------- void Window::PrivateData::idleCallback() { +// #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->windowSpecificIdle(); } // ----------------------------------------------------------------------- @@ -156,8 +254,6 @@ extern "C" { # define DGL_DEBUG_EVENTS # include "pugl-upstream/src/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/src/mac.m" #elif defined(DISTRHO_OS_WINDOWS) # include "ppugl-upstream/src/win.c" @@ -214,189 +310,6 @@ struct Fallback { // ----------------------------------------------------------------------- -Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(false), - fUsingEmbed(false), - fScaling(1.0), - fAutoScaling(1.0), - fWidgets(), - fModal() -{ - DGL_DBG("Creating window without parent..."); DGL_DBGF; - init(); -} - -#ifndef DPF_TEST_WINDOW_CPP -Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self, Window& transientWindow) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(false), - fUsingEmbed(false), - fScaling(1.0), - fAutoScaling(1.0), - fWidgets(), - fModal(transientWindow.pData) -{ - DGL_DBG("Creating window with parent..."); DGL_DBGF; - init(); - - puglSetTransientFor(fView, transientWindow.getNativeWindowHandle()); -} -#endif - -Window::PrivateData::PrivateData(Application::PrivateData* const appData, Window* const self, - const uintptr_t parentWindowHandle, - const double scaling, - const bool resizable) - : fAppData(appData), - fSelf(self), - fView(puglNewView(appData->world)), - fFirstInit(true), - fVisible(parentWindowHandle != 0), - fUsingEmbed(parentWindowHandle != 0), - fScaling(scaling), - fAutoScaling(1.0), - fWidgets(), - fModal() -{ - if (fUsingEmbed) - { - DGL_DBG("Creating embedded window..."); DGL_DBGF; - puglSetParentWindow(fView, parentWindowHandle); - } - else - { - DGL_DBG("Creating window without parent..."); DGL_DBGF; - } - - init(resizable); - - if (fUsingEmbed) - { - DGL_DBG("NOTE: Embed window is always visible and non-resizable\n"); -// puglShowWindow(fView); -// fAppData->oneWindowShown(); -// fFirstInit = false; - } -} - -Window::PrivateData::~PrivateData() -{ - DGL_DBG("Destroying window..."); DGL_DBGF; - -#if 0 - if (fModal.enabled) - { - exec_fini(); - close(); - } -#endif - - fWidgets.clear(); - - if (fUsingEmbed) - { -// puglHideWindow(fView); -// fAppData->oneWindowHidden(); - } - - if (fSelf != nullptr) - fAppData->windows.remove(fSelf); - -#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) - if (fOpenFilePanel) - { - [fOpenFilePanel release]; - fOpenFilePanel = nullptr; - } - if (fFilePanelDelegate) - { - [fFilePanelDelegate release]; - fFilePanelDelegate = nullptr; - } -#endif - - if (fView != nullptr) - puglFreeView(fView); - - DGL_DBG("Success!\n"); -} - -// ----------------------------------------------------------------------- - -void Window::PrivateData::setVisible(const bool visible) -{ - if (fVisible == visible) - { - DGL_DBG("Window setVisible matches current state, ignoring request\n"); - return; - } - if (fUsingEmbed) - { - DGL_DBG("Window setVisible cannot be called when embedded\n"); - return; - } - - DGL_DBG("Window setVisible called\n"); - - fVisible = visible; - - if (visible) - { -#if 0 && defined(DISTRHO_OS_MAC) - if (mWindow != nullptr) - { - if (mParentWindow != nullptr) - [mParentWindow addChildWindow:mWindow - ordered:NSWindowAbove]; - } -#endif - - if (fFirstInit) - { - puglRealize(fView); -#ifdef DISTRHO_OS_WINDOWS - puglShowWindowCentered(fView); -#else - puglShow(fView); -#endif - fAppData->oneWindowShown(); - fFirstInit = false; - } - else - { -#ifdef DISTRHO_OS_WINDOWS - puglWin32RestoreWindow(fView); -#else - puglShow(fView); -#endif - } - } - else - { -#if 0 && defined(DISTRHO_OS_MAC) - if (mWindow != nullptr) - { - if (mParentWindow != nullptr) - [mParentWindow removeChildWindow:mWindow]; - } -#endif - - puglHide(fView); - -// if (fModal.enabled) -// exec_fini(); - } -} - -// ----------------------------------------------------------------------- - void Window::PrivateData::addWidget(Widget* const widget) { fWidgets.push_back(widget); @@ -486,23 +399,6 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) // ----------------------------------------------------------------------- -void Window::PrivateData::windowSpecificIdle() -{ -#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->windowSpecificIdle(); -} - -// ----------------------------------------------------------------------- - static inline int printModifiers(const uint32_t mods) { diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 499a94bd..ae326454 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,7 +18,6 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" -// #include "ApplicationPrivateData.hpp" #include "pugl.hpp" @@ -41,6 +40,16 @@ struct Window::PrivateData : IdleCallback { /** Pugl view instance. */ PuglView* const view; + /** Whether this Window is closed (not visible or counted in the Application it is tied to). + Defaults to true unless embed (embed windows are never closed). */ + bool isClosed; + + /** Whether this Window is currently visible/mapped. Defaults to false. */ + bool isVisible; + + /** Whether this Window is embed into another (usually not DGL-controlled) Window. */ + const bool isEmbed; + /** Constructor for a regular, standalone window. */ PrivateData(AppData* appData, Window* self); @@ -58,13 +67,15 @@ struct Window::PrivateData : IdleCallback { /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). - * The application event-loop will stop if all windows have been closed. + * The application event-loop will stop when all windows have been closed. * - * @note It is possible to hide the window while not stopping event-loop. + * @note It is possible to hide the window while not stopping the event-loop. * A closed window is always hidden, but the reverse is not always true. */ void close(); + void setVisible(bool visible); + void idleCallback() override; static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 2d3100e4..5a3a1309 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 2d3100e47eee6a8bf2209ee19157de6d23dd1c55 +Subproject commit 5a3a1309ad6432d72cdb7f37e3e36383dd6ba372 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index d31a8467..dc39fc31 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -67,6 +67,8 @@ START_NAMESPACE_DGL #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) +# define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) +# define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) #elif defined(DISTRHO_OS_WINDOWS) #else # include "pugl-upstream/src/x11.c" diff --git a/tests/Makefile b/tests/Makefile index f4126348..e7a413dd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -35,7 +35,7 @@ TARGETS = $(TESTS:%=../build/tests/%) TARGETS += $(WTESTS:Window.%=../build/tests/Window.%) OBJS = $(TESTS:%=../build/tests/%.cpp.o) -OBJS += $(WTESTS:Window.%=../build/tests/Window.%.cpp.o) +OBJS += $(WTESTS:Window.%=../build/tests/Window.cpp.%.o) # --------------------------------------------------------------------------------------------------------------------- @@ -68,8 +68,7 @@ all: $(TARGETS) @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ @echo "Running test for $*" -# $(SILENT) - $@ + $(SILENT) $@ # gdb -ex run ../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o diff --git a/tests/Window.cpp b/tests/Window.cpp index de5d6d00..7de187e1 100644 --- a/tests/Window.cpp +++ b/tests/Window.cpp @@ -39,6 +39,7 @@ int main() { Application app(true); Window win(app); + app.idle(); } // TODO From d85c806875762976946faaae280233429645a49e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 May 2021 17:00:46 +0100 Subject: [PATCH 036/159] Add libglu1-mesa-dev to CI --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index fbca5d6c..b45bf775 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -22,7 +22,7 @@ if [ "${TARGET}" = "win32" ]; then elif [ "${TARGET}" = "win64" ]; then sudo apt-get install -y binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 winehq-stable else - sudo apt-get install -y libcairo2-dev libgl1-mesa-dev libjack-jackd2-dev liblo-dev libx11-dev + sudo apt-get install -y libcairo2-dev libgl1-mesa-dev libglu1-mesa-dev libjack-jackd2-dev liblo-dev libx11-dev fi # Special handling for caching deb archives From 05c6d04adbd2018ca50e8e41a4ddb2ce45c9bd89 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 May 2021 22:39:36 +0100 Subject: [PATCH 037/159] Add the needed bits to make window visible, setting size Signed-off-by: falkTX --- dgl/Application.hpp | 5 - dgl/Cairo.hpp | 2 +- dgl/Window.hpp | 88 ++++- dgl/src/ApplicationPrivateData.cpp | 6 +- dgl/src/ApplicationPrivateData.hpp | 4 +- dgl/src/Window.cpp | 154 +++++---- dgl/src/WindowPrivateData.cpp | 520 ++++++++++++++++------------- dgl/src/WindowPrivateData.hpp | 27 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 76 ++++- dgl/src/pugl.hpp | 12 + tests/Application.cpp | 24 -- tests/Makefile | 53 +-- tests/Window.cpp | 22 +- tests/tests.hpp | 32 +- 15 files changed, 642 insertions(+), 385 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index cf634b3a..ee11db3c 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -21,11 +21,6 @@ START_NAMESPACE_DGL -// -------------------------------------------------------------------------------------------------------------------- -// Forward class names - -class Window; - // -------------------------------------------------------------------------------------------------------------------- /** diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 2aa38408..a234b009 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -30,7 +30,7 @@ START_NAMESPACE_DGL */ struct CairoGraphicsContext : GraphicsContext { - cairo_t* cairo; // FIXME proper name.. + cairo_t* handle; }; // ----------------------------------------------------------------------- diff --git a/dgl/Window.hpp b/dgl/Window.hpp index bd6f17f3..09e719cb 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -56,7 +56,12 @@ public: /** Constructor for an embed Window, typically used in modules or plugins that run inside another host. */ - explicit Window(Application& app, uintptr_t parentWindowHandle, double scaling, bool resizable); + explicit Window(Application& app, + uintptr_t parentWindowHandle, + uint width, + uint height, + double scaling, + bool resizable); /** Destructor. @@ -68,11 +73,33 @@ public: */ bool isEmbed() const noexcept; + /** + Check if this window is visible / mapped. + Invisible windows do not receive events except resize. + @see setVisible(bool) + */ bool isVisible() const noexcept; + + /** + Set windows visible (or not) according to @a visible. + Only valid for standalones, embed windows are always visible. + @see isVisible(), hide(), show() + */ void setVisible(bool visible); - inline void show() { setVisible(true); } - inline void hide() { setVisible(true); } + /** + Show window. + This is the same as calling setVisible(true). + @see isVisible(), setVisible(bool) + */ + void show(); + + /** + Hide window. + This is the same as calling setVisible(false). + @see isVisible(), setVisible(bool) + */ + void hide(); /** Hide window and notify application of a window close event. @@ -85,6 +112,44 @@ public: */ void close(); + /** + Get width. + */ + uint getWidth() const noexcept; + + /** + Get height. + */ + uint getHeight() const noexcept; + + /** + Get size. + */ + Size getSize() const noexcept; + + /** + Set width. + */ + void setWidth(uint width); + + /** + Set height. + */ + void setHeight(uint height); + + /** + Set size using @a width and @a height values. + */ + void setSize(uint width, uint height); + + /** + Set size. + */ + void setSize(const Size& size); + + const char* getTitle() const noexcept; + void setTitle(const char* title); + /** Get the "native" window handle. Returned value depends on the platform: @@ -95,6 +160,11 @@ public: */ uintptr_t getNativeWindowHandle() const noexcept; +protected: + virtual void onDisplayBefore(); + virtual void onDisplayAfter(); + virtual void onReshape(uint width, uint height); + private: struct PrivateData; PrivateData* const pData; @@ -180,15 +250,6 @@ END_NAMESPACE_DGL bool getIgnoringKeyRepeat() const noexcept; void setIgnoringKeyRepeat(bool ignore) noexcept; - uint getWidth() const noexcept; - uint getHeight() const noexcept; - Size getSize() const noexcept; - void setSize(uint width, uint height); - void setSize(Size size); - - const char* getTitle() const noexcept; - void setTitle(const char* title); - void setGeometryConstraints(uint width, uint height, bool aspect); void setTransientWinId(uintptr_t winId); @@ -206,9 +267,6 @@ END_NAMESPACE_DGL protected: - virtual void onDisplayBefore(); - virtual void onDisplayAfter(); - virtual void onReshape(uint width, uint height); virtual void onClose(); #ifndef DGL_FILE_BROWSER_DISABLED diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index d92fe1a2..0a75af82 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -21,6 +21,8 @@ START_NAMESPACE_DGL +typedef std::list::reverse_iterator WindowListReverseIterator; + // -------------------------------------------------------------------------------------------------------------------- Application::PrivateData::PrivateData(const bool standalone) @@ -97,9 +99,9 @@ void Application::PrivateData::quit() isQuitting = true; #ifndef DPF_TEST_APPLICATION_CPP - for (std::list::reverse_iterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) + for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) { - Window* const window(*rit); + DGL_NAMESPACE::Window* const window(*rit); window->close(); } #endif diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 0074755c..15b252a6 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -47,10 +47,10 @@ struct Application::PrivateData { uint visibleWindows; /** List of windows for this application. Only used during `close`. */ - std::list windows; + std::list windows; /** List of idle callbacks for this application. */ - std::list idleCallbacks; + std::list idleCallbacks; /** Constructor and destructor */ PrivateData(const bool standalone); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 040d6f66..36bb8284 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -29,8 +29,13 @@ START_NAMESPACE_DGL Window::Window(Application& app) : pData(new PrivateData(app.pData, this)) {} -Window::Window(Application& app, const uintptr_t parentWindowHandle, const double scaling, const bool resizable) - : pData(new PrivateData(app.pData, this, parentWindowHandle, scaling, resizable)) {} +Window::Window(Application& app, + const uintptr_t parentWindowHandle, + const uint width, + const uint height, + const double scaling, + const bool resizable) + : pData(new PrivateData(app.pData, this, parentWindowHandle, width, height, scaling, resizable)) {} Window::~Window() { @@ -49,7 +54,20 @@ bool Window::isVisible() const noexcept void Window::setVisible(const bool visible) { - pData->setVisible(visible); + if (visible) + pData->show(); + else + pData->hide(); +} + +void Window::show() +{ + pData->show(); +} + +void Window::hide() +{ + pData->hide(); } void Window::close() @@ -57,11 +75,77 @@ void Window::close() pData->close(); } +uint Window::getWidth() const noexcept +{ + return puglGetFrame(pData->view).width; +} + +uint Window::getHeight() const noexcept +{ + return puglGetFrame(pData->view).height; +} + +Size Window::getSize() const noexcept +{ + const PuglRect rect = puglGetFrame(pData->view); + return Size(rect.width, rect.height); +} + +void Window::setWidth(const uint width) +{ + setSize(width, getHeight()); +} + +void Window::setHeight(const uint height) +{ + setSize(getWidth(), height); +} + +void Window::setSize(const uint width, const uint height) +{ + DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,); + + puglSetWindowSize(pData->view, width, height); +} + +void Window::setSize(const Size& size) +{ + setSize(size.getWidth(), size.getHeight()); +} + +const char* Window::getTitle() const noexcept +{ + return puglGetWindowTitle(pData->view); +} + +void Window::setTitle(const char* const title) +{ + puglSetWindowTitle(pData->view, title); +} + uintptr_t Window::getNativeWindowHandle() const noexcept { return puglGetNativeWindow(pData->view); } +void Window::onDisplayBefore() +{ + const GraphicsContext& context(pData->getGraphicsContext()); + PrivateData::Fallback::onDisplayBefore(context); +} + +void Window::onDisplayAfter() +{ + const GraphicsContext& context(pData->getGraphicsContext()); + PrivateData::Fallback::onDisplayAfter(context); +} + +void Window::onReshape(const uint width, const uint height) +{ + const GraphicsContext& context(pData->getGraphicsContext()); + PrivateData::Fallback::onReshape(context, width, height); +} + #if 0 #if 0 void Window::exec(bool lockWait) @@ -134,44 +218,6 @@ void Window::setGeometryConstraints(const uint width, const uint height, bool as puglUpdateGeometryConstraints(pData->fView, width, height, aspect); } -uint Window::getWidth() const noexcept -{ - return puglGetFrame(pData->fView).width; -} - -uint Window::getHeight() const noexcept -{ - return puglGetFrame(pData->fView).height; -} - -Size Window::getSize() const noexcept -{ - const PuglRect rect = puglGetFrame(pData->fView); - return Size(rect.width, rect.height); -} - -void Window::setSize(const uint width, const uint height) -{ - DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); - - puglSetWindowSize(pData->fView, width, height); -} - -void Window::setSize(const Size size) -{ - setSize(size.getWidth(), size.getHeight()); -} - -const char* Window::getTitle() const noexcept -{ - return puglGetWindowTitle(pData->fView); -} - -void Window::setTitle(const char* const title) -{ - puglSetWindowTitle(pData->fView, title); -} - void Window::setTransientWinId(const uintptr_t winId) { puglSetTransientFor(pData->fView, winId); @@ -189,17 +235,6 @@ Application& Window::getApp() const noexcept } #endif -#if 0 -const GraphicsContext& Window::getGraphicsContext() const noexcept -{ - GraphicsContext& context = pData->fContext; -#ifdef DGL_CAIRO - context.cairo = (cairo_t*)puglGetContext(pData->fView); -#endif - return context; -} -#endif - void Window::_setAutoScaling(double scaling) noexcept { DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); @@ -240,21 +275,6 @@ void Window::removeIdleCallback(IdleCallback* const callback) // ----------------------------------------------------------------------- -void Window::onDisplayBefore() -{ - PrivateData::Fallback::onDisplayBefore(); -} - -void Window::onDisplayAfter() -{ - PrivateData::Fallback::onDisplayAfter(); -} - -void Window::onReshape(const uint width, const uint height) -{ - PrivateData::Fallback::onReshape(width, height); -} - void Window::onClose() { } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 9e07004d..cbc2665c 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -21,6 +21,21 @@ START_NAMESPACE_DGL +#define DGL_DEBUG_EVENTS + +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) +# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); +# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); +# define DGL_DBGF std::fflush(stderr); +#else +# define DGL_DBG(msg) +# define DGL_DBGp(...) +# define DGL_DBGF +#endif + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + // ----------------------------------------------------------------------- Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s) @@ -31,7 +46,7 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons isVisible(false), isEmbed(false) { - init(false); + init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, Window& transientWindow) @@ -42,13 +57,15 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons isVisible(false), isEmbed(false) { - init(false); + init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); } Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, - const uintptr_t parentWindowHandle, const double scaling, const bool resizable) + const uintptr_t parentWindowHandle, + const uint width, const uint height, + const double scaling, const bool resizable) : appData(a), self(s), view(puglNewView(appData->world)), @@ -56,11 +73,12 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0) { - init(resizable); + init(width, height, resizable); if (isEmbed) { appData->oneWindowShown(); + puglSetDefaultSize(view, width, height); puglSetParentWindow(view, parentWindowHandle); puglShow(view); } @@ -85,16 +103,15 @@ Window::PrivateData::~PrivateData() // ----------------------------------------------------------------------- -void Window::PrivateData::init(const bool resizable) +void Window::PrivateData::init(const uint width, const uint height, const bool resizable) { appData->windows.push_back(self); appData->idleCallbacks.push_back(this); + memset(graphicsContext, 0, sizeof(graphicsContext)); if (view == nullptr) { - /* - DGL_DBG("Failed!\n"); - */ + DGL_DBG("Failed to create Pugl view, everything will fail!\n"); return; } @@ -116,231 +133,144 @@ void Window::PrivateData::init(const bool resizable) // puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); // #endif - // DGL_DBG("Success!\n"); -} - -// ----------------------------------------------------------------------- - -void Window::PrivateData::close() -{ -// DGL_DBG("Window close\n"); - - if (isEmbed || isClosed) - return; - - isClosed = true; - setVisible(false); - appData->oneWindowClosed(); + PuglRect rect = puglGetFrame(view); + rect.width = width; + rect.height = height; + puglSetFrame(view, rect); } // ----------------------------------------------------------------------- -void Window::PrivateData::setVisible(const bool visible) +void Window::PrivateData::show() { - if (isVisible == visible) + if (isVisible) { -// DGL_DBG("Window setVisible matches current state, ignoring request\n"); + DGL_DBG("Window show matches current visible state, ignoring request\n"); return; } if (isEmbed) { -// DGL_DBG("Window setVisible cannot be called when embedded\n"); + DGL_DBG("Window show cannot be called when embedded\n"); return; } -// DGL_DBG("Window setVisible called\n"); + DGL_DBG("Window show called\n"); - isVisible = visible; +#if 0 && defined(DISTRHO_OS_MAC) +// if (mWindow != nullptr) +// { +// if (mParentWindow != nullptr) +// [mParentWindow addChildWindow:mWindow +// ordered:NSWindowAbove]; +// } +#endif - if (visible) + if (isClosed) { -// #if 0 && defined(DISTRHO_OS_MAC) -// if (mWindow != nullptr) -// { -// if (mParentWindow != nullptr) -// [mParentWindow addChildWindow:mWindow -// ordered:NSWindowAbove]; -// } -// #endif + isClosed = false; + appData->oneWindowShown(); + + const PuglStatus status = puglRealize(view); + DISTRHO_SAFE_ASSERT_INT_RETURN(status == PUGL_SUCCESS, status, close()); - if (isClosed) - { - puglRealize(view); #ifdef DISTRHO_OS_WINDOWS - puglWin32ShowWindowCentered(view); + puglWin32ShowWindowCentered(view); #else - puglShow(view); + puglShow(view); #endif - appData->oneWindowShown(); - isClosed = false; - } - else - { + } + else + { #ifdef DISTRHO_OS_WINDOWS - puglWin32RestoreWindow(view); + puglWin32RestoreWindow(view); #else - puglShow(view); + puglShow(view); #endif - } } - else - { -// #if 0 && defined(DISTRHO_OS_MAC) -// if (mWindow != nullptr) -// { -// if (mParentWindow != nullptr) -// [mParentWindow removeChildWindow:mWindow]; -// } -// #endif - puglHide(view); + isVisible = true; +} -// if (fModal.enabled) -// exec_fini(); +void Window::PrivateData::hide() +{ + if (! isVisible) + { + DGL_DBG("Window hide matches current visible state, ignoring request\n"); + return; + } + if (isEmbed) + { + DGL_DBG("Window hide cannot be called when embedded\n"); + return; } -} -// ----------------------------------------------------------------------- + DGL_DBG("Window hide called\n"); -void Window::PrivateData::idleCallback() -{ -// #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) -// if (fSelectedFile.isNotEmpty()) +#if 0 && defined(DISTRHO_OS_MAC) +// if (mWindow != nullptr) // { -// char* const buffer = fSelectedFile.getAndReleaseBuffer(); -// fView->fileSelectedFunc(fView, buffer); -// std::free(buffer); +// if (mParentWindow != nullptr) +// [mParentWindow removeChildWindow:mWindow]; // } -// #endif -// -// if (fModal.enabled && fModal.parent != nullptr) -// fModal.parent->windowSpecificIdle(); -} - -// ----------------------------------------------------------------------- - -PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) -{ - Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); - return PUGL_SUCCESS; -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL - -#if 0 -#ifdef DGL_CAIRO -# define PUGL_CAIRO -# include "../Cairo.hpp" -#endif -#ifdef DGL_OPENGL -# define PUGL_OPENGL -# include "../OpenGL.hpp" #endif -#ifndef DPF_TEST_WINDOW_CPP -#include "WidgetPrivateData.hpp" -#include "pugl-upstream/include/pugl/pugl.h" -#include "pugl-extra/extras.h" -#endif + puglHide(view); -extern "C" { -#include "pugl-upstream/src/implementation.c" -#include "pugl-extra/extras.c" -} +// if (fModal.enabled) +// exec_fini(); -#if defined(DISTRHO_OS_HAIKU) -# define DGL_DEBUG_EVENTS -# include "pugl-upstream/src/haiku.cpp" -#elif defined(DISTRHO_OS_MAC) -# include "pugl-upstream/src/mac.m" -#elif defined(DISTRHO_OS_WINDOWS) -# include "ppugl-upstream/src/win.c" -# undef max -# undef min -#else -# define DGL_PUGL_USING_X11 -extern "C" { -# include "pugl-upstream/src/x11.c" -// # ifdef DGL_CAIRO -// # include "pugl-upstream/src/x11_cairo.c" -// # endif -# ifdef DGL_OPENGL -# include "pugl-upstream/src/x11_gl.c" -# endif -# define PUGL_DETAIL_X11_H_INCLUDED -# include "pugl-extra/x11.c" + isVisible = false; } -#endif -#include -#include -#include -#include -#include -#include - -#define FOR_EACH_WIDGET(it) \ - for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) - -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) - -#define DGL_DEBUG_EVENTS +// ----------------------------------------------------------------------- -#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) -# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); -# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); -# define DGL_DBGF std::fflush(stderr); -#else -# define DGL_DBG(msg) -# define DGL_DBGp(...) -# define DGL_DBGF -#endif +void Window::PrivateData::close() +{ + DGL_DBG("Window close\n"); -START_NAMESPACE_DGL + if (isEmbed || isClosed) + return; -// Fallback build-specific Window functions -struct Fallback { - static void onDisplayBefore(); - static void onDisplayAfter(); - static void onReshape(uint width, uint height); -}; + isClosed = true; + hide(); + appData->oneWindowClosed(); +} // ----------------------------------------------------------------------- -void Window::PrivateData::addWidget(Widget* const widget) +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { - fWidgets.push_back(widget); -} - -void Window::PrivateData::removeWidget(Widget* const widget) -{ - fWidgets.remove(widget); + GraphicsContext& context((GraphicsContext&)graphicsContext); +#ifdef DGL_CAIRO + ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); +#endif + return context; } // ----------------------------------------------------------------------- -void Window::PrivateData::onPuglClose() +void Window::PrivateData::idleCallback() { - DGL_DBG("PUGL: onClose\n"); - -// if (fModal.enabled) -// exec_fini(); - - fSelf->onClose(); - - if (fModal.childFocus != nullptr) - fModal.childFocus->fSelf->onClose(); - - close(); +// #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->windowSpecificIdle(); } +// ----------------------------------------------------------------------- + void Window::PrivateData::onPuglDisplay() { - fSelf->onDisplayBefore(); + self->onDisplayBefore(); + /* if (fWidgets.size() != 0) { const PuglRect rect = puglGetFrame(fView); @@ -353,8 +283,9 @@ void Window::PrivateData::onPuglDisplay() widget->pData->display(width, height, fAutoScaling, false); } } + */ - fSelf->onDisplayAfter(); + self->onDisplayAfter(); } void Window::PrivateData::onPuglReshape(const int width, const int height) @@ -363,8 +294,9 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); - fSelf->onReshape(width, height); + self->onReshape(width, height); + /* FOR_EACH_WIDGET(it) { Widget* const widget(*it); @@ -372,35 +304,43 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) if (widget->pData->needsFullViewport) widget->setSize(width, height); } + */ } -void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) -{ - DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); - -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); +static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); - Widget::MouseEvent rev = ev; - double x = ev.pos.getX() / fAutoScaling; - double y = ev.pos.getY() / fAutoScaling; +PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) +{ + printEvent(event, "pugl event: ", true); + Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); - FOR_EACH_WIDGET_INV(rit) + switch (event->type) { - Widget* const widget(*rit); + ///< No event + case PUGL_NOTHING: + break; - rev.pos = Point(x - widget->getAbsoluteX(), - y - widget->getAbsoluteY()); + ///< View moved/resized, a #PuglEventConfigure + case PUGL_CONFIGURE: + pData->onPuglReshape(event->configure.width, event->configure.height); + break; - if (widget->isVisible() && widget->onMouse(rev)) - break; + ///< View must be drawn, a #PuglEventExpose + case PUGL_EXPOSE: + pData->onPuglDisplay(); + break; + + // TODO + default: + break; } + + return PUGL_SUCCESS; } // ----------------------------------------------------------------------- -static inline int -printModifiers(const uint32_t mods) +static int printModifiers(const uint32_t mods) { return fprintf(stderr, "Modifiers:%s%s%s%s\n", (mods & PUGL_MOD_SHIFT) ? " Shift" : "", @@ -409,8 +349,7 @@ printModifiers(const uint32_t mods) (mods & PUGL_MOD_SUPER) ? " Super" : ""); } -static inline int -printEvent(const PuglEvent* event, const char* prefix, const bool verbose) +static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose) { #define FFMT "%6.1f" #define PFMT FFMT " " FFMT @@ -526,9 +465,156 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose) return 0; } +// ----------------------------------------------------------------------- + +void Window::PrivateData::Fallback::onDisplayBefore(const GraphicsContext&) +{ +#ifdef DGL_OPENGL + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); +#endif +} + +void Window::PrivateData::Fallback::onDisplayAfter(const GraphicsContext&) +{ +} + +void Window::PrivateData::Fallback::onReshape(const GraphicsContext&, 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(width), static_cast(height), 0.0, 0.0, 1.0); + glViewport(0, 0, static_cast(width), static_cast(height)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#else + // unused + (void)width; + (void)height; +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#if 0 +#ifdef DGL_CAIRO +# define PUGL_CAIRO +# include "../Cairo.hpp" +#endif +#ifdef DGL_OPENGL +# define PUGL_OPENGL +# include "../OpenGL.hpp" +#endif + +#ifndef DPF_TEST_WINDOW_CPP +#include "WidgetPrivateData.hpp" +#include "pugl-upstream/include/pugl/pugl.h" +#include "pugl-extra/extras.h" +#endif + +extern "C" { +#include "pugl-upstream/src/implementation.c" +#include "pugl-extra/extras.c" +} + +#if defined(DISTRHO_OS_HAIKU) +# define DGL_DEBUG_EVENTS +# include "pugl-upstream/src/haiku.cpp" +#elif defined(DISTRHO_OS_MAC) +# include "pugl-upstream/src/mac.m" +#elif defined(DISTRHO_OS_WINDOWS) +# include "ppugl-upstream/src/win.c" +# undef max +# undef min +#else +# define DGL_PUGL_USING_X11 +extern "C" { +# include "pugl-upstream/src/x11.c" +// # ifdef DGL_CAIRO +// # include "pugl-upstream/src/x11_cairo.c" +// # endif +# ifdef DGL_OPENGL +# include "pugl-upstream/src/x11_gl.c" +# endif +# define PUGL_DETAIL_X11_H_INCLUDED +# include "pugl-extra/x11.c" +} +#endif + +#include +#include +#include +#include +#include +#include + +START_NAMESPACE_DGL + +#define FOR_EACH_WIDGET(it) \ + for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + +// ----------------------------------------------------------------------- + +void Window::PrivateData::addWidget(Widget* const widget) +{ + fWidgets.push_back(widget); +} + +void Window::PrivateData::removeWidget(Widget* const widget) +{ + fWidgets.remove(widget); +} + +// ----------------------------------------------------------------------- + +void Window::PrivateData::onPuglClose() +{ + DGL_DBG("PUGL: onClose\n"); + +// if (fModal.enabled) +// exec_fini(); + + fSelf->onClose(); + + if (fModal.childFocus != nullptr) + fModal.childFocus->fSelf->onClose(); + + close(); +} + +void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) +{ + DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + + Widget::MouseEvent rev = ev; + double x = ev.pos.getX() / fAutoScaling; + double y = ev.pos.getY() / fAutoScaling; + + FOR_EACH_WIDGET_INV(rit) + { + Widget* const widget(*rit); + + rev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->isVisible() && widget->onMouse(rev)) + break; + } +} + PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) { - printEvent(event, "", true); Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); switch (event->type) @@ -607,37 +693,5 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu // ----------------------------------------------------------------------- -void Window::PrivateData::Fallback::onDisplayBefore() -{ -#ifdef DGL_OPENGL - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); -#endif -} - -void Window::PrivateData::Fallback::onDisplayAfter() -{ -} - -void 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(width), static_cast(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast(width), static_cast(height)); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#else - // unused - (void)width; - (void)height; -#endif -} - -// ----------------------------------------------------------------------- - END_NAMESPACE_DGL #endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index ae326454..f50dd632 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -40,6 +40,9 @@ struct Window::PrivateData : IdleCallback { /** Pugl view instance. */ PuglView* const view; + /** Reserved space for graphics context. */ + mutable uint8_t graphicsContext[sizeof(void*)]; + /** Whether this Window is closed (not visible or counted in the Application it is tied to). Defaults to true unless embed (embed windows are never closed). */ bool isClosed; @@ -57,13 +60,17 @@ struct Window::PrivateData : IdleCallback { PrivateData(AppData* appData, Window* self, Window& transientWindow); /** Constructor for an embed Window, with a few extra hints from the host side. */ - PrivateData(AppData* appData, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); + PrivateData(AppData* appData, Window* self, uintptr_t parentWindowHandle, + uint width, uint height, double scaling, bool resizable); /** Destructor. */ ~PrivateData() override; /** Helper initialization function called at the end of all this class constructors. */ - void init(bool resizable); + void init(uint width, uint height, bool resizable); + + void show(); + void hide(); /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). @@ -74,12 +81,24 @@ struct Window::PrivateData : IdleCallback { */ void close(); - void setVisible(bool visible); + const GraphicsContext& getGraphicsContext() const noexcept; void idleCallback() override; + // pugl events + void onPuglDisplay(); + void onPuglReshape(int width, int height); + + // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); + // Fallback build-specific Window functions + struct Fallback { + static void onDisplayBefore(const GraphicsContext& context); + static void onDisplayAfter(const GraphicsContext& context); + static void onReshape(const GraphicsContext& context, uint width, uint height); + }; + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; @@ -161,8 +180,6 @@ END_NAMESPACE_DGL // ------------------------------------------------------------------- void onPuglClose(); - void onPuglDisplay(); - void onPuglReshape(const int width, const int height); void onPuglMouse(const Widget::MouseEvent& ev); // ------------------------------------------------------------------- diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 5a3a1309..b0a018a0 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 5a3a1309ad6432d72cdb7f37e3e36383dd6ba372 +Subproject commit b0a018a006c5d7d7796074b86c1b69f8671d2d83 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index dc39fc31..0307107d 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -35,6 +35,10 @@ # include # include # include +# ifdef HAVE_XCURSOR +# include +# include +# endif # ifdef HAVE_XRANDR # include # endif @@ -42,10 +46,6 @@ # include # include # endif -# ifdef HAVE_XCURSOR -# include -# include -# endif # ifdef DGL_CAIRO # include # include @@ -85,6 +85,74 @@ START_NAMESPACE_DGL #include "pugl-upstream/src/implementation.c" +// -------------------------------------------------------------------------------------------------------------------- +// missing in pugl, directly returns title char* pointer + +const char* puglGetWindowTitle(const PuglView* view) +{ + return view->title; +} + +// -------------------------------------------------------------------------------------------------------------------- +// set window size without changing frame x/y position + +PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +{ +#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) + // TODO + const PuglRect frame = { 0.0, 0.0, (double)width, (double)height }; + return puglSetFrame(view, frame); +#elif defined(DISTRHO_OS_WINDOWS) + // matches upstream pugl, except we add SWP_NOMOVE flag + if (view->impl->hwnd) + { + RECT rect = { (long)frame.x, + (long)frame.y, + (long)frame.x + (long)frame.width, + (long)frame.y + (long)frame.height }; + + AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view)); + + if (! SetWindowPos(view->impl->hwnd, + HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER)) + return PUGL_UNKNOWN_ERROR; + } +#else + // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow + if (view->impl->win) + { + if (! XResizeWindow(view->world->impl->display, view->impl->win, width, height)) + return PUGL_UNKNOWN_ERROR; +#if 0 + if (! fResizable) + { + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + + sizeHints.flags = PSize|PMinSize|PMaxSize; + sizeHints.width = static_cast(width); + sizeHints.height = static_cast(height); + sizeHints.min_width = static_cast(width); + sizeHints.min_height = static_cast(height); + sizeHints.max_width = static_cast(width); + sizeHints.max_height = static_cast(height); + + XSetWMNormalHints(xDisplay, xWindow, &sizeHints); + } +#endif + } +#endif + + view->frame.width = width; + view->frame.height = height; + return PUGL_SUCCESS; +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 3d5374e5..4d58b87a 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -31,6 +31,18 @@ START_NAMESPACE_DGL #define PUGL_DISABLE_DEPRECATED #include "pugl-upstream/include/pugl/pugl.h" +PUGL_BEGIN_DECLS + +// missing in pugl, directly returns title char* pointer +PUGL_API const char* +puglGetWindowTitle(const PuglView* view); + +// set window size without changing frame x/y position +PUGL_API PuglStatus +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); + +PUGL_END_DECLS + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/tests/Application.cpp b/tests/Application.cpp index 95c08e61..b9859804 100644 --- a/tests/Application.cpp +++ b/tests/Application.cpp @@ -21,34 +21,10 @@ #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" -#include "distrho/extra/Thread.hpp" - START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- -class ApplicationQuitter : public Thread -{ - Application& app; - -public: - ApplicationQuitter(Application& a) - : Thread("ApplicationQuitter"), - app(a) - { - startThread(); - } - -private: - void run() override - { - d_sleep(2); - app.quit(); - } -}; - -// -------------------------------------------------------------------------------------------------------------------- - struct IdleCallbackCounter : IdleCallback { int counter; diff --git a/tests/Makefile b/tests/Makefile index e7a413dd..276b3360 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -45,39 +45,32 @@ endif ifeq ($(HAVE_OPENGL),true) endif +ifeq ($(HAVE_VULKAN),true) +endif + # --------------------------------------------------------------------------------------------------------------------- all: $(TARGETS) # --------------------------------------------------------------------------------------------------------------------- -../build/tests/%: ../build/tests/%.cpp.o - @echo "Linking $*" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ - @echo "Running test for $*" - $(SILENT)$@ +define RUN_TEST + + ${1} +endef + # valgrind --leak-check=full $@ -../build/tests/%.cairo: ../build/tests/%.cpp.cairo.o - @echo "Linking $*" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ - @echo "Running test for $*" - $(SILENT)$@ +run: $(TARGETS) + $(foreach TEST,$(TARGETS),$(call RUN_TEST,$(TEST))) -../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o - @echo "Linking $*" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ - @echo "Running test for $*" - $(SILENT) $@ -# gdb -ex run +# --------------------------------------------------------------------------------------------------------------------- -../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o - @echo "Linking $*" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ - @echo "Running test for $*" - $(SILENT)$@ +clean: + rm -rf ../build/tests # --------------------------------------------------------------------------------------------------------------------- +# building steps ../build/tests/%.c.o: %.c -@mkdir -p ../build/tests @@ -105,9 +98,23 @@ all: $(TARGETS) $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_VULKAN -c -o $@ # --------------------------------------------------------------------------------------------------------------------- +# linking steps -clean: - rm -rf ../build/tests +../build/tests/%: ../build/tests/%.cpp.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ + +../build/tests/%.cairo: ../build/tests/%.cpp.cairo.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ + +../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + +../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Window.cpp b/tests/Window.cpp index 7de187e1..2c726278 100644 --- a/tests/Window.cpp +++ b/tests/Window.cpp @@ -21,9 +21,11 @@ #include "tests.hpp" #define DPF_TEST_WINDOW_CPP +#define DPF_TEST_POINT_CPP #include "dgl/src/pugl.cpp" #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" +#include "dgl/src/Geometry.cpp" #include "dgl/src/Window.cpp" #include "dgl/src/WindowPrivateData.cpp" @@ -35,11 +37,27 @@ int main() using DGL_NAMESPACE::Window; - // creating simple window + // creating and destroying simple window { Application app(true); Window win(app); - app.idle(); + } + + // creating and destroying simple window, with a delay + { + Application app(true); + ApplicationQuitter appQuitter(app); + Window win(app); + app.exec(); + } + + // showing and closing simple window, MUST be visible on screen + { + Application app(true); + ApplicationQuitter appQuitter(app); + Window win(app); + win.show(); + app.exec(); } // TODO diff --git a/tests/tests.hpp b/tests/tests.hpp index f3aeae85..37bdd6f1 100644 --- a/tests/tests.hpp +++ b/tests/tests.hpp @@ -14,10 +14,40 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "dgl/Base.hpp" +#include "dgl/Application.hpp" + +#include "distrho/extra/Thread.hpp" #define DISTRHO_ASSERT_EQUAL(v1, v2, msg) \ if (v1 != v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } #define DISTRHO_ASSERT_NOT_EQUAL(v1, v2, msg) \ if (v1 == v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +class ApplicationQuitter : public Thread +{ + Application& app; + +public: + ApplicationQuitter(Application& a) + : Thread("ApplicationQuitter"), + app(a) + { + startThread(); + } + +private: + void run() override + { + d_sleep(2); + app.quit(); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL From 272e1bbfbd9d70e3852fcd666716c72310e3ac23 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 May 2021 23:40:48 +0100 Subject: [PATCH 038/159] Start coupling widget and window Signed-off-by: falkTX --- dgl/Makefile | 33 ++++---- dgl/TopLevelWidget.hpp | 28 +++++++ dgl/Widget.hpp | 22 +++--- dgl/Window.hpp | 10 +-- dgl/src/TopLevelWidget.cpp | 25 +++++- dgl/src/TopLevelWidgetPrivateData.cpp | 68 ++++++++++++++++ dgl/src/TopLevelWidgetPrivateData.hpp | 12 +-- dgl/src/Window.cpp | 18 ----- dgl/src/WindowPrivateData.cpp | 109 ++++++-------------------- dgl/src/WindowPrivateData.hpp | 9 ++- dgl/src/pugl.cpp | 65 +++++++++++++++ dgl/src/pugl.hpp | 12 +++ 12 files changed, 266 insertions(+), 145 deletions(-) create mode 100644 dgl/src/TopLevelWidgetPrivateData.cpp diff --git a/dgl/Makefile b/dgl/Makefile index e75d88d4..8497ed50 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -10,7 +10,7 @@ include ../Makefile.base.mk BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter -# -Isrc/pugl-upstream/include +BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) # TODO fix these after pugl-upstream is done @@ -27,22 +27,24 @@ OBJS_common = \ ../build/dgl/Application.cpp.o \ ../build/dgl/ApplicationPrivateData.cpp.o \ ../build/dgl/Color.cpp.o \ - ../build/dgl/Geometry.cpp.o + ../build/dgl/Geometry.cpp.o \ + ../build/dgl/TopLevelWidget.cpp.o \ + ../build/dgl/Window.cpp.o \ + ../build/dgl/WindowPrivateData.cpp.o # ../build/dgl/ImageBase.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 # --------------------------------------------------------------------------------------------------------------------- -OBJS_cairo = $(OBJS_common) +OBJS_cairo = $(OBJS_common) \ + ../build/dgl/pugl.cpp.cairo.o # ../build/dgl/Cairo.cpp.cairo.o \ -# ../build/dgl/WidgetPrivateData.cpp.cairo.o \ -# ../build/dgl/WindowPrivateData.cpp.cairo.o +# ../build/dgl/WidgetPrivateData.cpp.cairo.o # ifeq ($(MACOS),true) # OBJS_cairo += ../build/dgl/Window.mm.cairo.o @@ -52,7 +54,8 @@ OBJS_cairo = $(OBJS_common) # --------------------------------------------------------------------------------------------------------------------- -OBJS_opengl = $(OBJS_common) +OBJS_opengl = $(OBJS_common) \ + ../build/dgl/pugl.cpp.opengl.o # ../build/dgl/OpenGL.cpp.opengl.o \ # ../build/dgl/Image.cpp.opengl.o \ # ../build/dgl/ImageWidgets.cpp.opengl.o \ @@ -115,10 +118,10 @@ all: $(TARGETS) # --------------------------------------------------------------------------------------------------------------------- -# ../build/dgl/%.cpp.cairo.o: src/%.cpp -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (Cairo variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ +../build/dgl/%.cpp.cairo.o: src/%.cpp + -@mkdir -p ../build/dgl + @echo "Compiling $< (Cairo variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ # ../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* # -@mkdir -p ../build/dgl @@ -132,10 +135,10 @@ all: $(TARGETS) # --------------------------------------------------------------------------------------------------------------------- -# ../build/dgl/%.cpp.opengl.o: src/%.cpp -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (OpenGL variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ +../build/dgl/%.cpp.opengl.o: src/%.cpp + -@mkdir -p ../build/dgl + @echo "Compiling $< (OpenGL variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ # ../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* # -@mkdir -p ../build/dgl diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 056cbac8..96f39c73 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -21,6 +21,8 @@ START_NAMESPACE_DGL +class Window; + // ----------------------------------------------------------------------- /** @@ -31,6 +33,9 @@ START_NAMESPACE_DGL This widget takes the full size of the Window it is mapped to. Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent. Doing so allows for custom position and sizes. + + This class is used as the type for DPF Plugin UIs. + So anything that a plugin UI might need that does not belong in a simple Widget will go here. */ class TopLevelWidget : public Widget { @@ -45,9 +50,32 @@ public: */ virtual ~TopLevelWidget(); +protected: + /** + A function called before any draw operations begin (in the current event-loop cycle). + Can be used to setup any resources for needed drawing. + The default implementation simply paints the full Widget contents black. + */ + virtual void onDisplayBefore(); + + /** + A function called after all draw operations have ended (in the current event-loop cycle). + Can be used to clear any resources setup during onDisplayBefore(). + The default implementation does nothing. + */ + virtual void onDisplayAfter(); + + /** + A function called when the widget is resized. + Reimplemented from Widget::onResize. + */ + void onResize(const ResizeEvent&) override; + private: struct PrivateData; PrivateData* const pData; + friend class Window; + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) }; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 9a993cbd..31cca833 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -22,19 +22,19 @@ // ----------------------------------------------------------------------- // Forward class names -#ifdef DISTRHO_DEFINES_H_INCLUDED -START_NAMESPACE_DISTRHO -class UI; -END_NAMESPACE_DISTRHO -#endif +// #ifdef DISTRHO_DEFINES_H_INCLUDED +// START_NAMESPACE_DISTRHO +// class UI; +// END_NAMESPACE_DISTRHO +// #endif START_NAMESPACE_DGL // class Application; // class NanoWidget; -class Window; +// class Window; // class StandaloneWindow; -class SubWidget; +// class SubWidget; class TopLevelWidget; using namespace Events; @@ -208,10 +208,10 @@ private: // friend class NanoWidget; // friend class Window; // friend class StandaloneWindow; - friend class SubWidget; -#ifdef DISTRHO_DEFINES_H_INCLUDED - friend class DISTRHO_NAMESPACE::UI; -#endif + friend class TopLevelWidget; +// #ifdef DISTRHO_DEFINES_H_INCLUDED +// friend class DISTRHO_NAMESPACE::UI; +// #endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) }; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 09e719cb..0868f7be 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -37,12 +37,13 @@ class Application; Typically the event handling functions as following: Application -> Window -> Top-Level-Widget -> SubWidgets - ... - Please note that, unlike many other graphical toolkits out there, DGL makes a clear distinction between a Window and a Widget. You cannot directly draw in a Window, you need to create a Widget for that. + Also, a Window MUST have a single top-level Widget. + The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle. + ... */ class Window @@ -160,11 +161,6 @@ public: */ uintptr_t getNativeWindowHandle() const noexcept; -protected: - virtual void onDisplayBefore(); - virtual void onDisplayAfter(); - virtual void onReshape(uint width, uint height); - private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 903ccdca..59525400 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -15,11 +15,34 @@ */ #include "TopLevelWidgetPrivateData.hpp" +#include "pugl.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- TopLevelWidget::TopLevelWidget(Window& windowToMapTo) - : pData(new PrivateData(this, windowToMapTo)) {} + : Widget(*this), + pData(new PrivateData(this, windowToMapTo)) {} TopLevelWidget::~TopLevelWidget() { delete pData; } + +void TopLevelWidget::onDisplayBefore() +{ +} + +void TopLevelWidget::onDisplayAfter() +{ +} + +void TopLevelWidget::onResize(const ResizeEvent& ev) +{ + Widget::onResize(ev); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp new file mode 100644 index 00000000..5c20e7ad --- /dev/null +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -0,0 +1,68 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "TopLevelWidgetPrivateData.hpp" +#include "../Window.hpp" +// #include "pugl.hpp" + +START_NAMESPACE_DGL + +#define FOR_EACH_WIDGET(it) \ + for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + +#define FOR_EACH_WIDGET_INV(rit) \ + for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + +// ----------------------------------------------------------------------- + +TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) + : self(s), + window(w), + widgets() {} + +void TopLevelWidget::PrivateData::display() +{ + if (widgets.size() == 0) + return; + + const Size size(window.getSize()); +// const int width = rect.width; +// const int height = rect.height; + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + widget->pData->display(width, height, fAutoScaling, false); + } +} + +void TopLevelWidget::PrivateData::resize(const uint width, const uint height) +{ + if (widgets.size() == 0) + return; + + FOR_EACH_WIDGET(it) + { + Widget* const widget(*it); + + if (widget->pData->needsFullViewport) + widget->setSize(width, height); + } +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index 7f394c8b..b6eefdff 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -18,7 +18,8 @@ #define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED #include "../TopLevelWidget.hpp" -// #include "../WidgetPrivateData.hpp" + +#include START_NAMESPACE_DGL @@ -27,12 +28,13 @@ START_NAMESPACE_DGL struct TopLevelWidget::PrivateData { TopLevelWidget* const self; Window& window; + std::list widgets; - PrivateData(TopLevelWidget* const s, Window& w) - : self(s), - window(w) {} + PrivateData(TopLevelWidget* const s, Window& w); + void display(); + void resize(uint width, uint height); - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; // ----------------------------------------------------------------------- diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 36bb8284..cade10cd 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -128,24 +128,6 @@ uintptr_t Window::getNativeWindowHandle() const noexcept return puglGetNativeWindow(pData->view); } -void Window::onDisplayBefore() -{ - const GraphicsContext& context(pData->getGraphicsContext()); - PrivateData::Fallback::onDisplayBefore(context); -} - -void Window::onDisplayAfter() -{ - const GraphicsContext& context(pData->getGraphicsContext()); - PrivateData::Fallback::onDisplayAfter(context); -} - -void Window::onReshape(const uint width, const uint height) -{ - const GraphicsContext& context(pData->getGraphicsContext()); - PrivateData::Fallback::onReshape(context, width, height); -} - #if 0 #if 0 void Window::exec(bool lockWait) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index cbc2665c..72d891f0 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -15,10 +15,12 @@ */ #include "WindowPrivateData.hpp" -#include "../Widget.hpp" +#include "../TopLevelWidget.hpp" #include "pugl.hpp" +#include + START_NAMESPACE_DGL #define DGL_DEBUG_EVENTS @@ -42,6 +44,7 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons : appData(a), self(s), view(puglNewView(appData->world)), + topLevelWidget(nullptr), isClosed(true), isVisible(false), isEmbed(false) @@ -53,6 +56,7 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons : appData(a), self(s), view(puglNewView(appData->world)), + topLevelWidget(nullptr), isClosed(true), isVisible(false), isEmbed(false) @@ -69,6 +73,7 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons : appData(a), self(s), view(puglNewView(appData->world)), + topLevelWidget(nullptr), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0) @@ -115,15 +120,7 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r return; } -#ifdef DGL_CAIRO - puglSetBackend(view, puglCairoBackend()); -#endif -#ifdef DGL_OPENGL - puglSetBackend(view, puglGlBackend()); -#endif -#ifdef DGL_Vulkan - puglSetBackend(view, puglVulkanBackend()); -#endif + puglSetMatchingBackendForCurrentBuild(view); puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); @@ -238,17 +235,6 @@ void Window::PrivateData::close() // ----------------------------------------------------------------------- -const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept -{ - GraphicsContext& context((GraphicsContext&)graphicsContext); -#ifdef DGL_CAIRO - ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); -#endif - return context; -} - -// ----------------------------------------------------------------------- - void Window::PrivateData::idleCallback() { // #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) @@ -268,24 +254,18 @@ void Window::PrivateData::idleCallback() void Window::PrivateData::onPuglDisplay() { - self->onDisplayBefore(); - - /* - if (fWidgets.size() != 0) +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) { - const PuglRect rect = puglGetFrame(fView); - const int width = rect.width; - const int height = rect.height; - - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - widget->pData->display(width, height, fAutoScaling, false); - } + topLevelWidget->onDisplayBefore(); + topLevelWidget->onDisplay(); + topLevelWidget->onDisplayAfter(); + } + else +#endif + { + puglFallbackOnDisplay(view); } - */ - - self->onDisplayAfter(); } void Window::PrivateData::onPuglReshape(const int width, const int height) @@ -294,17 +274,12 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); - self->onReshape(width, height); - - /* - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - - if (widget->pData->needsFullViewport) - widget->setSize(width, height); - } - */ +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->setSize(width, height); + else +#endif + puglFallbackOnResize(view); } static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); @@ -467,38 +442,6 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver // ----------------------------------------------------------------------- -void Window::PrivateData::Fallback::onDisplayBefore(const GraphicsContext&) -{ -#ifdef DGL_OPENGL - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); -#endif -} - -void Window::PrivateData::Fallback::onDisplayAfter(const GraphicsContext&) -{ -} - -void Window::PrivateData::Fallback::onReshape(const GraphicsContext&, 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(width), static_cast(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast(width), static_cast(height)); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#else - // unused - (void)width; - (void)height; -#endif -} - -// ----------------------------------------------------------------------- - END_NAMESPACE_DGL #if 0 @@ -555,12 +498,6 @@ extern "C" { START_NAMESPACE_DGL -#define FOR_EACH_WIDGET(it) \ - for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) - -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) - // ----------------------------------------------------------------------- void Window::PrivateData::addWidget(Widget* const widget) diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index f50dd632..8f47cebb 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,12 +18,13 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" +#include "ApplicationPrivateData.hpp" #include "pugl.hpp" START_NAMESPACE_DGL -class Widget; +class TopLevelWidget; // ----------------------------------------------------------------------- @@ -43,6 +44,9 @@ struct Window::PrivateData : IdleCallback { /** Reserved space for graphics context. */ mutable uint8_t graphicsContext[sizeof(void*)]; + /** The top-level widget associated with this Window. */ + TopLevelWidget* topLevelWidget; + /** Whether this Window is closed (not visible or counted in the Application it is tied to). Defaults to true unless embed (embed windows are never closed). */ bool isClosed; @@ -92,12 +96,14 @@ struct Window::PrivateData : IdleCallback { // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); +#if 0 // Fallback build-specific Window functions struct Fallback { static void onDisplayBefore(const GraphicsContext& context); static void onDisplayAfter(const GraphicsContext& context); static void onReshape(const GraphicsContext& context, uint width, uint height); }; +#endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; @@ -115,7 +121,6 @@ END_NAMESPACE_DGL bool fUsingEmbed; double fScaling; double fAutoScaling; - std::list fWidgets; struct Modal { bool enabled; diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 0307107d..aba3442e 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -154,5 +154,70 @@ PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int he } // -------------------------------------------------------------------------------------------------------------------- +// set backend that matches current build + +void puglSetMatchingBackendForCurrentBuild(PuglView* view) +{ +#ifdef DGL_CAIRO + puglSetBackend(view, puglCairoBackend()); +#endif +#ifdef DGL_OPENGL + puglSetBackend(view, puglGlBackend()); +#endif +#ifdef DGL_Vulkan + puglSetBackend(view, puglVulkanBackend()); +#endif +} + +// -------------------------------------------------------------------------------------------------------------------- +// DGL specific, build-specific fallback drawing +void puglFallbackOnDisplay(PuglView*) +{ +#ifdef DGL_OPENGL + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); +#endif +} + +// -------------------------------------------------------------------------------------------------------------------- +// DGL specific, build-specific fallback resize + +void puglFallbackOnResize(PuglView* view) +{ +#ifdef DGL_OPENGL + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, static_cast(view->frame.width), static_cast(view->frame.height), 0.0, 0.0, 1.0); + glViewport(0, 0, static_cast(view->frame.width), static_cast(view->frame.height)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +#endif +} + +END_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- +// extra, build-specific stuff + +#include "WindowPrivateData.hpp" + +#ifdef DGL_CAIRO +# include "../Cairo.hpp" +#endif + +START_NAMESPACE_DGL + +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept +{ + GraphicsContext& context((GraphicsContext&)graphicsContext); +#ifdef DGL_CAIRO + ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); +#endif + return context; +} END_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 4d58b87a..86f46eb1 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -41,6 +41,18 @@ puglGetWindowTitle(const PuglView* view); PUGL_API PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); +// DGL specific, assigns backend that matches current DGL build +PUGL_API void +puglSetMatchingBackendForCurrentBuild(PuglView* view); + +// DGL specific, build-specific fallback drawing +PUGL_API void +puglFallbackOnDisplay(PuglView* view); + +// DGL specific, build-specific fallback resize +PUGL_API void +puglFallbackOnResize(PuglView* view); + PUGL_END_DECLS // -------------------------------------------------------------------------------------------------------------------- From c6e9bec69384bbb47fd6dffe30abb601d07804b8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 6 May 2021 17:29:41 +0100 Subject: [PATCH 039/159] Start adding back widget stuff, almost at drawing step Signed-off-by: falkTX --- dgl/Application.hpp | 2 +- dgl/Cairo.hpp | 49 ++++++- dgl/StandaloneWindow.hpp | 54 ++++--- dgl/TopLevelWidget.hpp | 14 +- dgl/Widget.hpp | 73 ++++++---- dgl/Window.hpp | 19 ++- dgl/src/Cairo.cpp | 69 ++++----- dgl/src/OpenGL.cpp | 106 ++++++++------ dgl/src/StandaloneWindow.cpp | 9 +- dgl/src/SubWidget.cpp | 15 +- dgl/src/SubWidgetPrivateData.hpp | 20 +-- dgl/src/TopLevelWidget.cpp | 12 +- dgl/src/TopLevelWidgetPrivateData.cpp | 22 +-- dgl/src/Widget.cpp | 27 ++-- dgl/src/WidgetPrivateData.cpp | 117 +++++++-------- dgl/src/WidgetPrivateData.hpp | 56 +++----- dgl/src/Window.cpp | 14 +- dgl/src/WindowPrivateData.cpp | 31 ++-- dgl/src/WindowPrivateData.hpp | 22 +-- tests/Demo.cpp | 198 ++++++++++++++++++++++++++ tests/Makefile | 12 +- tests/widgets/ExampleColorWidget.hpp | 134 +++++++++++++++++ 22 files changed, 747 insertions(+), 328 deletions(-) create mode 100644 tests/Demo.cpp create mode 100644 tests/widgets/ExampleColorWidget.hpp diff --git a/dgl/Application.hpp b/dgl/Application.hpp index ee11db3c..3d574ca0 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -38,7 +38,7 @@ public: Constructor. */ // NOTE: the default value is not yet passed, so we catch where we use this - Application(bool isStandalone /* = true */); + Application(bool isStandalone = true); /** Destructor. diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index a234b009..9a896ec7 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -17,13 +17,14 @@ #ifndef DGL_CAIRO_HPP_INCLUDED #define DGL_CAIRO_HPP_INCLUDED -#include "Base.hpp" +#include "SubWidget.hpp" +#include "TopLevelWidget.hpp" #include START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** Cairo Graphics context. @@ -33,7 +34,49 @@ struct CairoGraphicsContext : GraphicsContext cairo_t* handle; }; -// ----------------------------------------------------------------------- +/** + Cairo SubWidget, handy class that takes graphics context during onDisplay and passes it in a new function. + */ +class CairoSubWidget : public SubWidget +{ +public: + CairoSubWidget(Widget* widgetToGroupTo) + : SubWidget(widgetToGroupTo) {} + +protected: + void onDisplay() override + { + const CairoGraphicsContext& context((const CairoGraphicsContext&)getGraphicsContext()); + onCairoDisplay(context); + } + + virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoSubWidget); +}; + +/** + Cairo TopLevelWidget, handy class that takes graphics context during onDisplay and passes it in a new function. + */ +class CairoTopLevelWidget : public TopLevelWidget +{ +public: + CairoTopLevelWidget(Window& windowToMapTo) + : TopLevelWidget(windowToMapTo) {} + +protected: + void onDisplay() override + { + const CairoGraphicsContext& context((const CairoGraphicsContext&)getGraphicsContext()); + onCairoDisplay(context); + } + + virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoTopLevelWidget); +}; + +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 3bdfec5a..a788dfb0 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -17,7 +17,6 @@ #ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED #define DGL_STANDALONE_WINDOW_HPP_INCLUDED -#include "Application.hpp" #include "TopLevelWidget.hpp" #include "Window.hpp" @@ -25,33 +24,54 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -class StandaloneWindow : public Application, - public Window +class StandaloneWindow : public Window, + public TopLevelWidget { public: /** Constructor. */ - StandaloneWindow(); + StandaloneWindow(Application& app) + : Window(app), + TopLevelWidget((Window&)*this) {} /** - Show window and execute application. + Overloaded functions to ensure they apply to the Window class. */ - void exec(); + bool isVisible() const noexcept { return Window::isVisible(); } + void setVisible(bool yesNo) { Window::setVisible(yesNo); } + void hide() { Window::hide(); } + void show() { Window::show(); } + uint getWidth() const noexcept { return Window::getWidth(); } + uint getHeight() const noexcept { return Window::getHeight(); } + const Size getSize() const noexcept { return Window::getSize(); } -private: - TopLevelWidget* fWidget; + /** + Overloaded functions to ensure size changes apply on both TopLevelWidget and Window classes. + */ + void setWidth(uint width) + { + TopLevelWidget::setWidth(width); + Window::setWidth(width); + } - /** @internal */ - void onReshape(uint width, uint height) override; + void setHeight(uint height) + { + TopLevelWidget::setHeight(height); + Window::setHeight(height); + } -#if 0 - /** @internal */ - void _addWidget(TopLevelWidget* widget) override; + void setSize(uint width, uint height) + { + TopLevelWidget::setSize(width, height); + Window::setSize(width, height); + } - /** @internal */ - void _removeWidget(TopLevelWidget* widget) override; -#endif + void setSize(const Size& size) + { + TopLevelWidget::setSize(size); + Window::setSize(size); + } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) }; diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 96f39c73..53773073 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -52,18 +52,10 @@ public: protected: /** - A function called before any draw operations begin (in the current event-loop cycle). - Can be used to setup any resources for needed drawing. - The default implementation simply paints the full Widget contents black. + A function called to draw the widget contents. + Reimplemented from Widget::onDisplay. */ - virtual void onDisplayBefore(); - - /** - A function called after all draw operations have ended (in the current event-loop cycle). - Can be used to clear any resources setup during onDisplayBefore(). - The default implementation does nothing. - */ - virtual void onDisplayAfter(); + void onDisplay() override; /** A function called when the widget is resized. diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 31cca833..07fe4eb1 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -30,11 +30,11 @@ START_NAMESPACE_DGL -// class Application; +class Application; // class NanoWidget; // class Window; // class StandaloneWindow; -// class SubWidget; +class SubWidget; class TopLevelWidget; using namespace Events; @@ -46,26 +46,31 @@ using namespace Events; This is the base Widget class, from which all widgets are built. - All widgets have a parent Window where they'll be drawn. - This parent is never changed during the widget lifetime. + All widgets have a parent widget where they'll be drawn, this can be the top-level widget or a group widget. + This parent is never changed during a widget's lifetime. - Widgets receive events in relative coordinates. - (0, 0) means its top-left position. + Widgets receive events in relative coordinates. (0, 0) means its top-left position. - Windows paint widgets in the order they are constructed. - Early widgets are drawn first, at the bottom, then newer ones on top. - Events are sent in the inverse order so that the top-most widget gets + The top-level widget will draw subwidgets in the order they are constructed. + Early subwidgets are drawn first, at the bottom, then newer ones on top. + Events are sent in the inverse order so that the top-most widgets get a chance to catch the event and stop its propagation. - All widget event callbacks do nothing by default. + All widget event callbacks do nothing by default and onDisplay MUST be reimplemented by subclasses. + + @note It is not possible to subclass this Widget class directly, you must use SubWidget or TopLevelWidget instead. */ class Widget { /** - Constructor, reserved for DGL .. - use TopLevelWidget or SubWidget instead + Private constructor, reserved for SubWidget class. + */ + explicit Widget(Widget* widgetToGroupTo); + + /** + Private constructor, reserved for TopLevelWidget class. */ - explicit Widget(TopLevelWidget& topLevelWidget); + explicit Widget(TopLevelWidget* topLevelWidget); public: /** @@ -109,7 +114,7 @@ public: /** Get size. */ - const Size& getSize() const noexcept; + const Size getSize() const noexcept; /** Set width. @@ -131,17 +136,6 @@ public: */ void setSize(const Size& size) noexcept; - /** - Get top-level widget, as passed in the constructor. - */ - TopLevelWidget& getTopLevelWidget() const noexcept; - - /** - Tell this widget's window to repaint itself. - FIXME better description, partial redraw - */ - void repaint() noexcept; - /** Get the Id associated with this widget. @see setId @@ -154,9 +148,35 @@ public: */ void setId(uint id) noexcept; + /** + Get the application associated with this widget's window. + This is the same as calling `getTopLevelWidget()->getApp()`. + */ + Application& getApp() const noexcept; + + /** + Get the graphics context associated with this widget. + GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, + for example GraphicsContext. + @see CairoSubWidget, CairoTopLevelWidget + */ + const GraphicsContext& getGraphicsContext() const noexcept; + + /** + Get top-level widget, as passed directly in the constructor + or going up the chain of group widgets until it finds the top-level widget. + */ + TopLevelWidget* getTopLevelWidget() const noexcept; + + /** + Tell this widget's window to repaint itself. + FIXME better description, partial redraw + */ + void repaint() noexcept; + protected: /** - A function called to draw the view contents with OpenGL. + A function called to draw the widget contents. */ virtual void onDisplay() = 0; @@ -208,6 +228,7 @@ private: // friend class NanoWidget; // friend class Window; // friend class StandaloneWindow; + friend class SubWidget; friend class TopLevelWidget; // #ifdef DISTRHO_DEFINES_H_INCLUDED // friend class DISTRHO_NAMESPACE::UI; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 0868f7be..bb039b6e 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -22,6 +22,7 @@ START_NAMESPACE_DGL class Application; +class TopLevelWidget; // ----------------------------------------------------------------------- @@ -151,6 +152,11 @@ public: const char* getTitle() const noexcept; void setTitle(const char* title); + /** + Get the application associated with this window. + */ + Application& getApp() const noexcept; + /** Get the "native" window handle. Returned value depends on the platform: @@ -161,10 +167,18 @@ public: */ uintptr_t getNativeWindowHandle() const noexcept; +protected: + /** + A function called when the window is resized. + If there is a top-level widget associated with this window, its size will be set right after this function. + */ + virtual void onReshape(uint width, uint height); + private: struct PrivateData; PrivateData* const pData; friend class Application; + friend class TopLevelWidget; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); }; @@ -251,11 +265,6 @@ END_NAMESPACE_DGL double getScaling() const noexcept; -#if 0 - // should this be removed? - Application& getApp() const noexcept; -#endif - const GraphicsContext& getGraphicsContext() const noexcept; void addIdleCallback(IdleCallback* const callback); diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index ac9fff5c..2c1f60d4 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -14,8 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "../Geometry.hpp" #include "../Cairo.hpp" +#include "WidgetPrivateData.hpp" START_NAMESPACE_DGL @@ -63,49 +63,28 @@ void Rectangle::_draw(const bool outline) } // ----------------------------------------------------------------------- -// Possible template data types - -template class Point; -template class Point; -template class Point; -template class Point; -template class Point; -template class Point; - -template class Size; -template class Size; -template class Size; -template class Size; -template class Size; -template class Size; - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; + +void Widget::PrivateData::display(const uint width, + const uint height, + const double scaling, + const bool renderingSubWidget) +{ + if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) + return; + + cairo_t* cr = static_cast(parent.getGraphicsContext()).cairo; + cairo_matrix_t matrix; + cairo_get_matrix(cr, &matrix); + cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); + // TODO: scaling and cropping + + // display widget + self->onDisplay(); + + cairo_set_matrix(cr, &matrix); + + displaySubWidgets(width, height, scaling); +} // ----------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 25a38619..6d3c5bb9 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -14,8 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "../Geometry.hpp" #include "../OpenGL.hpp" +#include "WidgetPrivateData.hpp" START_NAMESPACE_DGL @@ -108,49 +108,65 @@ void Rectangle::_draw(const bool outline) } // ----------------------------------------------------------------------- -// Possible template data types - -template class Point; -template class Point; -template class Point; -template class Point; -template class Point; -template class Point; - -template class Size; -template class Size; -template class Size; -template class Size; -template class Size; -template class Size; - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; + +void Widget::PrivateData::display(const uint width, + const uint height, + const double scaling, + const bool renderingSubWidget) +{ + if (/*(skipDisplay && ! renderingSubWidget) ||*/ size.isInvalid() || ! visible) + return; + +// bool needsDisableScissor = false; + + // reset color + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + +// if (needsFullViewport || (absolutePos.isZero() && size == Size(width, height))) + { + // full viewport size + glViewport(0, + -(height * scaling - height), + width * scaling, + height * scaling); + } +// else if (needsScaling) +// { +// // limit viewport to widget bounds +// glViewport(absolutePos.getX(), +// height - self->getHeight() - absolutePos.getY(), +// self->getWidth(), +// self->getHeight()); +// } +// else +// { +// // only set viewport pos +// glViewport(absolutePos.getX() * scaling, +// -std::round((height * scaling - height) + (absolutePos.getY() * scaling)), +// std::round(width * scaling), +// std::round(height * scaling)); +// +// // then cut the outer bounds +// glScissor(absolutePos.getX() * scaling, +// height - std::round((self->getHeight() + absolutePos.getY()) * scaling), +// std::round(self->getWidth() * scaling), +// std::round(self->getHeight() * scaling)); +// +// glEnable(GL_SCISSOR_TEST); +// needsDisableScissor = true; +// } + + // display widget + self->onDisplay(); + +// if (needsDisableScissor) +// { +// glDisable(GL_SCISSOR_TEST); +// needsDisableScissor = false; +// } + + displaySubWidgets(width, height, scaling); +} // ----------------------------------------------------------------------- diff --git a/dgl/src/StandaloneWindow.cpp b/dgl/src/StandaloneWindow.cpp index 735acd04..6de8a291 100644 --- a/dgl/src/StandaloneWindow.cpp +++ b/dgl/src/StandaloneWindow.cpp @@ -15,16 +15,15 @@ */ #include "../StandaloneWindow.hpp" -#include "WidgetPrivateData.hpp" +#include "../TopLevelWidget.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -StandaloneWindow::StandaloneWindow() - : Application(true), - Window((Application&)*this), - fWidget(nullptr) {} +StandaloneWindow::StandaloneWindow(Application& app) + : Window(app), + TopLevelWidget(this) {} void StandaloneWindow::exec() { diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index f7934d75..7a4838b6 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -15,9 +15,15 @@ */ #include "SubWidgetPrivateData.hpp" +#include "../TopLevelWidget.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- SubWidget::SubWidget(Widget* const widgetToGroupTo) - : pData(new PrivateData(this, widgetToGroupTo)) {} + : Widget(widgetToGroupTo), + pData(new PrivateData(this, widgetToGroupTo)) {} SubWidget::~SubWidget() { @@ -79,9 +85,14 @@ void SubWidget::setAbsolutePos(const Point& pos) noexcept pData->absolutePos = pos; onPositionChanged(ev); - getTopLevelWidget().repaint(); + // repaint the whole thing + pData->parent->repaint(); } void SubWidget::onPositionChanged(const PositionChangedEvent&) { } + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index fb9d490b..12b893ab 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -18,31 +18,25 @@ #define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED #include "../SubWidget.hpp" -#include "../WidgetPrivateData.hpp" - -#include START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- struct SubWidget::PrivateData { SubWidget* const self; - Widget* const groupWidget; + Widget* const parent; Point absolutePos; - PrivateData(SubWidget* const s, Widget* const g) + PrivateData(SubWidget* const s, Widget* const p) : self(s), - groupWidget(g), - absolutePos() - { - groupWidget->pData->subWidgets.push_back(self); - } + parent(p), + absolutePos() {} - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 59525400..2a862243 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -15,14 +15,14 @@ */ #include "TopLevelWidgetPrivateData.hpp" -#include "pugl.hpp" +// #include "pugl.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- TopLevelWidget::TopLevelWidget(Window& windowToMapTo) - : Widget(*this), + : Widget(this), pData(new PrivateData(this, windowToMapTo)) {} TopLevelWidget::~TopLevelWidget() @@ -30,16 +30,14 @@ TopLevelWidget::~TopLevelWidget() delete pData; } -void TopLevelWidget::onDisplayBefore() -{ -} - -void TopLevelWidget::onDisplayAfter() +void TopLevelWidget::onDisplay() { + pData->display(); } void TopLevelWidget::onResize(const ResizeEvent& ev) { + pData->resize(ev.size.getWidth(), ev.size.getHeight()); Widget::onResize(ev); } diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 5c20e7ad..85df0f68 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -15,16 +15,17 @@ */ #include "TopLevelWidgetPrivateData.hpp" -#include "../Window.hpp" -// #include "pugl.hpp" +#include "WidgetPrivateData.hpp" +#include "WindowPrivateData.hpp" +#include "pugl.hpp" START_NAMESPACE_DGL #define FOR_EACH_WIDGET(it) \ - for (std::list::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) + for (std::list::iterator it = widgets.begin(); it != widgets.end(); ++it) #define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) + for (std::list::reverse_iterator rit = widgets.rbegin(); rit != widgets.rend(); ++rit) // ----------------------------------------------------------------------- @@ -35,17 +36,20 @@ TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) void TopLevelWidget::PrivateData::display() { + puglFallbackOnDisplay(window.pData->view); + if (widgets.size() == 0) return; const Size size(window.getSize()); -// const int width = rect.width; -// const int height = rect.height; + const uint width = size.getWidth(); + const uint height = size.getHeight(); + const double scaling = window.pData->autoScaling; FOR_EACH_WIDGET(it) { Widget* const widget(*it); - widget->pData->display(width, height, fAutoScaling, false); + widget->pData->display(width, height, scaling, false); } } @@ -53,14 +57,14 @@ void TopLevelWidget::PrivateData::resize(const uint width, const uint height) { if (widgets.size() == 0) return; - +/* FOR_EACH_WIDGET(it) { Widget* const widget(*it); if (widget->pData->needsFullViewport) widget->setSize(width, height); - } + }*/ } // ----------------------------------------------------------------------- diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 3ad64d70..c331b021 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -21,8 +21,11 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Widget -Widget::Widget(TopLevelWidget& tlw) - : pData(new PrivateData(this, tlw)) {} +Widget::Widget(Widget* const widgetToGroupTo) + : pData(new PrivateData(this, widgetToGroupTo)) {} + +Widget::Widget(TopLevelWidget* const topLevelWidget) + : pData(new PrivateData(this, topLevelWidget)) {} Widget::~Widget() { @@ -40,7 +43,7 @@ void Widget::setVisible(bool yesNo) return; pData->visible = yesNo; - pData->topLevelWidget.repaint(); + repaint(); } void Widget::show() @@ -63,7 +66,7 @@ uint Widget::getHeight() const noexcept return pData->size.getHeight(); } -const Size& Widget::getSize() const noexcept +const Size Widget::getSize() const noexcept { return pData->size; } @@ -80,7 +83,7 @@ void Widget::setWidth(uint width) noexcept pData->size.setWidth(width); onResize(ev); - pData->topLevelWidget.repaint(); + pData->repaint(); } void Widget::setHeight(uint height) noexcept @@ -95,7 +98,7 @@ void Widget::setHeight(uint height) noexcept pData->size.setHeight(height); onResize(ev); - pData->topLevelWidget.repaint(); + pData->repaint(); } void Widget::setSize(uint width, uint height) noexcept @@ -115,17 +118,15 @@ void Widget::setSize(const Size& size) noexcept pData->size = size; onResize(ev); - pData->topLevelWidget.repaint(); + pData->repaint(); } -#if 0 -Application& Widget::getParentApp() const noexcept +Application& Widget::getApp() const noexcept { - return pData->parent.getApp(); + return pData->topLevelWidget->getApp(); } -#endif -TopLevelWidget& Widget::getTopLevelWidget() const noexcept +TopLevelWidget* Widget::getTopLevelWidget() const noexcept { return pData->topLevelWidget; } diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index b28bf176..73f493c2 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -15,6 +15,7 @@ */ #include "WidgetPrivateData.hpp" +#include "../TopLevelWidget.hpp" #ifdef DGL_CAIRO # include "../Cairo.hpp" @@ -27,79 +28,67 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -void Widget::PrivateData::display(const uint width, - const uint height, - const double scaling, - const bool renderingSubWidget) +Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) + : self(s), + topLevelWidget(tlw), + groupWidget(nullptr), + id(0), + needsScaling(false), + visible(true), + size(0, 0), + subWidgets() { - if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) - return; +} -#ifdef DGL_OPENGL - bool needsDisableScissor = false; +Widget::PrivateData::PrivateData(Widget* const s, Widget* const g) + : self(s), + topLevelWidget(findTopLevelWidget(g)), + groupWidget(g), + id(0), + needsScaling(false), + visible(true), + size(0, 0), + subWidgets() +{ + groupWidget->pData->subWidgets.push_back(self); +} - // reset color - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +Widget::PrivateData::~PrivateData() +{ + if (groupWidget != nullptr) + groupWidget->pData->subWidgets.remove(self); - if (needsFullViewport || (absolutePos.isZero() && size == Size(width, height))) - { - // full viewport size - glViewport(0, - -(height * scaling - height), - width * scaling, - height * scaling); - } - else if (needsScaling) - { - // limit viewport to widget bounds - glViewport(absolutePos.getX(), - height - self->getHeight() - absolutePos.getY(), - self->getWidth(), - self->getHeight()); - } - else - { - // only set viewport pos - glViewport(absolutePos.getX() * scaling, - -std::round((height * scaling - height) + (absolutePos.getY() * scaling)), - std::round(width * scaling), - std::round(height * scaling)); + subWidgets.clear(); +} - // then cut the outer bounds - glScissor(absolutePos.getX() * scaling, - height - std::round((self->getHeight() + absolutePos.getY()) * scaling), - std::round(self->getWidth() * scaling), - std::round(self->getHeight() * scaling)); +void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double scaling) +{ + for (std::list::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) + { + Widget* const widget(*it); + DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); - glEnable(GL_SCISSOR_TEST); - needsDisableScissor = true; + widget->pData->display(width, height, scaling, true); } -#endif - -#ifdef DGL_CAIRO - cairo_t* cr = static_cast(parent.getGraphicsContext()).cairo; - cairo_matrix_t matrix; - cairo_get_matrix(cr, &matrix); - cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); - // TODO: scaling and cropping -#endif - - // display widget - self->onDisplay(); +} -#ifdef DGL_CAIRO - cairo_set_matrix(cr, &matrix); -#endif +void Widget::PrivateData::repaint() +{ + if (groupWidget != nullptr) + groupWidget->repaint(); + else if (topLevelWidget != nullptr) + topLevelWidget->repaint(); +} -#ifdef DGL_OPENGL - if (needsDisableScissor) - { - glDisable(GL_SCISSOR_TEST); - needsDisableScissor = false; - } -#endif +// ----------------------------------------------------------------------- - displaySubWidgets(width, height, scaling); +TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) +{ + if (w->pData->topLevelWidget != nullptr) + return w->pData->topLevelWidget; + if (w->pData->groupWidget != nullptr) + return findTopLevelWidget(w->pData->groupWidget); + return nullptr; } // ----------------------------------------------------------------------- diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 1007eafd..7df7a623 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -17,61 +17,41 @@ #ifndef DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED #define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED -#include "../TopLevelWidget.hpp" -#include "../Window.hpp" +#include "../Widget.hpp" -#include +#include START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- struct Widget::PrivateData { Widget* const self; - Size size; - std::vector subWidgets; - TopLevelWidget& topLevelWidget; - + TopLevelWidget* const topLevelWidget; + Widget* const groupWidget; uint id; bool needsScaling; bool visible; + Size size; + std::list subWidgets; - PrivateData(Widget* const s, TopLevelWidget& tlw) - : self(s), - size(0, 0), - subWidgets(), - topLevelWidget(tlw), - id(0), - needsScaling(false), - visible(true) - { -// parent._addWidget(self); - } - - ~PrivateData() - { -// parent._removeWidget(self); - subWidgets.clear(); - } + PrivateData(Widget* const s, TopLevelWidget* const tlw); + PrivateData(Widget* const s, Widget* const g); + ~PrivateData(); - // display function is different depending on build type + // NOTE display function is different depending on build type void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget); - void displaySubWidgets(const uint width, const uint height, const double scaling) - { - for (std::vector::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) - { - Widget* const widget(*it); - DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); + void displaySubWidgets(const uint width, const uint height, const double scaling); + + void repaint(); - widget->pData->display(width, height, scaling, true); - } - } + static TopLevelWidget* findTopLevelWidget(Widget* const w); - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index cade10cd..096f580c 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -27,7 +27,7 @@ START_NAMESPACE_DGL // : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} Window::Window(Application& app) - : pData(new PrivateData(app.pData, this)) {} + : pData(new PrivateData(app, this)) {} Window::Window(Application& app, const uintptr_t parentWindowHandle, @@ -35,7 +35,7 @@ Window::Window(Application& app, const uint height, const double scaling, const bool resizable) - : pData(new PrivateData(app.pData, this, parentWindowHandle, width, height, scaling, resizable)) {} + : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaling, resizable)) {} Window::~Window() { @@ -123,11 +123,21 @@ void Window::setTitle(const char* const title) puglSetWindowTitle(pData->view, title); } +Application& Window::getApp() const noexcept +{ + return pData->app; +} + uintptr_t Window::getNativeWindowHandle() const noexcept { return puglGetNativeWindow(pData->view); } +void Window::onReshape(const uint width, const uint height) +{ + puglFallbackOnResize(pData->view); +} + #if 0 #if 0 void Window::exec(bool lockWait) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 72d891f0..8735541e 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -40,8 +40,9 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s) - : appData(a), +Window::PrivateData::PrivateData(Application& a, Window* const s) + : app(a), + appData(a.pData), self(s), view(puglNewView(appData->world)), topLevelWidget(nullptr), @@ -52,31 +53,37 @@ Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* cons init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } -Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, Window& transientWindow) - : appData(a), +Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transientWindow) + : app(a), + appData(a.pData), self(s), view(puglNewView(appData->world)), topLevelWidget(nullptr), isClosed(true), isVisible(false), - isEmbed(false) + isEmbed(false), + scaling(1.0), + autoScaling(1.0) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); } -Window::PrivateData::PrivateData(Application::PrivateData* const a, Window* const s, +Window::PrivateData::PrivateData(Application& a, Window* const s, const uintptr_t parentWindowHandle, const uint width, const uint height, - const double scaling, const bool resizable) - : appData(a), + const double scale, const bool resizable) + : app(a), + appData(a.pData), self(s), view(puglNewView(appData->world)), topLevelWidget(nullptr), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), - isEmbed(parentWindowHandle != 0) + isEmbed(parentWindowHandle != 0), + scaling(scale), + autoScaling(1.0) { init(width, height, resizable); @@ -257,9 +264,7 @@ void Window::PrivateData::onPuglDisplay() #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) { - topLevelWidget->onDisplayBefore(); topLevelWidget->onDisplay(); - topLevelWidget->onDisplayAfter(); } else #endif @@ -274,12 +279,12 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); + self->onReshape(width, height); + #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) topLevelWidget->setSize(width, height); - else #endif - puglFallbackOnResize(view); } static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 8f47cebb..0c626d52 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -29,13 +29,13 @@ class TopLevelWidget; // ----------------------------------------------------------------------- struct Window::PrivateData : IdleCallback { - /** Handy typedef for ... */ - typedef Application::PrivateData AppData; + /* Reference to the DGL Application class this (private data) window associates with. */ + Application& app; - /** Direct access to DGL Application private data where we registers ourselves in. */ - AppData* const appData; + /** Direct access to the DGL Application private data where we registers ourselves in. */ + Application::PrivateData* const appData; - /** Pointer to the DGL Window class that this private data belongs to. */ + /** Pointer to the the DGL Window class that this private data belongs to. */ Window* const self; /** Pugl view instance. */ @@ -57,14 +57,20 @@ struct Window::PrivateData : IdleCallback { /** Whether this Window is embed into another (usually not DGL-controlled) Window. */ const bool isEmbed; + /** Scaling to report to widgets on request, purely informational. */ + double scaling; + + /** Automatic scaling to apply on widgets, implemented internally. */ + double autoScaling; + /** Constructor for a regular, standalone window. */ - PrivateData(AppData* appData, Window* self); + PrivateData(Application& app, Window* self); /** Constructor for a regular, standalone window with a transient parent. */ - PrivateData(AppData* appData, Window* self, Window& transientWindow); + PrivateData(Application& app, Window* self, Window& transientWindow); /** Constructor for an embed Window, with a few extra hints from the host side. */ - PrivateData(AppData* appData, Window* self, uintptr_t parentWindowHandle, + PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, uint width, uint height, double scaling, bool resizable); /** Destructor. */ diff --git a/tests/Demo.cpp b/tests/Demo.cpp new file mode 100644 index 00000000..e484e3e9 --- /dev/null +++ b/tests/Demo.cpp @@ -0,0 +1,198 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_OPENGL +#error OpenGL build required for Demo +#endif + +#include "tests.hpp" + +// #define DPF_TEST_POINT_CPP +#include "dgl/src/pugl.cpp" +// #include "dgl/src/SubWidget.cpp" +#include "dgl/src/Application.cpp" +#include "dgl/src/ApplicationPrivateData.cpp" +#include "dgl/src/Geometry.cpp" +#include "dgl/src/OpenGL.cpp" +#include "dgl/src/SubWidget.cpp" +#include "dgl/src/TopLevelWidget.cpp" +#include "dgl/src/TopLevelWidgetPrivateData.cpp" +#include "dgl/src/Widget.cpp" +#include "dgl/src/WidgetPrivateData.cpp" +#include "dgl/src/Window.cpp" +#include "dgl/src/WindowPrivateData.cpp" +#include "dgl/StandaloneWindow.hpp" + +#include "widgets/ExampleColorWidget.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------ +// Left side tab-like widget + +class LeftSideWidget : public SubWidget +{ +public: + static const int kPageCount = 5; + + class Callback + { + public: + virtual ~Callback() {} + virtual void curPageChanged(int curPage) = 0; + }; + + LeftSideWidget(Widget* parent, Callback* const cb) + : SubWidget(parent), + callback(cb), + curPage(0), + curHover(-1) + { + // for text +// font = nvg.createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); + +// using namespace DemoArtwork; +// img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); +// img2.loadFromMemory(ico2Data, ico2Width, ico2Height, GL_BGR); +// img3.loadFromMemory(ico3Data, ico3Width, ico2Height, GL_BGR); +// img4.loadFromMemory(ico4Data, ico4Width, ico4Height, GL_BGR); +// img5.loadFromMemory(ico5Data, ico5Width, ico5Height, GL_BGR); + } + +private: + Callback* const callback; + int curPage, curHover; +// Rectangle bgIcon; +// Line lineSep; +// Image img1, img2, img3, img4, img5; + + // for text +// NanoVG nvg; +// NanoVG::FontId font; +}; + +// ------------------------------------------------------ +// Our Demo Window + +class DemoWindow : public StandaloneWindow, + public LeftSideWidget::Callback +{ + static const int kSidebarWidth = 81; + + ExampleColorWidget wColor; + Widget* curWidget; + +public: + DemoWindow(Application& app) + : StandaloneWindow(app), + wColor(this), + curWidget(nullptr) + { + wColor.hide(); +// wImages.hide(); +// wRects.hide(); +// wShapes.hide(); +// wText.hide(); +// //wPerf.hide(); + + wColor.setAbsoluteX(kSidebarWidth); +// wImages.setAbsoluteX(kSidebarWidth); +// wRects.setAbsoluteX(kSidebarWidth); +// wShapes.setAbsoluteX(kSidebarWidth); +// wText.setAbsoluteX(kSidebarWidth); +// wLeft.setAbsolutePos(2, 2); +// wPerf.setAbsoluteY(5); + + setSize(600, 500); + setTitle("DGL Demo"); + + curPageChanged(0); + } + +protected: + void curPageChanged(int curPage) override + { + if (curWidget != nullptr) + { + curWidget->hide(); + curWidget = nullptr; + } + + switch (curPage) + { + case 0: + curWidget = &wColor; + break; +// case 1: +// curWidget = &wImages; +// break; +// case 2: +// curWidget = &wRects; +// break; +// case 3: +// curWidget = &wShapes; +// break; +// case 4: +// curWidget = &wText; +// break; + } + + if (curWidget != nullptr) + curWidget->show(); + } + + void onReshape(uint width, uint height) override + { + StandaloneWindow::onReshape(width, height); + + if (width < kSidebarWidth) + return; + + Size size(width-kSidebarWidth, height); + wColor.setSize(size); +// wImages.setSize(size); +// wRects.setSize(size); +// wShapes.setSize(size); +// wText.setSize(size); + +// wLeft.setSize(kSidebarWidth-4, height-4); +// //wRezHandle.setAbsoluteX(width-wRezHandle.getWidth()); +// //wRezHandle.setAbsoluteY(height-wRezHandle.getHeight()); +// +// wPerf.setAbsoluteX(width-wPerf.getWidth()-5); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +int main() +{ + USE_NAMESPACE_DGL; + + Application app; + DemoWindow win(app); + + win.show(); + app.exec(); + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Makefile b/tests/Makefile index 276b3360..93c6e6c5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,6 +25,7 @@ ifeq ($(HAVE_CAIRO),true) WTESTS = Window.cairo endif ifeq ($(HAVE_OPENGL),true) +TESTS += Demo WTESTS = Window.opengl endif ifeq ($(HAVE_VULKAN),true) @@ -87,12 +88,17 @@ clean: @echo "Compiling $< (Cairo)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ +../build/tests/Demo.cpp.o: Demo.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (OpenGL)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ + ../build/tests/%.cpp.opengl.o: %.cpp -@mkdir -p ../build/tests @echo "Compiling $< (OpenGL)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -../build/tests/%.cpp.vulkan.o: Window.cpp +../build/tests/%.cpp.vulkan.o: %.cpp -@mkdir -p ../build/tests @echo "Compiling $< (Vulkan)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_VULKAN -c -o $@ @@ -108,6 +114,10 @@ clean: @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ +../build/tests/Demo: ../build/tests/Demo.cpp.o + @echo "Linking Demo" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + ../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp new file mode 100644 index 00000000..ce35e5e6 --- /dev/null +++ b/tests/widgets/ExampleColorWidget.hpp @@ -0,0 +1,134 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 EXAMPLE_COLOR_WIDGET_HPP_INCLUDED +#define EXAMPLE_COLOR_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "../../dgl/SubWidget.hpp" +// #include "Window.hpp" + +START_NAMESPACE_DGL + +// ------------------------------------------------------ +// our widget + +class ExampleColorWidget : public SubWidget, + public IdleCallback +{ +public: + ExampleColorWidget(TopLevelWidget* const topWidget) + : SubWidget(topWidget), + cur('r'), + reverse(false), + r(0), g(0), b(0) + { + setSize(300, 300); + +// groupWidget->getApp().addIdleCallback(this); + } + +protected: + void idleCallback() noexcept override + { + switch (cur) + { + case 'r': + if (reverse) + { + if (--r == 0) + cur = 'g'; + } + else + { + if (++r == 100) + cur = 'g'; + } + break; + + case 'g': + if (reverse) + { + if (--g == 0) + cur = 'b'; + } + else + { + if (++g == 100) + cur = 'b'; + } + break; + + case 'b': + if (reverse) + { + if (--b == 0) + { + cur = 'r'; + reverse = false; + } + } + else + { + if (++b == 100) + { + cur = 'r'; + reverse = true; + } + } + break; + } + + repaint(); + } + + void onDisplay() override + { + // paint bg color (in full size) + glColor3b(r, g, b); + bgFull.draw(); + + // paint inverted color (in 2/3 size) + glColor3b(100-r, 100-g, 100-b); + bgSmall.draw(); + } + + void onResize(const ResizeEvent& ev) override + { + const uint width = ev.size.getWidth(); + const uint height = ev.size.getHeight(); + + // full bg + bgFull = Rectangle(0, 0, width, height); + + // small bg, centered 2/3 size + bgSmall = Rectangle(width/6, height/6, width*2/3, height*2/3); + } + + char cur; + bool reverse; + int r, g, b; + + Rectangle bgFull, bgSmall; +}; + +// ------------------------------------------------------ + +END_NAMESPACE_DGL + +#endif // EXAMPLE_COLOR_WIDGET_HPP_INCLUDED From 9c5c7929a6234236c9370f5a79e9b4df82f2e4ad Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 6 May 2021 20:10:30 +0100 Subject: [PATCH 040/159] Cleanup SubWidget class Signed-off-by: falkTX --- dgl/SubWidget.hpp | 18 +++++++++++------- dgl/src/SubWidget.cpp | 5 ++--- dgl/src/WidgetPrivateData.cpp | 20 +++++++++++--------- dgl/src/WidgetPrivateData.hpp | 4 ++-- tests/widgets/ExampleColorWidget.hpp | 17 ++++++++--------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 45778703..7510ae83 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -21,16 +21,20 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** Sub-Widget class. - This is a handy Widget class that can be freely positioned to be used directly on a Window. + This class is the main entry point for creating any reusable widgets from within DGL. + It can be freely positioned from within a parent widget, thus being named subwidget. - This widget takes the full size of the Window it is mapped to. - Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent. - Doing so allows for custom position and sizes. + Many subwidgets can share the same parent, and subwidgets themselves can also have its own subwidgets. + It is subwidgets all the way down. + + TODO check absolute vs relative position and see what makes more sense. + + @see CairoSubWidget */ class SubWidget : public Widget { @@ -70,7 +74,7 @@ public: /** Get absolute position. */ - const Point& getAbsolutePos() const noexcept; + Point getAbsolutePos() const noexcept; /** Set absolute X. @@ -104,7 +108,7 @@ private: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 7a4838b6..48edb7d5 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -33,8 +33,7 @@ SubWidget::~SubWidget() template bool SubWidget::contains(T x, T y) const noexcept { - const Size& size(getSize()); - return (x >= 0 && y >= 0 && static_cast(x) < size.getWidth() && static_cast(y) < size.getHeight()); + return Rectangle(getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight()).contains(x, y); } template @@ -53,7 +52,7 @@ int SubWidget::getAbsoluteY() const noexcept return pData->absolutePos.getY(); } -const Point& SubWidget::getAbsolutePos() const noexcept +Point SubWidget::getAbsolutePos() const noexcept { return pData->absolutePos; } diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 73f493c2..e09c8860 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -31,7 +31,7 @@ START_NAMESPACE_DGL Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) : self(s), topLevelWidget(tlw), - groupWidget(nullptr), + parentGroupWidget(nullptr), id(0), needsScaling(false), visible(true), @@ -43,20 +43,20 @@ Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) Widget::PrivateData::PrivateData(Widget* const s, Widget* const g) : self(s), topLevelWidget(findTopLevelWidget(g)), - groupWidget(g), + parentGroupWidget(g), id(0), needsScaling(false), visible(true), size(0, 0), subWidgets() { - groupWidget->pData->subWidgets.push_back(self); + parentGroupWidget->pData->subWidgets.push_back(self); } Widget::PrivateData::~PrivateData() { - if (groupWidget != nullptr) - groupWidget->pData->subWidgets.remove(self); + if (parentGroupWidget != nullptr) + parentGroupWidget->pData->subWidgets.remove(self); subWidgets.clear(); } @@ -74,8 +74,8 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, void Widget::PrivateData::repaint() { - if (groupWidget != nullptr) - groupWidget->repaint(); + if (parentGroupWidget != nullptr) + parentGroupWidget->repaint(); else if (topLevelWidget != nullptr) topLevelWidget->repaint(); } @@ -84,10 +84,12 @@ void Widget::PrivateData::repaint() TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) { + if (TopLevelWidget* const tlw = dynamic_cast(w)) + return tlw; if (w->pData->topLevelWidget != nullptr) return w->pData->topLevelWidget; - if (w->pData->groupWidget != nullptr) - return findTopLevelWidget(w->pData->groupWidget); + if (w->pData->parentGroupWidget != nullptr) + return findTopLevelWidget(w->pData->parentGroupWidget); return nullptr; } diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 7df7a623..0331c0d1 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -28,7 +28,7 @@ START_NAMESPACE_DGL struct Widget::PrivateData { Widget* const self; TopLevelWidget* const topLevelWidget; - Widget* const groupWidget; + Widget* const parentGroupWidget; uint id; bool needsScaling; bool visible; @@ -36,7 +36,7 @@ struct Widget::PrivateData { std::list subWidgets; PrivateData(Widget* const s, TopLevelWidget* const tlw); - PrivateData(Widget* const s, Widget* const g); + PrivateData(Widget* const s, Widget* const pgw); ~PrivateData(); // NOTE display function is different depending on build type diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index ce35e5e6..f5b913b7 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2015 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -21,7 +21,6 @@ // DGL Stuff #include "../../dgl/SubWidget.hpp" -// #include "Window.hpp" START_NAMESPACE_DGL @@ -31,6 +30,12 @@ START_NAMESPACE_DGL class ExampleColorWidget : public SubWidget, public IdleCallback { + char cur; + bool reverse; + int r, g, b; + + Rectangle bgFull, bgSmall; + public: ExampleColorWidget(TopLevelWidget* const topWidget) : SubWidget(topWidget), @@ -40,7 +45,7 @@ public: { setSize(300, 300); -// groupWidget->getApp().addIdleCallback(this); +// topWidget->getApp().addIdleCallback(this); } protected: @@ -119,12 +124,6 @@ protected: // small bg, centered 2/3 size bgSmall = Rectangle(width/6, height/6, width*2/3, height*2/3); } - - char cur; - bool reverse; - int r, g, b; - - Rectangle bgFull, bgSmall; }; // ------------------------------------------------------ From eadd3f9c4e4c0060ae9b6d4146d45d9ef71f237a Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 6 May 2021 20:10:56 +0100 Subject: [PATCH 041/159] Add files to be used for demo test Signed-off-by: falkTX --- tests/demo_res/DemoArtwork.cpp | 569 ++ tests/demo_res/DemoArtwork.hpp | 35 + tests/demo_res/ico1.png | Bin 0 -> 259 bytes tests/demo_res/ico2.png | Bin 0 -> 3462 bytes tests/demo_res/ico3.png | Bin 0 -> 415 bytes tests/demo_res/ico4.png | Bin 0 -> 1355 bytes tests/demo_res/ico5.png | Bin 0 -> 539 bytes tests/images_res/CREDITS.txt | 3 + tests/images_res/CatPics.cpp | 7083 +++++++++++++++++++++ tests/images_res/CatPics.hpp | 25 + tests/images_res/cat1.png | Bin 0 -> 121583 bytes tests/images_res/cat2.png | Bin 0 -> 70352 bytes tests/images_res/cat3.png | Bin 0 -> 64331 bytes tests/widgets/ExampleImagesWidget.hpp | 203 + tests/widgets/ExampleRectanglesWidget.hpp | 156 + tests/widgets/ExampleShapesWidget.hpp | 108 + tests/widgets/ExampleTextWidget.hpp | 70 + tests/widgets/NanoPerfWidget.hpp | 240 + 18 files changed, 8492 insertions(+) create mode 100644 tests/demo_res/DemoArtwork.cpp create mode 100644 tests/demo_res/DemoArtwork.hpp create mode 100644 tests/demo_res/ico1.png create mode 100644 tests/demo_res/ico2.png create mode 100644 tests/demo_res/ico3.png create mode 100644 tests/demo_res/ico4.png create mode 100644 tests/demo_res/ico5.png create mode 100644 tests/images_res/CREDITS.txt create mode 100644 tests/images_res/CatPics.cpp create mode 100644 tests/images_res/CatPics.hpp create mode 100644 tests/images_res/cat1.png create mode 100644 tests/images_res/cat2.png create mode 100644 tests/images_res/cat3.png create mode 100644 tests/widgets/ExampleImagesWidget.hpp create mode 100644 tests/widgets/ExampleRectanglesWidget.hpp create mode 100644 tests/widgets/ExampleShapesWidget.hpp create mode 100644 tests/widgets/ExampleTextWidget.hpp create mode 100644 tests/widgets/NanoPerfWidget.hpp diff --git a/tests/demo_res/DemoArtwork.cpp b/tests/demo_res/DemoArtwork.cpp new file mode 100644 index 00000000..6875a665 --- /dev/null +++ b/tests/demo_res/DemoArtwork.cpp @@ -0,0 +1,569 @@ +/* (Auto-generated binary data file). */ + +#include "DemoArtwork.hpp" + +static const unsigned char temp_ico1_1[] = { + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 2, 2, 73, 15, 15, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, + 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 15, 15, 77, 2, 2, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 15, 15, 77, 172, 172, 121, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, + 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, + 186, 186, 125, 186, 186, 125, 186, 186, 125, 172, 172, 121, 15, 15, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 16, 16, 77, 186, 186, 125, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, + 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 201, 201, 129, 186, 186, 125, 16, 16, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 15, 15, 77, 172, 172, 121, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, + 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, 186, 186, 125, + 172, 172, 121, 15, 15, 77, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 2, 2, 73, 15, 15, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, + 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, + 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 16, 16, 77, 15, 15, 77, 2, 2, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, + 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73, 1, 1, 73,}; +const char* DemoArtwork::ico1Data = (const char*)temp_ico1_1; + +static const unsigned char temp_ico2_2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 46, 12, 43, 64, 17, 57, 67, 19, 60, 72, 23, 65, 78, 26, 71, 84, 28, 78, 90, 31, 83, + 93, 35, 87, 83, 32, 78, 76, 47, 77, 49, 18, 46, 40, 9, 36, 38, 9, 34, 37, 9, 34, 38, 10, 35, 40, 11, 37, 42, 12, 40, 43, 16, 42, 56, 42, 62, 91, 96, 111, 117, 132, 149, 108, 124, 146, 87, 99, 120, 74, 83, 106, 59, 66, 91, 40, 39, 47, 40, 30, 37, 57, 54, 69, + 110, 110, 127, 114, 98, 111, 20, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 45, 12, 42, + 63, 16, 57, 66, 18, 59, 71, 22, 64, 78, 25, 70, 84, 28, 78, 90, 31, 83, 90, 33, 84, 85, 49, 86, 117, 118, 134, 110, 109, 124, 61, 46, 67, 37, 14, 36, 34, 7, 32, 35, 8, 32, 35, 7, 33, 37, 11, 36, 45, 24, 46, 69, 63, 81, 95, 104, 120, 111, 127, 146, 104, 121, 143, + 88, 99, 119, 77, 87, 110, 68, 74, 101, 43, 43, 54, 28, 23, 27, 54, 56, 71, 121, 123, 144, 117, 108, 120, 19, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 44, 12, 41, 61, 16, 55, 64, 18, 57, 69, 20, 62, 77, 25, 68, 85, 30, 79, 91, 33, 84, 86, 32, 79, 75, 40, 75, 80, 76, 98, 141, 154, 163, 148, 159, 168, 114, 115, 128, 80, 71, 89, 68, 54, 75, 96, 87, 104, + 117, 111, 125, 129, 128, 139, 150, 156, 166, 159, 172, 181, 148, 165, 178, 73, 90, 105, 64, 83, 99, 79, 96, 114, 106, 118, 137, 78, 86, 97, 37, 42, 50, 54, 62, 80, 116, 122, 149, 119, 112, 124, 19, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 43, 11, 39, 59, 16, 54, 63, 17, 55, 68, 20, 61, 76, 24, 67, 83, 27, 76, 87, 29, 80, 78, 25, 71, 65, 25, 60, 72, 65, 89, + 85, 100, 117, 148, 164, 169, 164, 180, 186, 165, 179, 186, 176, 188, 194, 208, 217, 221, 227, 234, 236, 235, 241, 242, 237, 244, 245, 241, 250, 252, 124, 138, 146, 31, 50, 65, 45, 69, 87, 66, 91, 109, 170, 185, 193, 212, 222, 224, 177, 187, 192, 128, 143, 155, 115, 126, 154, 116, 111, 130, 20, 17, 19, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 43, 11, 40, 60, 17, 55, 64, 20, 58, 69, 24, 63, + 74, 26, 68, 81, 29, 75, 86, 33, 79, 77, 28, 70, 60, 19, 55, 68, 57, 82, 85, 96, 117, 89, 107, 123, 133, 150, 157, 167, 185, 191, 206, 219, 223, 228, 236, 238, 237, 244, 245, 233, 241, 242, 210, 224, 229, 154, 173, 180, 52, 69, 81, 33, 53, 68, 39, 62, 80, 49, 78, 104, 95, 123, 142, + 202, 216, 220, 236, 244, 244, 233, 239, 240, 195, 206, 214, 145, 145, 161, 23, 21, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 1, 2, 44, 12, 40, 61, 16, 55, 64, 17, 56, 68, 20, 60, 71, 22, 64, 77, 25, 69, 79, 25, 71, 74, 26, 66, 69, 31, 63, 71, 52, 79, 91, 101, 121, 108, 128, 144, 112, 134, 144, 158, 177, 182, 198, 213, 217, 207, 220, 224, 204, 218, 223, 183, 201, 208, 113, 139, 152, + 47, 63, 72, 32, 49, 58, 48, 69, 85, 50, 73, 93, 46, 68, 96, 45, 76, 104, 140, 160, 169, 225, 234, 234, 240, 246, 246, 242, 247, 246, 229, 235, 236, 37, 37, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 44, 13, 41, 61, 16, 55, 63, 18, 57, 66, 20, 59, 70, 22, 62, 77, 26, 68, 82, 29, 74, 79, 29, 72, 76, 36, 72, 83, 56, 83, 117, 120, 137, 152, 168, 178, 165, 181, 188, + 180, 196, 202, 160, 181, 189, 156, 177, 186, 149, 173, 183, 167, 187, 194, 125, 147, 156, 63, 82, 93, 45, 64, 76, 70, 92, 107, 62, 83, 103, 45, 65, 90, 31, 57, 85, 103, 124, 136, 213, 225, 227, 236, 242, 242, 242, 248, 247, 255, 255, 255, 46, 48, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 43, 12, 40, 61, 17, 55, 63, 18, 56, 66, 22, 59, 70, 24, 63, 74, 24, 66, 84, 40, 78, + 95, 63, 95, 117, 100, 123, 154, 152, 165, 176, 185, 192, 183, 197, 202, 180, 194, 199, 143, 164, 172, 122, 146, 157, 118, 143, 153, 116, 143, 155, 154, 177, 185, 162, 182, 189, 133, 155, 164, 111, 133, 145, 101, 123, 134, 86, 106, 120, 64, 86, 104, 47, 68, 88, 97, 120, 132, 193, 209, 213, 222, 232, 233, + 241, 247, 246, 255, 255, 255, 46, 48, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 39, 12, 37, + 57, 16, 51, 62, 19, 56, 68, 25, 60, 78, 46, 76, 100, 78, 105, 129, 122, 142, 170, 176, 188, 187, 200, 209, 195, 212, 218, 188, 205, 212, 169, 187, 195, 141, 161, 171, 94, 116, 124, 101, 122, 131, 83, 107, 118, 98, 127, 143, 151, 176, 188, 164, 184, 194, 158, 179, 188, 145, 168, 177, 144, 164, 171, + 147, 167, 175, 97, 119, 132, 57, 72, 86, 114, 136, 146, 184, 201, 207, 208, 221, 224, 235, 242, 242, 255, 255, 255, 46, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 36, 13, 34, 53, 18, 49, 59, 24, 56, 83, 62, 89, 145, 151, 166, 178, 194, 203, 187, 207, 215, 184, 205, 214, 182, 203, 213, 180, 201, 211, 165, 187, 197, 128, 151, 162, 90, 111, 121, 53, 69, 77, 79, 101, 110, 62, 83, 92, + 95, 118, 133, 109, 138, 153, 164, 185, 196, 186, 202, 209, 168, 188, 196, 163, 182, 189, 157, 176, 184, 117, 140, 150, 67, 84, 93, 106, 129, 138, 181, 197, 204, 189, 207, 213, 222, 232, 233, 255, 255, 255, 46, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 37, 23, 39, 84, 72, 91, 115, 111, 127, 139, 147, 161, 176, 198, 209, 177, 199, 210, 173, 195, 208, 171, 194, 206, 169, 194, 206, 168, 193, 205, + 125, 154, 167, 62, 80, 87, 54, 68, 74, 44, 55, 59, 46, 58, 64, 40, 54, 60, 37, 47, 54, 82, 99, 107, 149, 167, 176, 175, 191, 200, 181, 198, 207, 181, 200, 207, 180, 198, 204, 152, 173, 181, 91, 110, 117, 89, 110, 118, 102, 126, 138, 164, 184, 191, 220, 231, 232, 250, 255, 255, 46, 47, 47, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 64, 67, 76, 166, 181, 190, 192, 211, 219, 184, 205, 215, + 168, 193, 205, 161, 187, 201, 158, 183, 198, 151, 178, 192, 145, 173, 188, 142, 171, 186, 105, 131, 143, 53, 59, 59, 21, 22, 22, 63, 75, 79, 73, 86, 90, 51, 63, 68, 27, 27, 26, 10, 10, 14, 102, 107, 108, 120, 143, 156, 125, 150, 163, 167, 189, 197, 190, 209, 215, 185, 204, 211, 174, 195, 202, + 156, 178, 187, 149, 171, 180, 168, 190, 198, 217, 229, 231, 251, 255, 255, 46, 48, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 8, 8, 134, 147, 153, 192, 211, 219, 184, 205, 214, 172, 194, 206, 151, 178, 193, 144, 172, 188, 135, 164, 181, 109, 140, 158, 85, 118, 133, 96, 130, 145, 99, 124, 136, 48, 62, 67, 57, 69, 72, 111, 124, 127, 121, 134, 136, 116, 130, 140, 64, 70, 76, 53, 55, 55, 82, 96, 103, + 82, 103, 113, 97, 121, 132, 124, 149, 160, 171, 192, 200, 183, 202, 208, 178, 197, 203, 178, 197, 203, 186, 204, 210, 197, 213, 218, 222, 233, 235, 253, 255, 255, 46, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 9, 153, 167, 172, 194, 214, 221, 177, 199, 209, 162, 186, 199, 143, 171, 188, 132, 161, 178, 102, 134, 151, 81, 110, 125, 60, 88, 101, 80, 113, 125, 101, 126, 138, 48, 62, 69, 137, 151, 155, + 136, 148, 150, 119, 132, 135, 110, 123, 130, 92, 110, 119, 76, 94, 104, 67, 86, 96, 79, 98, 107, 107, 129, 138, 136, 158, 166, 140, 164, 174, 170, 189, 194, 177, 195, 199, 187, 204, 209, 196, 212, 216, 211, 224, 226, 228, 237, 237, 251, 255, 255, 46, 46, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 9, 155, 167, 173, 194, 213, 221, 176, 196, 207, 168, 189, 201, 149, 175, 188, 120, 150, 167, 96, 126, 143, + 82, 109, 124, 67, 94, 106, 87, 118, 130, 109, 136, 148, 93, 107, 113, 97, 104, 105, 110, 121, 123, 104, 119, 124, 91, 108, 114, 55, 70, 77, 59, 77, 85, 70, 89, 97, 85, 104, 111, 101, 124, 132, 125, 146, 154, 129, 152, 160, 145, 168, 177, 164, 185, 191, 173, 191, 196, 189, 205, 209, 204, 218, 220, + 222, 232, 231, 247, 253, 252, 45, 46, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 10, 158, 171, 175, + 200, 218, 225, 182, 202, 211, 170, 191, 202, 151, 176, 190, 138, 164, 179, 114, 143, 159, 84, 113, 128, 77, 103, 118, 91, 121, 133, 132, 160, 172, 96, 117, 124, 20, 23, 24, 76, 85, 88, 157, 173, 177, 159, 176, 182, 102, 122, 128, 67, 86, 91, 66, 80, 86, 66, 83, 88, 69, 88, 95, 82, 104, 110, + 118, 141, 148, 147, 169, 175, 158, 178, 184, 161, 182, 187, 187, 204, 207, 211, 223, 224, 215, 226, 225, 236, 245, 243, 43, 45, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 10, 164, 175, 178, 208, 224, 229, 190, 208, 215, 176, 197, 206, 157, 180, 194, 131, 159, 174, 105, 136, 152, 88, 117, 133, 88, 114, 127, 90, 117, 127, 123, 152, 162, 108, 130, 137, 58, 67, 69, 90, 101, 104, 144, 159, 162, 166, 183, 186, + 163, 181, 187, 125, 145, 147, 91, 109, 111, 71, 85, 89, 68, 80, 85, 69, 84, 91, 89, 110, 115, 106, 131, 138, 140, 162, 168, 166, 186, 189, 193, 207, 210, 220, 229, 227, 217, 226, 224, 235, 243, 240, 37, 40, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 10, 166, 176, 179, 208, 224, 229, 192, 211, 217, 178, 198, 209, 141, 168, 182, 117, 147, 163, 108, 137, 154, 96, 123, 138, 83, 107, 119, 78, 104, 114, + 115, 142, 149, 115, 137, 143, 81, 95, 99, 69, 83, 87, 102, 115, 116, 130, 146, 149, 148, 165, 169, 125, 140, 142, 96, 112, 113, 79, 94, 98, 67, 85, 91, 82, 99, 104, 84, 103, 108, 100, 121, 127, 137, 159, 163, 186, 200, 201, 204, 215, 213, 217, 225, 223, 216, 224, 222, 231, 240, 237, 119, 107, 103, + 76, 93, 110, 94, 71, 60, 116, 56, 6, 111, 54, 8, 104, 50, 8, 96, 44, 7, 85, 38, 6, 80, 37, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 10, 160, 172, 176, 199, 217, 224, 189, 207, 215, 174, 195, 204, + 140, 168, 182, 121, 149, 165, 104, 133, 150, 80, 107, 122, 76, 101, 113, 92, 117, 124, 97, 124, 131, 115, 138, 144, 100, 122, 128, 97, 118, 124, 81, 96, 100, 78, 93, 96, 109, 121, 121, 102, 116, 116, 93, 109, 109, 89, 105, 107, 98, 113, 116, 102, 121, 124, 120, 138, 140, 135, 153, 153, 165, 179, 179, + 186, 198, 198, 200, 210, 208, 212, 220, 217, 214, 222, 219, 228, 235, 230, 157, 179, 197, 136, 144, 166, 183, 114, 76, 188, 93, 11, 180, 86, 7, 163, 77, 11, 154, 71, 5, 141, 63, 9, 136, 60, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 154, 166, 171, 193, 212, 218, 184, 203, 209, 161, 185, 194, 141, 168, 181, 114, 145, 161, 99, 128, 147, 89, 115, 129, 97, 120, 130, 80, 105, 113, 93, 117, 123, 103, 127, 133, 108, 127, 131, 101, 121, 126, 107, 127, 131, 101, 118, 121, 106, 122, 123, 121, 131, 132, 92, 107, 109, + 90, 105, 106, 99, 115, 115, 135, 146, 146, 155, 167, 165, 154, 167, 166, 157, 171, 169, 177, 188, 186, 189, 200, 197, 213, 220, 217, 211, 221, 217, 228, 234, 229, 178, 198, 216, 173, 176, 188, 183, 125, 100, 186, 93, 24, 167, 80, 18, 164, 77, 12, 167, 78, 10, 153, 70, 10, 148, 66, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 9, 9, 149, 167, 172, 182, 206, 215, 176, 200, 209, 169, 195, 205, 144, 175, 191, 112, 148, 169, 88, 121, 142, 91, 121, 137, 79, 110, 123, 80, 109, 119, 97, 126, 134, 92, 121, 128, 89, 116, 123, + 90, 113, 118, 99, 117, 119, 104, 119, 119, 103, 121, 123, 108, 125, 125, 107, 121, 123, 96, 114, 113, 107, 121, 122, 126, 140, 137, 130, 147, 147, 143, 161, 161, 127, 148, 148, 157, 173, 169, 191, 200, 197, 207, 216, 212, 213, 223, 218, 222, 231, 226, 201, 218, 231, 180, 197, 214, 170, 145, 143, 176, 91, 43, + 176, 88, 24, 186, 91, 14, 186, 91, 11, 167, 79, 11, 160, 73, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 13, 6, 129, 96, 89, 152, 119, 109, 153, 118, 107, 161, 123, 109, 154, 117, 104, 135, 100, 88, 127, 90, 75, + 125, 86, 69, 121, 83, 64, 126, 88, 67, 132, 94, 72, 128, 89, 67, 120, 81, 58, 120, 78, 59, 80, 80, 97, 73, 91, 119, 124, 90, 87, 125, 92, 89, 128, 98, 99, 108, 80, 79, 73, 76, 93, 64, 72, 80, 99, 69, 65, 110, 76, 74, 114, 115, 115, 145, 162, 160, 180, 192, 190, 197, 207, 203, + 217, 225, 221, 223, 231, 227, 219, 235, 245, 185, 202, 219, 180, 174, 179, 196, 120, 67, 209, 112, 27, 203, 104, 15, 190, 92, 11, 164, 77, 10, 162, 76, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 21, 2, 122, 28, 4, + 136, 36, 4, 148, 43, 4, 158, 48, 4, 165, 54, 4, 171, 58, 5, 173, 60, 5, 173, 62, 4, 173, 62, 3, 174, 63, 3, 175, 63, 3, 176, 63, 3, 179, 64, 3, 182, 64, 5, 92, 29, 14, 3, 34, 87, 15, 60, 129, 13, 59, 127, 12, 53, 119, 10, 37, 94, 3, 16, 62, 17, 4, 14, + 117, 20, 3, 102, 8, 0, 120, 93, 92, 149, 165, 163, 175, 187, 185, 186, 199, 194, 217, 224, 221, 230, 234, 231, 229, 243, 248, 205, 215, 225, 194, 161, 150, 198, 129, 88, 199, 109, 48, 184, 96, 38, 165, 81, 30, 153, 73, 18, 156, 75, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 26, 2, 130, 32, 2, 141, 39, 2, 154, 46, 2, 162, 53, 2, 168, 57, 3, 171, 61, 5, 174, 65, 6, 177, 67, 4, 180, 68, 3, 183, 70, 2, 173, 65, 3, 157, 60, 14, 182, 69, 10, 181, 71, 18, 119, 48, 53, 55, 23, 75, + 41, 22, 99, 64, 18, 83, 59, 13, 60, 48, 14, 58, 50, 14, 55, 23, 10, 52, 95, 25, 25, 87, 13, 12, 104, 85, 84, 145, 160, 158, 165, 176, 172, 170, 184, 180, 193, 203, 200, 212, 218, 216, 234, 245, 246, 226, 239, 244, 186, 169, 170, 180, 118, 89, 189, 108, 59, 195, 106, 40, 193, 98, 17, + 190, 95, 13, 184, 94, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 28, 2, 133, 36, 2, 146, 43, 2, 158, 51, 3, 166, 57, 3, 170, 63, 11, 171, 68, 36, 174, 71, 42, 181, 74, 27, 186, 75, 13, 190, 77, 6, + 153, 57, 6, 94, 50, 75, 106, 68, 106, 64, 59, 122, 40, 48, 117, 28, 38, 107, 23, 34, 104, 29, 23, 75, 21, 18, 68, 13, 20, 84, 14, 20, 79, 11, 13, 74, 6, 20, 82, 12, 11, 63, 55, 59, 77, 112, 129, 130, 134, 151, 151, 137, 155, 155, 142, 164, 165, 142, 170, 174, 193, 215, 233, + 242, 254, 255, 212, 219, 223, 133, 107, 98, 109, 63, 33, 143, 79, 29, 137, 71, 12, 120, 59, 10, 138, 71, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 31, 2, 131, 38, 2, 145, 46, 3, 158, 55, 3, 167, 62, 6, + 169, 70, 41, 177, 115, 132, 189, 150, 172, 187, 88, 79, 192, 82, 37, 195, 83, 17, 168, 67, 12, 142, 52, 28, 105, 99, 137, 38, 63, 138, 50, 46, 111, 27, 63, 137, 13, 53, 127, 18, 36, 92, 4, 21, 76, 14, 28, 81, 20, 27, 80, 32, 23, 70, 18, 18, 59, 27, 8, 41, 85, 90, 107, + 159, 174, 186, 203, 218, 223, 213, 224, 226, 194, 214, 222, 99, 156, 195, 205, 226, 245, 252, 255, 255, 239, 252, 254, 161, 177, 189, 48, 57, 65, 8, 4, 3, 8, 3, 2, 2, 0, 1, 15, 6, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 31, 0, 124, 35, 0, 143, 50, 4, 158, 58, 6, 167, 66, 14, 169, 74, 58, 181, 138, 166, 204, 202, 231, 208, 147, 163, 199, 105, 88, 200, 89, 34, 185, 79, 25, 148, 53, 14, 160, 109, 107, 16, 65, 132, 24, 56, 116, 16, 83, 161, 28, 77, 153, 68, 53, 110, 12, 31, 90, + 30, 47, 103, 21, 44, 103, 18, 39, 97, 6, 29, 84, 25, 10, 39, 166, 149, 149, 225, 239, 246, 225, 239, 249, 243, 252, 254, 252, 255, 255, 222, 234, 243, 245, 251, 254, 250, 255, 255, 242, 254, 255, 224, 240, 247, 93, 104, 112, 1, 1, 2, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 52, 55, 175, 153, 152, 88, 29, 24, 109, 43, 28, 118, 49, 30, 123, 65, 75, 137, 91, 112, 186, 184, 202, 221, 220, 228, 174, 111, 99, 179, 94, 67, 187, 95, 62, 135, 57, 42, 50, 88, 142, + 27, 105, 174, 16, 90, 169, 14, 92, 175, 13, 61, 133, 36, 45, 109, 14, 41, 107, 14, 32, 96, 5, 22, 91, 3, 12, 79, 5, 12, 76, 49, 8, 32, 198, 163, 152, 255, 255, 255, 231, 244, 250, 179, 197, 224, 190, 209, 237, 227, 239, 247, 242, 253, 254, 246, 254, 254, 244, 254, 254, 233, 246, 252, + 201, 214, 222, 59, 65, 71, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 83, 97, 138, 129, 143, 66, 32, 58, 70, 36, 69, 130, 121, 32, 127, 118, 132, 103, 86, 122, 108, 95, 135, + 183, 186, 200, 226, 225, 208, 158, 154, 168, 155, 152, 173, 96, 98, 133, 22, 107, 180, 12, 76, 140, 12, 78, 143, 9, 56, 123, 7, 56, 127, 4, 26, 83, 5, 22, 78, 4, 18, 64, 3, 10, 50, 3, 7, 35, 3, 4, 21, 9, 0, 6, 154, 153, 155, 245, 254, 255, 238, 246, 249, 102, 124, 181, + 95, 124, 220, 187, 208, 240, 232, 245, 251, 235, 249, 253, 243, 253, 254, 232, 246, 251, 211, 224, 232, 145, 156, 164, 14, 17, 19, 0, 0, 0, 9, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 73, 112, 92, 76, 114, + 72, 46, 78, 66, 42, 74, 98, 87, 69, 77, 52, 75, 91, 70, 112, 124, 142, 176, 159, 190, 210, 209, 204, 173, 159, 117, 106, 142, 75, 79, 128, 82, 95, 23, 100, 176, 12, 84, 156, 12, 82, 156, 9, 49, 107, 6, 24, 75, 4, 6, 23, 8, 12, 32, 5, 7, 24, 5, 9, 29, 4, 6, 20, + 6, 9, 28, 0, 0, 15, 127, 138, 148, 221, 236, 242, 225, 239, 244, 198, 213, 234, 121, 143, 203, 219, 236, 249, 227, 240, 245, 227, 241, 248, 233, 247, 253, 225, 239, 246, 210, 224, 232, 164, 176, 184, 51, 57, 62, 6, 7, 10, 83, 96, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 50, 87, 63, 56, 94, 47, 50, 87, 53, 50, 85, 49, 49, 89, 39, 59, 108, 81, 115, 185, 102, 148, 217, 111, 147, 207, 217, 217, 201, 179, 119, 95, 162, 83, 81, 166, 75, 55, 102, 84, 108, 39, 113, 192, 6, 46, 100, 6, 29, 76, + 8, 19, 58, 12, 24, 59, 9, 15, 42, 8, 18, 51, 8, 24, 66, 5, 14, 47, 5, 14, 44, 0, 6, 38, 104, 115, 129, 184, 202, 210, 162, 179, 190, 110, 126, 146, 148, 164, 181, 217, 231, 238, 220, 233, 239, 219, 235, 242, 221, 236, 245, 213, 229, 236, 201, 215, 222, 169, 182, 190, 127, 138, 146, + 116, 127, 135, 158, 175, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 62, 106, 74, 69, 114, 54, 24, 49, 60, 30, 50, 65, 44, 84, 27, 82, 150, 33, 98, 173, 39, 83, 150, 58, 89, 153, 57, 69, 114, 122, 90, 140, + 140, 75, 106, 130, 74, 114, 103, 87, 143, 29, 88, 162, 6, 35, 85, 7, 22, 63, 10, 25, 67, 13, 35, 87, 11, 29, 78, 11, 35, 85, 9, 35, 84, 7, 30, 80, 5, 12, 40, 0, 8, 42, 73, 84, 102, 100, 116, 126, 133, 150, 164, 112, 130, 146, 104, 121, 135, 176, 192, 200, 215, 229, 237, + 220, 235, 244, 210, 225, 232, 200, 215, 222, 191, 205, 213, 175, 186, 194, 173, 185, 193, 181, 195, 203, 170, 187, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 26, 63, 23, 19, 53, 37, 10, 44, 36, 12, 46, 30, 34, 78, + 26, 57, 114, 22, 70, 133, 22, 50, 107, 28, 51, 109, 44, 51, 105, 51, 53, 108, 61, 30, 52, 69, 23, 48, 46, 104, 174, 23, 85, 151, 6, 25, 65, 6, 20, 62, 7, 29, 77, 13, 48, 106, 10, 34, 86, 10, 37, 86, 9, 36, 83, 7, 36, 87, 3, 9, 37, 0, 3, 27, 57, 68, 80, + 102, 120, 130, 142, 160, 171, 134, 153, 165, 152, 170, 184, 185, 202, 212, 201, 217, 225, 205, 220, 228, 191, 207, 214, 182, 197, 205, 175, 188, 196, 168, 181, 189, 174, 188, 195, 178, 193, 200, 168, 185, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 17, 56, 24, 11, 42, 32, 16, 49, 33, 22, 62, 32, 33, 75, 30, 34, 79, 33, 41, 93, 44, 53, 112, 65, 61, 122, 56, 56, 106, 20, 18, 41, 62, 25, 33, 100, 60, 89, 30, 54, 109, 8, 45, 99, 7, 35, 77, 5, 20, 53, 4, 16, 47, 5, 22, 60, 5, 14, 45, + 5, 17, 46, 5, 25, 68, 5, 27, 72, 4, 14, 47, 0, 5, 31, 41, 49, 60, 72, 86, 95, 80, 95, 104, 104, 119, 128, 136, 149, 157, 134, 147, 155, 133, 146, 153, 132, 144, 150, 135, 147, 153, 126, 137, 143, 123, 133, 140, 124, 134, 140, 125, 135, 141, 123, 134, 140, 117, 130, 137, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 42, 94, 17, 51, 111, 22, 58, 120, 33, 71, 138, 40, 80, 149, 37, 76, 144, 40, 81, 151, 55, 91, 164, 62, 92, 165, 46, 84, 152, 37, 68, 125, 44, 78, 135, 44, 96, 169, 36, 93, 168, + 35, 88, 163, 31, 76, 143, 19, 54, 107, 10, 31, 72, 5, 19, 51, 3, 10, 29, 3, 9, 27, 3, 13, 41, 4, 16, 50, 3, 7, 31, 2, 5, 22, 4, 5, 9, 7, 8, 8, 7, 8, 9, 8, 9, 10, 9, 10, 11, 10, 11, 11, 11, 12, 12, 10, 11, 11, 10, 11, 11, 10, 10, 11, + 9, 10, 10, 10, 11, 11, 11, 11, 12, 10, 11, 11, 9, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 36, 93, 5, 45, 106, 13, 56, 122, 20, 71, 142, 29, 81, 155, 26, 79, 152, 30, 84, 158, 35, 90, 166, + 46, 101, 181, 36, 80, 149, 14, 60, 123, 7, 64, 129, 10, 63, 128, 22, 86, 159, 37, 109, 190, 36, 105, 185, 32, 91, 168, 26, 79, 151, 20, 66, 132, 12, 50, 107, 6, 34, 80, 4, 22, 61, 3, 15, 48, 3, 8, 34, 3, 6, 23, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 27, 78, 3, 35, 90, + 4, 40, 96, 7, 49, 109, 12, 58, 123, 16, 64, 131, 16, 68, 136, 21, 73, 143, 26, 75, 145, 11, 46, 101, 4, 52, 111, 3, 43, 99, 4, 47, 105, 3, 48, 107, 5, 54, 116, 5, 56, 118, 8, 63, 127, 10, 65, 130, 13, 72, 139, 9, 61, 126, 6, 50, 110, 5, 43, 99, 4, 36, 88, + 3, 28, 76, 3, 20, 61, 1, 6, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; +const char* DemoArtwork::ico2Data = (const char*)temp_ico2_2; + +static const unsigned char temp_ico3_3[] = { + 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 37, 23, 14, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, + 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 38, 23, 13, 13, 23, 38, 33, 55, 88, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, + 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 88, 14, 23, 37, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, + 88, 55, 33, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, 77, 130, 207, 77, 129, 206, 77, 129, 206, + 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, + 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, + 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, + 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 88, 55, 33, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, + 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, + 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, + 37, 23, 14, 39, 23, 13, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 40, 23, 12, 13, 23, 39, 33, 55, 88, 33, 55, 87, 33, 55, 87, + 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 88, 14, 23, 37, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, + 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 39, 23, 13, 12, 23, 40, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, + 30, 55, 90, 13, 23, 39, 14, 23, 37, 33, 55, 88, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 88, 14, 23, 37, 88, 55, 33, 207, 130, 77, 206, 129, 77, + 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, + 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 33, 55, 88, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, + 77, 129, 206, 77, 130, 207, 33, 55, 88, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, + 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, + 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, + 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, + 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 33, 55, 87, 77, 129, 206, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, + 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, + 77, 130, 207, 33, 55, 88, 33, 55, 88, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 37, 23, 14, 88, 55, 33, 87, 55, 33, + 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 39, 23, 13, 12, 23, 40, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, + 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 30, 55, 90, 13, 23, 39, 14, 23, 37, 33, 55, 88, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, + 33, 55, 87, 33, 55, 88, 14, 23, 37, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 37, 23, 14, 39, 23, 13, 90, 55, 30, + 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 90, 55, 30, 40, 23, 12, 13, 23, 39, 33, 55, 88, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, + 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 88, 14, 23, 37, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, + 206, 129, 77, 206, 129, 77, 207, 130, 77, 88, 55, 33, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, + 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, + 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, + 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, + 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, + 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 77, 129, 206, 33, 55, 87, 87, 55, 33, 206, 129, 77, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 87, 55, 33, 87, 55, 33, 206, 129, 77, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, + 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 204, 128, 76, 206, 129, 77, 90, 55, 30, 30, 55, 90, 77, 129, 206, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, 76, 128, 204, + 76, 128, 204, 77, 129, 206, 33, 55, 87, 88, 55, 33, 207, 130, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 88, 55, 33, 88, 55, 33, 207, 130, 77, + 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 206, 129, 77, 207, 130, 77, 90, 55, 30, 30, 55, 90, 77, 130, 207, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, + 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 129, 206, 77, 130, 207, 33, 55, 88, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, + 87, 55, 33, 87, 55, 33, 88, 55, 33, 37, 23, 14, 37, 23, 14, 88, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 87, 55, 33, 88, 55, 33, 38, 23, 13, 13, 23, 38, + 33, 55, 88, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 87, 33, 55, 88, 14, 23, 37,}; +const char* DemoArtwork::ico3Data = (const char*)temp_ico3_3; + +static const unsigned char temp_ico4_4[] = { + 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 91, 85, 76, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, + 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, + 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 78, 72, 63, 76, 70, 61, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 74, 68, 59, 74, 68, 59, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 67, 62, 55, 67, 62, 55, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 67, 61, 53, 126, 118, 106, 126, 118, 106, 67, 61, 53, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 68, 63, 56, 194, 181, 162, 194, 181, 162, 68, 63, 56, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 74, 69, 60, 140, 131, 117, 185, 172, 154, 185, 172, 154, 140, 131, 117, 74, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 66, 62, 55, 178, 166, 149, 184, 172, 154, 184, 172, 154, 178, 166, 149, 66, 62, 55, 76, 70, 61, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 65, 60, 52, 132, 124, 111, 186, 174, 156, 184, 172, 154, + 184, 172, 154, 186, 174, 156, 132, 124, 111, 65, 60, 52, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 76, 71, 63, 191, 178, 160, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 191, 178, 160, 76, 71, 63, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 131, 122, 109, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 131, 122, 109, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 59, 55, 49, 182, 171, 153, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 182, 171, 153, 59, 55, 49, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 67, 61, 53, 139, 131, 117, 186, 174, 156, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 156, 139, 131, 117, 67, 61, 53, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 80, 75, 66, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 80, 75, 66, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 58, 123, 115, 103, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 123, 115, 103, + 72, 66, 58, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 69, 60, 58, 54, 48, 189, 177, 158, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 189, 177, 158, 58, 54, 48, 76, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 69, 64, 56, 144, 135, 121, + 186, 174, 156, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 156, 144, 135, 121, 69, 64, 56, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 77, 72, 64, 179, 167, 150, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 179, 167, 150, 77, 72, 64, 76, 70, 61, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 68, 63, 55, 122, 115, 103, 185, 173, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 173, 155, 122, 115, 103, 68, 63, 55, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 63, 59, 52, 194, 181, 162, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 194, 181, 162, 63, 59, 52, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 73, 67, 59, + 143, 134, 120, 185, 173, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 173, 155, 143, 134, 120, 73, 67, 59, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 71, 66, 58, 177, 166, 148, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 155, 182, 170, 152, 170, 159, 143, 165, 154, 138, 165, 154, 138, 170, 159, 143, 182, 170, 152, 186, 174, 155, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 177, 166, 148, 71, 66, 58, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 66, 60, 52, 128, 120, 108, 186, 174, 156, 184, 172, 154, 184, 172, 154, 184, 172, 154, 190, 178, 159, 123, 116, 103, 69, 64, 56, 77, 70, 62, 72, 67, 58, + 72, 67, 58, 77, 70, 62, 69, 64, 56, 123, 116, 103, 190, 178, 159, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 156, 128, 120, 108, 66, 60, 52, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 68, 60, 193, 180, 162, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 171, 159, 143, 84, 78, 68, 66, 60, 52, 77, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 77, 70, 61, 66, 60, 52, 84, 78, 68, 171, 159, 143, 184, 172, 154, 184, 172, 154, 184, 172, 154, 193, 180, 162, 72, 68, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 136, 127, 114, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 172, 154, 67, 63, 54, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 67, 63, 54, 185, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 136, 127, 114, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 62, 58, 51, 180, 168, 151, 184, 172, 154, 184, 172, 154, 180, 169, 151, 72, 67, 59, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 76, 70, 61, 72, 67, 59, 180, 169, 151, 184, 172, 154, 184, 172, 154, 180, 168, 151, 62, 58, 51, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 66, 60, 53, 136, 127, 114, 186, 174, 156, 184, 172, 154, 185, 173, 155, 144, 134, 119, 72, 66, 57, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 144, 134, 119, 185, 173, 155, 184, 172, 154, 186, 174, 156, 136, 127, 114, 66, 60, 53, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 79, 74, 65, 188, 175, 157, 184, 172, 154, 184, 172, 154, + 191, 178, 160, 80, 74, 65, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 80, 74, 65, 191, 178, 160, 184, 172, 154, 184, 172, 154, 188, 175, 157, + 79, 74, 65, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 74, 68, 59, 126, 118, 106, 184, 172, 154, 184, 172, 154, 184, 172, 154, 191, 179, 160, 42, 39, 33, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 42, 39, 33, 191, 179, 160, 184, 172, 154, 184, 172, 154, 184, 172, 154, 126, 118, 106, 74, 68, 59, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 58, 54, 48, 186, 174, 156, 184, 172, 154, 184, 172, 154, 184, 172, 154, 159, 149, 133, 57, 53, 45, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 57, 53, 45, 159, 149, 133, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 156, 58, 54, 48, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 68, 62, 54, 142, 134, 120, 186, 174, 156, 184, 172, 154, 184, 172, 154, 184, 172, 154, 159, 149, 133, 57, 53, 45, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 57, 53, 45, 159, 149, 133, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 156, 142, 134, 120, 68, 62, 54, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 60, 79, 74, 65, 181, 169, 152, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 191, 179, 160, 42, 39, 33, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 42, 39, 33, 191, 179, 160, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 181, 169, 152, 79, 74, 65, 76, 70, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 70, 64, 56, 121, 114, 102, 185, 173, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 191, 178, 160, 80, 74, 65, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 80, 74, 65, 191, 178, 160, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 173, 155, 121, 114, 102, 70, 64, 56, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 60, 56, 49, 192, 180, 161, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 173, 155, 144, 134, 119, 72, 66, 57, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 144, 134, 119, 185, 173, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 192, 180, 161, 60, 56, 49, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 71, 66, 57, 145, 135, 121, 185, 173, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 180, 169, 151, 72, 67, 59, 76, 70, 61, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 72, 67, 59, 180, 169, 151, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 185, 173, 155, 145, 135, 121, 71, 66, 57, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 76, 70, 61, 75, 69, 61, 177, 166, 149, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 172, 154, 67, 63, 54, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 67, 63, 54, + 185, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 177, 166, 149, 75, 69, 61, 76, 70, 61, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 67, 61, 53, 124, 117, 105, 186, 174, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 171, 159, 143, 84, 78, 68, 66, 60, 52, 77, 70, 61, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 77, 70, 61, 66, 60, 52, 84, 78, 68, 171, 159, 143, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 155, 124, 117, 105, 67, 61, 53, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 68, 63, 56, 194, 181, 163, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 190, 178, 159, 123, 116, 103, 69, 64, 56, 77, 70, 62, 72, 67, 58, 72, 67, 58, 77, 70, 62, 69, 64, 56, 123, 116, 103, 190, 178, 159, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 194, 181, 163, 68, 63, 56, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 74, 69, 60, 140, 131, 117, 185, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 186, 174, 155, 182, 170, 152, 170, 159, 143, 165, 154, 138, 165, 154, 138, 170, 159, 143, 182, 170, 152, 186, 174, 155, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 185, 172, 154, 140, 131, 117, 74, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 76, 70, 61, 66, 62, 55, + 178, 166, 149, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, + 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 184, 172, 154, 178, 166, 149, 66, 62, 55, 76, 70, 61, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 65, 60, 52, 137, 129, 115, 191, 179, 160, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, + 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 189, 177, 158, 191, 179, 160, + 137, 129, 115, 65, 60, 52, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 71, 66, 57, 58, 53, 47, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, + 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, + 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 55, 51, 45, 58, 53, 47, 72, 66, 58, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 89, 83, 74, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, + 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 75, 69, 60, 72, 66, 57, + 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 91, 85, 76, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, + 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, + 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 82, 76, 67, 81, 75, 66, 93, 87, 78, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, + 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77, 92, 86, 77,}; +const char* DemoArtwork::ico4Data = (const char*)temp_ico4_4; + +static const unsigned char temp_ico5_5[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 0, 0, 0, 12, 12, 12, 148, 148, 148, 161, 161, 161, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 156, 156, 156, 155, 155, 155, 10, 10, 10, 18, 18, 18, 215, 215, 215, 229, 229, 229, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, + 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, + 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 221, 221, 221, 222, 222, 222, 19, 19, 19, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, 219, 219, 219, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, + 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 223, 223, 223, 220, 220, 220, 219, 219, 219, 219, 219, 219, 226, 226, 226, + 220, 220, 220, 218, 218, 218, 221, 221, 221, 223, 223, 223, 225, 225, 225, 218, 218, 218, 217, 217, 217, 221, 221, 221, 222, 222, 222, 219, 219, 219, 226, 226, 226, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, + 225, 225, 225, 184, 184, 184, 207, 207, 207, 209, 209, 209, 215, 215, 215, 165, 165, 165, 203, 203, 203, 224, 224, 224, 210, 210, 210, 182, 182, 182, 174, 174, 174, 222, 222, 222, 223, 223, 223, 203, 203, 203, 195, 195, 195, 213, 213, 213, 166, 166, 166, 217, 217, 217, 219, 219, 219, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 218, 218, 218, 232, 232, 232, 127, 127, 127, 164, 164, 164, 126, 126, 126, 145, 145, 145, 148, 148, 148, 141, 141, 141, 140, 140, 140, 218, 218, 218, 105, 105, 105, 137, 137, 137, 146, 146, 146, 129, 129, 129, 149, 149, 149, 152, 152, 152, + 142, 142, 142, 145, 145, 145, 214, 214, 214, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 219, 219, 219, 225, 225, 225, 187, 187, 187, 213, 213, 213, 166, 166, 166, 194, 194, 194, 182, 182, 182, 182, 182, 182, 181, 181, 181, 217, 217, 217, + 178, 178, 178, 190, 190, 190, 184, 184, 184, 176, 176, 176, 199, 199, 199, 184, 184, 184, 187, 187, 187, 192, 192, 192, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 221, 221, 221, 220, 220, 220, + 221, 221, 221, 221, 221, 221, 222, 222, 222, 220, 220, 220, 222, 222, 222, 220, 220, 220, 223, 223, 223, 221, 221, 221, 222, 222, 222, 221, 221, 221, 221, 221, 221, 222, 222, 222, 219, 219, 219, 221, 221, 221, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 215, 215, 215, 215, 215, 215, 18, 18, 18, 17, 17, 17, 208, 208, 208, 222, 222, 222, 219, 219, 219, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 214, 214, 214, 215, 215, 215, 18, 18, 18, + 17, 17, 17, 206, 206, 206, 219, 219, 219, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 212, 212, 212, 213, 213, 213, 19, 19, 19, 14, 14, 14, 177, 177, 177, 194, 194, 194, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 187, 187, 187, 187, 187, 187, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; +const char* DemoArtwork::ico5Data = (const char*)temp_ico5_5; + diff --git a/tests/demo_res/DemoArtwork.hpp b/tests/demo_res/DemoArtwork.hpp new file mode 100644 index 00000000..2a00e2fe --- /dev/null +++ b/tests/demo_res/DemoArtwork.hpp @@ -0,0 +1,35 @@ +/* (Auto-generated binary data file). */ + +#ifndef BINARY_DEMOARTWORK_HPP +#define BINARY_DEMOARTWORK_HPP + +namespace DemoArtwork +{ + extern const char* ico1Data; + const unsigned int ico1DataSize = 6912; + const unsigned int ico1Width = 48; + const unsigned int ico1Height = 48; + + extern const char* ico2Data; + const unsigned int ico2DataSize = 6912; + const unsigned int ico2Width = 48; + const unsigned int ico2Height = 48; + + extern const char* ico3Data; + const unsigned int ico3DataSize = 6912; + const unsigned int ico3Width = 48; + const unsigned int ico3Height = 48; + + extern const char* ico4Data; + const unsigned int ico4DataSize = 6912; + const unsigned int ico4Width = 48; + const unsigned int ico4Height = 48; + + extern const char* ico5Data; + const unsigned int ico5DataSize = 6912; + const unsigned int ico5Width = 48; + const unsigned int ico5Height = 48; +} + +#endif // BINARY_DEMOARTWORK_HPP + diff --git a/tests/demo_res/ico1.png b/tests/demo_res/ico1.png new file mode 100644 index 0000000000000000000000000000000000000000..d8a00bf344f4886c71fc5faa8168ab6919fd699b GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H&O4_(A8i#7&u~1LH1WS|^)C-6e*f@LNzKVTVh#;;?3;dVUbR%^Z(U1A(LA9- z@f1zn-+7khpG3YV-|qT7ne%DR=R1Ywy&sFOG%zqSv2X||I6xT-!Y^moE3Jw>8Lj#5 tYLSP_mV3edVG&>F3ApoeTxj3PFn_<=-F00SsX(_ec)I$ztaD0e0ss&wTVntK literal 0 HcmV?d00001 diff --git a/tests/demo_res/ico2.png b/tests/demo_res/ico2.png new file mode 100644 index 0000000000000000000000000000000000000000..9f22da6d62eda56f353126c5d674ec27a3933c9f GIT binary patch literal 3462 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�Zsv={a;anx z62@^DMoFAx86O-RIDB}DVDxt{zxw%Ac5UfuoB>br7;8q^(w6Ro(PlHkC=yUy>0bJH z#}%3VSKr#%X|ZMuT4~pk6`wTw8ZamgmI+d-fWvOLJ4(ka*QpE+Oqa?z*9pUZLTEqe zCvlP_8Rwa4nNmr4@7wkspe)HEW?LrrO(Zp5-l2P4tCI6vyVm8&&u%QG*0J+bjeoiF z>p1DVba0qH8I#D_RtO-GI=Unuk$LqT@DA0Fy?+kt7IN9et#8OT&&#);d6A413 zNhsF5;CkIKR_QJi+4UZ24CWexg@KYWRPbkqtX9Cby6HxjwL%qSnjk<3NiKe~m>+P) z_}mE2*~m1I?~uG>*d{e7ScZYIF}K!p%`8l`NfAccF)_yAScG8gSR{xi?ep10F6-Z3PZF&Dmd<%}2c+S$jxrEQNP4DgHgbyFKcpTRM@rC8l zV^6tx@!a9gPyQ>q-)H{y=ODj+O=#XiFX#9Dl@DK~ZdTLU*70UH?}GF1cfIZE-xm4m zQO>SfzKIgaJYB*m+YhNcSzp-Hq~Wae-S)F2TZ#h+nUpV(VG+DIp|c@I{MDb_a{|6G z4nbd)C)w_bs=SkLKc(lr=dXz&=RLxV!eXZUoEIE$<=OSAsy{ERyB!vD>zVI+^8;00 z@?PJhKPxgDDS*(dnf$81&u+0R`7mEK`RTkZzaIKCu8cLD%Fmg&#AdOWN!z4CC;469 z+oSf{U!AkoKIQ!cMoAF*c|qRJWdA<_;d=kS3|_J;{w#IT^#=wegQu&X%Q~loCIFI# BtOWo7 literal 0 HcmV?d00001 diff --git a/tests/demo_res/ico4.png b/tests/demo_res/ico4.png new file mode 100644 index 0000000000000000000000000000000000000000..79998963f89e4162527f4669b8d82675970893fd GIT binary patch literal 1355 zcmV-R1+@B!P)vW1lUPL zK~z}7&6!JY6G;%qyLujOXFMhWnb10;m-ouKtV0N^>c z=hy&nEP$wGr83!6CQB`A|96x3e+vmiW~zV?`sc4dkAsE8M4?D6i(1x1p@@)ZB@h6( zHZ@}12nmFQ5$kqsx=&Bq7m#IEGL!ibA=S$eA(_d%WmZ}Sq-k0*o#mYS*)^HYYMRzE zpi8My$FVohIF5ZOHQE9o0H~C{Cz7e1KP8f>O6hyko`jpuJ%wAdl2jvWNRnK*HQRJf zY7$6~>hZn--+Q(l-SNHwJ*poa5N9mj+h2c8ytkh>B_Emq0&}f2I+?G(Xmm1fu9Xmi zLkIF)`}&=^hP&sCUB5Hux%S}#0RSQ7j1jBpJ=;9#j1eP*>^0o>2ISbZr++|E)gX%% zRqg2?aBO;rK+JQq`C>pVCWOf5i_CKm3rKKYxH)UnJtP>LT7{dlqS5r|-UA=rzNVH%!eJr!R!8O} z9Hy4_;q7ZbD^hBuv(pC#1OQg09Pdl47j@pdS1&@L2KEny!tY+aV9fjfBJ4g|2LKd_>it6_Rqs(0b$Q{V zBuRm)5lLEJ_^2pq)sy;%Mj`t4VH58M$Zl0yZ*}DhZODN9XwJB{12tbQyMyzM^ z!G8Lzu~zyK{PAn0AE(b6n(L?_+TBRI0(p)T@4YC=TT{6}MyxwHkP++NR?14U z9Php8t>^JQ0NK>c=4ZC~pzAv6+;qU!7Go@(n|57ihXk0hskt{G#=PR~dyZ}I3_2rr z0Rw87;s5{xGGZ5YhLvO6#oPBbBDWzB0LYTub>5R>t=BEjJyBnNu5rJO2UbuWX;+Q*X;L< zPhYFAsx)1H@c3COGv+!J1LmA@&M;utp{dN+gU8P_U9Ye8#;31I7=q{_F;OU50qRcI zaYn|clIiSDf#&WWL>mIjvL*_}#rfa2l}dQ`eor7#i!s*pX#2@i?I%xZdK641|FNzx4FiQ2W1;$vZRAS6C%Vo}wu7i%Tr0G$C zP-kc7mQKDO(t3LbI`VO>JNfdu>2N3%Y8Axh3@rQc)z;S5Lc{n>{{e^BKJR0XhTH%E N002ovPDHLkV1nl&fJ^`Y literal 0 HcmV?d00001 diff --git a/tests/demo_res/ico5.png b/tests/demo_res/ico5.png new file mode 100644 index 0000000000000000000000000000000000000000..dc85b73d340df1761ae55057cbd83efafbd8982e GIT binary patch literal 539 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

*HDNGkYRZA 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 literal 0 HcmV?d00001 diff --git a/tests/images_res/cat2.png b/tests/images_res/cat2.png new file mode 100644 index 0000000000000000000000000000000000000000..c28be1ffa037aff87871009bb225b4e598646f2b GIT binary patch literal 70352 zcmXtfXH*ky*YzX>2uMi+A}S?>q9`4aB1(XObde6yn-ZjV=@20lX(Amdw}5mJsi8?P z38)ZIiZtmEKt!a!JZpX5kC|(&nKeIV&YW}h-q$`cx;hUT>95fP0KllOhS9rN_b-lo zDEQ+1B&pT+Vxe*F=APeyi*p3Xl6b^oOUeL~u-aZn)5|Mx9aXLqe-DEE1 zL9tdA{(Ion`9cYi|<-pXZu~!Qo6u01o2n(83eHsbLAOTH5zMO- z`|;Ts<)78r==OQ_`Eg#fO$9c4hD3<=^a(y)KO{%@T?@CnQS?6Gex8n3X07bkMb}luY@kxNOAwUw z0p^jWiqE8mfnxZ1K2YHvng9TbO8ha6EgHkhMx&(`qry=uMuh_CB;f+;e2{t5Ms*ky z!-M6lxsNVfZH>Jnc`khkv!}nqKd+mo>k-E|68--zj-0kzSq580{kO|%qBM3N=;Xn3 zaPY|Yg8-%Jt3Xn$tYqd@TQR_&U4}-ipp*xqm~ZSiiVwq!{RJQlvAWPOC=Lzrqm_g{ z1R7%5xt3kStQ~={japD9kd*PA7X|LyIan+y%1)&f9<3J3%M>~LWK4!H_fPf_DdVHadgE0TY}rP5vIryRO} zBKuyut|e#}FnQojH{C9M!VO0LW#aQD9=U>;b z`1a$|34)D(P)KE+YWHl&UeEDyw89BRlkey#Zsa0}eN!7-r2UXKt-Znt>FB$=+-?Ww zBkd=LNACanLjO&jpH+vR9>qz6h%GHaKK}a)FTOI@(OfDCV@Ps{`)RSAHDby6v+Zq+=D!h5c7LjNK-+BD4?Q(MA;=pO6uVr@*}Nj#2Vb8fRcLz zbm5mJ0H@;x#~=YDiUTZJ=s1#`n|6S@!vhFU7YF5(VN;&50K;V zW$s%%KY4V%zm=$vIAzn8O6VJ%eO~V}m>0VHqV_7&MWVL*CnKb^eQS$d^ldTn9QDS= zgc--8nY8{jg(ulj_Q2DtNb~dI?F0oH3Rpkmj zdBES?^5W}HN_4kD`e9dJpv9lFUGEn`nEaO3tN_29$to9D_9tD;UiPv7sZ!sf0QuqF ztX%yIj!?l4AwG6_A@T6c=*ryho%|eMNV+kd#2C(*E+U9G31%y-M|0pV*=KU&&1JMT zMv^}TItlB7B zELvU3Yk#XLTm&2NT95HW0qF5@vsAyMr!1gwp%c8Io#a8B;5SN7Ds5xV6-kxuD!1ml4m{nB&69+)y#|-Pb@IyjSxM;6KV3y4^BpM@j-90{WwC|Z#|tR(;&HenKE&!C=yj^xjGxRl!QZ#F=_g~%`{BLAAh`!`JGC^o z2yY4ElT(Xh&!b#~lK5%JV$CoMj_;tD$nRv3ZW0)2(h&(I%i{+4mBfK?juwX~G3L8B z96!0U`q5qr3+pzoi&BQDp^*Rzd@uT;m?uyZ{gPG}2d7tp05oDvFJJN^P!a$DL;3%s zzucvc1jGLuu6vty2z|g<>2|z~-IUI*cbTfvRF`=xcUSd&X=UAT=W+s`5*`)?0uuVW zL2BI_;{Dot&;7C+gU^1eq%pt}4-#+SY*w9170&D7K0g0fE&~~-oZ$8@#&?hs1>j2 z+^ivKmZC`JPOH-M-h%o%142!Pa2`h=cV-9}tt;($-Z50Fr1lCP`!raLdElS-B(7>cS*>+Fg)tr1=QqxKKlz%JNkx=d_@j&+K z$7cN`Rb*|T3>v}?;=v$eAP}6SjadFY5VDW}PyrdVytslWJDBZzY8we-3>1QM?|5|1 z)X5Wp8z2a_B zZ5y7q*=rCvV$9_CMx@~TPVf)I&`Zh~DAExEONKt5DsX97{Gi_C_wqQ=kNan^dD&m> z+EhJ_-1Nf_v)qa4qU|pt)nr%rPTbGGGg7e#7yZgH6)X9(H`NebuIzuZbIL3&EnU*r zM{;ctD!MMMXwT&`RjDQ^gashDC-3ivv`lT*J7jqdn{AlkRVp{KbVt;)>XZ4VPFITs zpQ13{>P;@cze|TdC+jJvd)=`x)_ifgzE9fP3O#l^Us=fuKHc#T%@R4@`pm0giWgiF zSZLe0YnnARwSwE=DG59Y+cR)p%c>b0`jU9Se6aYT$$Z(VO}9o2>U|ret)TrdSw1@| zFBv8e*A&Etq3DuebT}Ef7xbQu7*(N*+^WPUPUxE)C!)isVrD|Uyu?U!`Jd$;GTFDn zOmWB{ZEb$uzXkoC)_ri$F1&T<{ZEl3?h4sR`4{F9kC?oWp0@AGT-n1}VHg-l2MwZu zCB(?)e-xCsy0b~aF@c~Aa5zp)k!d~w{fmN%-~!geZiP91^7VxB(d4^RGoY0Y&=3qH z!W|{v&|%G5_bjFHI_f$k0Li0pe8OGvHhpMDf|G98*zW%Gtc@AR_6@&m?b+gPsM>M? zMa3>Fy=?!r1+KBU|N1=o2XTMhz-UR%xvXBylf0Y=KmTtIcULNi3lh395Mr+oZ zbEc*pp#WID)a41^X4dCQBoE6IQ(gbJ#d@)sV7o>?yK2Ya2^QPZOOmt@p5=O*!W60I-5eB`;XQBr?vLigLH&)0H#;)o zDl&0mUbA%;uGvvjs(OW88Lqwo&zuw%YB??TAN-=;^awclLN{&ss$qga`Vvkch@>BS z%9HkTUL*Bp?|!Ffg#4uxWbUlDEk~^u9BWWLhJk8g zGLsy&0DBM{1M3`aK89yH&x8le(oBV{(!f8INqPvtc|jl~EQx@E1OPN(RS2qrwe|%P z00)MBfB>+Uv;YbY>}+kclb1Z242J897-Mr>dTEvQ;Yc^>2B|#x@ccFxP_Gjq7_l|~jB+{`}i0rg^pbWfbCrHlC_X*#a9{y%$ zcCpix019uKlF>xaa9wc_DZ)cR2yvLWBr4-7NbWn3ajyC_+z%{gjPY6mbpwU z<2L52?@c?I6|fcNcxVyu1QKLobL+fX;7) z3#7W3I2+<8`&Pw-DH^*@iSF88>$cksv)M3i62kXCN9pVJPXD@hNIe zjNu~RjgG{Gy(ngeFZbEienU}%g6aF7`iKRu`=q^Zei&5P931wwerwRMdHR#fl%E{K zjB7*a!C89wMz(1C$pWQIxskJEi2H2$g3Ioa78gAleoUKNwV$q)Y{{4xA1?M0+O|uE zKFzu?A8$=rg|O+wN$9t=pU=J6oSN^6OTWNJr)ww36gg~Gy~UtG+=KSkYrVb!R;2%2 zzSW*OO4r-8aZau@Ox15o2M5+}XA@tk*+#l|i0!luj8y;4*3QGLn537A^pG(S(j_*( z#j%;5zy1_Qx5#f7puEo|lJp6EXWpN#sw}JCBM7}R%t_j*7ruXAGGXIijgl8XxIzd z#i!Or^V+&*6+1<@?cocytE*j=d>%XS!GAJz*|cv>7HklzQCt*moszw1LgLDhx0jd2 za;ej#(Aw1Y?ETO)i5xwwRclb|lxy2D*XvJHTUE|`dFQ_;>Nl#ZnUrTMg*Sa0W|+1d z?_o@&|Kzn!|Fm>_{jRH?`)KSg*vxtVBhl=NO`F{FsgrkGsR19&v^5(qL=1-DK0e8V zAATW^#-58_GP+UJ|7(Qid6BsTS8RLk(tmQu5v;gXBX_r)D|+2C|xo>>XSE++X~DK;;?o_as2L=e7B^ zo5+ghte-+#;K9yW*Gl{0B#~)iVqfC?&myy^Wh*$kh#2;6Z`yk%NH(x^RKi7iO2748 zYDB)vq*-0sWF1KlD)#m#V5{zWIJ&yVvWo9*9@o2=lV{-Wf||0qol;|YIdJ;_iU-L{3{A7? z5!BhW5w8t=n6p?jR(&oVLllJ1xUbmTUb2Te8uf6|BXV0DFg}6T=I6rZ`q^&5q3B(R zFba0x$RiRu!-4E%{5nC!u@RSSgmX0#Ps${6aDuwv19mQTP>Nc4NtYIgHH<6aCNR&| z(jId6-N)<8<=SnN%-J&O%P5b{D4S3pH5BIGmeqNg(TdZH#?WV; zZ0e_C&wUAmgYEkZKjKzG4-?NDTbuTGTvtm1*p>s{PFC@t_%u-1EIlyVkCso>Y*60y zE@rI0600rfcbadOnUTSRT^9V+_6O|o=<>#?Z~N8qL$~&`6TkM; zNqH{rN9Q{S7hQxPTDP2fAxK{G3fwy)g*05A7oBYhI{p3k2ZS~n;5C?)t)F~ zrCihwjoeJWrJ&%t5mTH=c?50agcFS)YO`6OAy>>pa=0ZoCM1V-0k$>a?yx*+*v3+O}1zLFp4dCRhK1XN<=Xh%%EB!pdWRNx{u`5ng9ZCaS^ z*;At6thv>!JVCuPedvOuvbMz6^;r1W=g3(+MgLRDId7jMp|b>G_0VaSR&#JAU#J*-^C^Z&czO^JZoH69lF6P2_o7zxI)e=1wS!_}B*!XT{A z&bkxfpCgjkgQ%}y)VM@kzE$mds9EGq-IbPQNzR1Zir=owxCW213ybcH#ZN1eCjx%i zz^c~Ghzw*l$!kY8k2$6P`{hWO`V(w5`1_yxM7@hNL&`S?x!{AX9wJ^=^H$;#WpQSo zwC`KnN@(AEb%V|N=!&4Kh)&Tvq-#fb$ zcP|prl|pe9NFj(s8>dYF3AQLE`7Xk%ES>oaa`xo47yo`p9q8h5fA+*s8JP@?sF^Lh zQhSZ7&WH`}1djxB>gwLCB_On)x1Dz`yK2LdZ&_T9ivu@f=^%CC;?C*QJll}w|D zEq=M7v-X2zY!%EEp1$Y6(Yqs)`{?-e?@Hd!@*iDYU9Jtmdq)oBW=`pB5}KWX2SsFs zI|w>c*1bdQ3rOD&f=9Fpu5p2Q)S^U$s8a={=s+HMQ*piTx|X`P+_%v2jk9+GzBMI9 zCf~shKv%9^VSXx@0OwIdXd;Ei5T1re2F5E8UL#2~{Xtj<`ZF@lkkNF0E9-GA6pY^y zVy93Ny4mnRO>+$tHg|iHDH4t5OX-06F@)uZp_idnx&M)a>1f<90f_))EnDuB*e93S zrFtj)k1ihT(z*;R84!!!ac7o<0{X_17B#U~QO#zRmYf~`2{B=UXQ!XLdA)`^nJr(G z&l%UL{nJBlx#Jl9Z)UThEw_t|$Rk$mgR+l@70!0LMAuHm{%+5>O!4-QA`? z-I;oog}=I+#2Qp4U&VqWWu`=XQc=#oqBCJ3k;=D=yw~0dgsiZRvH;t_(dgR$ECi!lb^aUS30{i{oDW# zV>hrX+m%T-E3yk5uPgB)E-TWBC-pPJKs+g=EKlkPcR$uY>x#GQ0eb;2{z7+T^+EzW zMF()k-~bM!c=;1##yy!2P*wIqJ2Vnjr9sal2Z03KWT~u3g`x2M6RcM{*7pI#RSb;9 z7K6Kn+}#cd{Lbvuu-4nQ)tr_*MjDG3(ELmw(HnC71+GJ2!R#$e_;J|DmPs~rr zsoA$-wsxnF5U46=F&v*W>D{Eo%$%6+)J)u4T>H06`LC?p2rm0}cu&3@JK^JH%Jrzv z<9m+I%7qv(K6&?ojsH*C6ZkS6JLl|r z=Uy9F@{DWkk3WlF>YLOt+B(4#8YvQ@ozs6?FXyfZh9x<%A9<|esCfW*rxJN4gu2bf zurBQ3&v%qL-brDBO=Owl8+Q4eInX1s&>)^fWLVKD{kM zhUM8Y{fmn=PT6Hg{_;AbJ<~&ap(nf1AsjDjGSc+qPduAsx?70x_O3pr6}QmH{OHQ! z#HM4)2`gU$3|&>Y|>n;98v6)@m)wygG&`h+P(SHVsL3$ z-blW+tf-jdHccVQTd&t%g~07`w}<+fNrcD0aC$)RhJrf0M@FBNPD&lP`p|N zdMW0mRebm=UguAFEmgU~d1B~+=e>HD*Z8LKW+5NTL5H$Ey%iPBR}Npzu!j0^Wsl3o zuNi7H5(|waz1%Cfyz2HmzzaDlBvMebR$|}iT2N+s&rD4J^!OIes`mCHk<@#rRI{1j ztf#2Vbca{3^Gf2%nvU;?UZh>!cO=1_Sq_->GFx*4osypSxPlPMeqHD0MV zbD4Sz6-kc)UJ9ZnebK&<=UI1GXkgBQ-Kl!Y@fe11qf0DQ`Ax!g-r6P0og9GayRln2 z_?!IdvW%xlb)tbD7+=2z!)IP>)TY>DtZk!_d>FpM94}$1mEM`GV)YcGL^sAhiN~(j zM`Q7w7kE{z*?hRapO`+{Blb61Swq5?3NfblIWpkY&8`=DQ^f;6SNX2emCe)Ib-Wfv8pWN+i*6VOocV_Z={jLP~ ze_jCoY6#7IdHQhquW)GIJLw%U=68>w0x;`aV7tG)2!!v20~F%j*S> z#UI|6QX>#vmCdXcgA$}Yy;wr^m2P7Buck@#M#&|B;_ArsLFve>&MY@w&-wnNbhF!l z*4{@UYS;yu*0dPYnwj2|*B-r-ZHj*XM3^6yBv!-$$8@92=uw^*1;#@9osf&`QwwZm zCtnKW^-HNH{%*a&#;Y?HdgFQ-irO*%JRhX&$d6!Ht=SFgA|jh5ZUAT$idB784W<6A z`4LJmT~F^d5()EstsO~6N|ilO=EiHgc1Ggi$tvc#7ibBChw(H|8#Hw1=y@JE2+pI4 zC5s;Hef7@AZE?_Y&F{_a3sALw*^->s(8|Y8;BYuq2CGQlUPybo5La?$Ss=S%EtF5m z%lM7@Z9B9qH6wLD@F={yg?<`TMNaM*o1uRh_vG&+443B40x*n0=@QW>y#~CPG>VB{ z^{=~0_}#*X$igHD@>_FSn6o?rj(YI&hK=wuu}cE60G}eN7HxmSY`@j;`)e`DB_|0# zGS;w;)o>46el0xjT4f+E5U4o25oYHPq%_gwXTQOge~eTEoX1~=RIZWd%niTo&r?1u z3mR+Roc<$NPYtWD0#p?F&}yA4lD~371N??dulDhb^IiQKAUC_db?Di2-<%45gAU4; zDUx2b;i*w1l7bK?OK57a#fIVDt7LBqee=6+$dX*_G|tX~7Q6JAPuW(5Pv)c%Dgp!0|+OK(3 zqiZI)XkGpi;;Cs7jk;}FdrM%$k9h6brBt}BsQ}s9(eI(V4zYWK8-_QzdKIc&=X7j3 zMBcA^SN#6^uf5VSCE#7XTw(`4yMlY<&?GIXW*ph^mM73ZER&bVL@X*x!X%E#@mDV; z!4(dvVq1p;!!Wp^v#l9Nikcy>+1})*6m{E96W)kTsG4GTnTT2}yP_HqK36{(gYVXI z*0~OVl5xVYBoGP>hb0xgpMO5o!q!;cqi0B}Y>uq1#-PY=%B#jM1LK1@z~VLVXw3pR zk%IkI|9$?a^Iu&fXqq!v`&AR6Z}2}71B}E02zQcUC@`$Xh#%)r8YcGot!R3^3$k#) z)bjz?qkBKHonEKFlmU>&s6nOLSkX8UC`kRNz#+0C39fjXkclYL)6k#;9kpH5va1m| zS$@9wBINJpTi9i@H+WC%Onj2scXR4)Mtrw8F4Odrj+7F6Nx*RF9u&f|Y-U*b!aw^S z-?NWnJ>&4t_~oc>C00NOyR43|E7R(^T_T^WQ|5>b`?vX{)`P>~4UkyE8?llqZ=eRH zf=^9<$RF<^%Fs3Q7A*DhI=|QT%K2>mR>Gt6VCB}gF@AOs7@G@n>YZu!T4U10Q2Bc} zpmnK@rMFrxP>`MWn$22oY)?ro9)du#Jp!9@G%E=+XY;#U zSErK^PL&oDkxA(q&ay0z!!}~ymSG=Pk~vA`?ivFXSWF=esGJSuQ7$NjpRd|r$)2v{ zS#YRve@3`VOnyKAYwnl5V|Twu(}s7JiV3;V=`%S7*P11eT-57J$Sy_^H5>0VU6;P_ zK?F35eOvOhDKxxmjo9gz$<)K$@yl$e-#K8rn=)fGWv9V|I%EyZtRzu8v;Hu^> zeb7r+?BDrFg~Pv7aS9@JA!G2x(s7W{(`!f}C^9G9mVBEB^<~F+3eFB4m;humv}#}Y zKbG`6aDV7U`!EiPDRGy^K$&&&z!5teqV%Dj1`~hV*m|!u$6mGyT0hSF@~!MyNW@G6 zP@`IB1otZ<(%ddLvC)9WJ>*N9c2va@goxU&vvd5WX7mR4$(MOvbrm~L@qN3~t7eLZCISfYhGLhCMk-juXY*L&YIEE8q#A8!utLR?cUZoj~!_Y;>u6k zO#IP(ZBw34>~A@gt}V7Y7SM}Z2qbSZjPxCr6rDHb)4sn)V{331wC~$G}IxrC-^J6)2=S!PU!?Xl05Yg^~c?wuRm-(9I{}B z@8^VrO<^4~F!6B*EdcEqf39#Vf?+<4FFoLHZQ(q;3fO2Cw?@)bre21_uI4e&1+9-e z!>h-vUzx|K1+9P2Qv<*gx}*;3rW4PamU{JT z8aD6SpdNEvb$jpQ)OK{S%aK5>cX0cR>rC*pYxeZ7R1-e^Dc{O>L4n7c6v}_4OF7Va z&j-;DHMW}MU8A)#8VHO4LSuc@^Fj`P2-q}cMw>#a{|w)mMt(Hqv4neRr*v6Vva<=O zDlscV5WZe;4`#o+DoUcjb#z3jfThUQ5SMN+e z7ky`+T6f+;y*oAeNKHhT+2u}SVTl`vb%4iw!okbK<=V4kxV;fjE2NV#$ni>7+g&!d z*W-zBT2C*HRs0aaOTfxFJGZih{o7U;l^BcYmGggM3arOJobEDrn9dk5YVpFp6skh3 z$sK4WoDdU$_##fIMD!rTAOxAY

pE?u;R;l#d;wx{#^@haV7@mQ627HJOCiR4dMCr?QW5sQgN zL5Sy6VOe&wtUwJmdy5<-TV}*Bm-mPr7QUDP7NX-VFxu$z$P%!ie_xc!RvA3Oi{QB; z9N!%;SW?DMjLcIbW}RScyrJ22Uy0&FEO=hqVk88$&|PkS_guqB)OljLbihYG*>5M_l9GO*&YQ`ZZ*CeGgPzTMA8_2{_}$xea!xy_6~{n3 zmmdfi7*)Y{*{q0Kh5;Y0P}47Fr0^hiJEzSJ+qaL`qAJGzt$jGzdVSWkV1kX(pueon zK@5Y#KvxZu!#7buw*U|ei^JaY^jh&-NefSJ^qV~DfwRhAh~qb2aIVcXUy0HuC$0z; zQ_YFp6u;Xg0N;2moVyAr^~CJ)3<%1cE`T1ww(vWdFhCd1#7Ca%j zhh_K8t`w^_>LTwa#BfvGjJn+Grd}eqXG8li<@@V11V6`mGe(}C^73-MGFTss(01F* zZj}#DNH@1#SAuHA#}Db*t}0ne8IWo;5lWd)rQOUp#XU6C!jpy<;hb7NS;fWMKkq+) zeUW*^IM}7-YPqaJnq19IPS$mH@@+;g0D?YsY>=@sk5z0X2N(uIA-?M+gZK-n5LCB3 z(eBB<{EO+uaKGf`Df*&M%_^WnZzLr^x-CdFT;A1@rr$90!2(`Z)B zX;_DA*NGH~#5I%DHak?Da&|ZDWv5O>g3i-iBTu&$|5dk-o$po#Ww}d?7$(DjG&4@o z4i>+s@93+_V)KRjhqo{>Jaw|Pa4CG;YgU+puOqK6!=;ZPq1Z<+Lo83+F3DffJ!m?5 z7xMn@75zwEA0U@}`FkdKujAsw6LL-F9&dTJQ|;{EMZLzuP!d%CHgpqV$uJQGa#7QX zlx}>xTn?Vqy$zA#UbR3p4g1O5H`HQQQS6UEEc&z6h1k4TQ! z_1EuR=k)svzrXM$&|aqab#%0hDO36_|7@s2YJcv@_Egv zdh5=mVdH)lm4?ohW}FsvG)z1yUR1h<+D{gsoLOr6OA z$31x%fzTqSQHP`P95*4hj&R6Rx?gHa{Vg+_5jgfvnFNGPaX()dwnq(XSg8T!-T%U&_>-U28HUn;HR_=eL}XSJo4u%~TCC*!k9-}(3~KO^7j zD11=H&7fd(JoKElXy8uy?GAIFJ91ZbVpTX-^=$XH9*AFpOUclq#P^omS$L4>(MdBM z+N^hDNi5nF>?7A9+pPaCxWAY8{)^=698gnC*3>0_sX4{cx^c2tg^}~<3%T5=kbfk% zp9&9t7G>x`{rnoHNOB{4t_>GPx2Lke&i}rwnQq{dmUvH=Gd?+7?Ss*lo|T4pExT>yo9kLisMvgbwgku$(6UT>zNPoD z;HDsseNpCZgqqgC%-+ZM>-|DD`I0+mQJi8^Qbld64*{B8QH2Mj@ zOFow59gD-{=;hg7QC=muwoh%}y^ek)ezr_FI6fUFsbKhm4k)?Hv2%V66Acr`(F9sL z^P%7VehP0NF()$5p8q`#c&j0D;CF!GLqXk--AB5O8yfgmHm0)826vbIz5F&DpHbA* z`SvXaUgCQ_C{aud&8Zz719X|}%36B?o5r`j(L8rLGQ6{HzB5Zp>vhvYV;+@y?R0da z*C}O6o^pAUALwBQ&KF))M+hGe#ML-54IGA0vP^?bUV3fj5{sGBE^|kL`QB42^P%k( zZA3K$0rn*8OTn29O?BDHVh!yKws~>_S;eKl)cO8exH%dd8w>H!h*z$D@e8M>cE8m$ ze$5Vwig6)mY(TBrILME~LBB@fWu`YC80Dmn75GPH$s9S@e^yI}i&#*m`xkK7gnlji z{$AVer~<_06b;2>b4)J#)#Qq%ZMrcAyohW$o4Kx^F3&rG0YW_h1~RW&K-eJGXo&a1 zmxW%Y4rb}DrJD`4ap$3@Etk2r3ZC8M_G>8q(v%%umTgw9u0>0DnaAf0S+Std|8A$@0dSZs)cN2XB1%>2PaP1$#rb!8s>}?6w z%4Bj!ugY7UaXLnee-h#n+T8K3}K2H=8avwaDz5R;)CtEK1GMcBwP#W1rX{ONegl zU+c)cDLUlmHk+{WIOA+Sn)~O22j(tDMjP3EeW{(I?LjB=(V-@TLab+pR%b6G--%dS zS(K#u8XJp_j1W%AUy2+|nWEFlA0{cjeO)%SKcIViLwhe;6_;})CGP<|jLKHnra*s_ z-{1Ulv%)R9jSzg@Wn8v$oL0808Uqn(#5z_KI>BD&u?eb>`BbhOiGYdg0d9=Z(uU)o zGv9B#pnb->UUoV2VzQ$*l}P0Lr3XQXQ#HVyuLIW+rPtusc}iu$bsvXW`A)9MJ(L#X zvk{%tNlClO6?H;@`U>x#0c z-}%8Kra7g*0W?&@lSICF{AKebj&NL0GOWyuTH>j_(L>%+txH8p&pB94ZhrDCYz<-; zRJ$EeDR-ylXZBGzgy~llWHW&Yw^|JS^2Kzn`iKb;cD=5^b1#(l6V#> zX%{o_)|)8}q@VPMap}ow$p^u8eRDkqe`1qWFshX1khi@lLuK4tT-1tfOZy&JQ;lNH6xY4`$DI6GM;YkGDrwMlVbsi`fl`cgvo^ zUS4u5E)GS7nihB_n16{tg*P_OJ7Izzk0$P6nFD%ni7m6|z2luq%>&DBBet)_) zF7&QW1Lou{$|vO@V2=}MFyKd}TMvN&1a*ciM2ViqNmwY{Bx3;TEKJlQDk@d>80afB zX1b#RloO^Hh9guhzqE^}P0cWB}JNF>E%6F(1wur%)RHLYEO^sopc z+Cn~DJSFTq2b=5_1QUnZbpXKD*_rNJ#13eu2Z)pr5q7NIR&W&b=R<*nA_?(aO=2vC z((MXBwKm;(24&{o(V(IjFshrVt15Q4SoQH}_6$&RQpQafLM$#r7KyQ?#YoyKJzHW% z`D2u%u}DNU{#RAlv$^mdj-+Ictf|G6r@V@)&*~SA@;<@U1Iu7fSm=JPA3Acir*e^m z9KQ(2s}XZ-V|i$71ymuBu|mgUm2{`ORM@-ha_*nVz-c zaVUDeZxwnTJ?mn$F?;s+)Rho=K+)C+kI4@7Co@a?`IR*jn&22etLh2amDByYOojWw zQxxAJnWEGTY8CUkvF4fQ&W45-)zW+Nbu#+GWk$y#{6fpOraL=Rhd7iLkbLaNmVMM} zN%`YSoz}6PKz^M&)LIoGCg8r90sYE>VTTWdXH2(_$0vxZ3aHMNU1HI+bwb$TO)h7K zy;SF5+%)vKi*^JQz1{UIfz_)1)D>z|Jhd)}S~lWxquOW!@#f|dg6hAHy*pBv*J9+d zuxE}uP#)Za6uYnMhWrq?cwMu7-2yg&i6+WsKumaNHl(Vy^O<>W*iGqoOkprJ)`!;^ z#~we9?@4~sb2GC$0xZ=1RXvP44Ei+&AqkV!^Fh0VvHh&JC|ae8%07EEVNOe=*rZ%( z(-qQ>DPluDpPe!TcpPo~+^jV&8b8aaMfP)rhI@CyI;bNz^T`#HJ!t5coHRjIxXtgj z4L;R!xj(ms2eksS(ml$G8rSC!cP?hNezs;bv8ja3o}b-7O}hAno&DXqhcPy!WsjO) zTKZw&Cp}TtJmX6KQyqFZ6cT#bPNQ^#@D^9R_q{S|Z`a!=m_RJ)+Pk<@C*)P?Q)l)@ zkI-h7_A>I5u6}KH#KuRVQq6#YU=5C6hf@uDSbD+YjHt69xqQ=2V;{?;4p0U(rd1>i z2ldIU#6m$@kGnt1V#J)QAjpM@Uw2u+Ncew&>o5H55P&t$!W^Zafr&M%Nv&h!w)4oG zp0(-@HNdX!RaMuVb<>28HdA|~R!^g?GdJRSz$yj;2SIi_l4h z+U+IXv<$XQ99dY{UR6#fBs0V9G#u|ou9U! z&mJ@!(AQ6Gdk*^@Fc*zgX`=muy(D!?*uSlLclL7QE65pNrZem_mtmdzTU-7QUc#70 zMzUwx_VNewkI70mH|G`ywYRHj!4OYlun^^jAqxOeZ3e=uQys5I$*_R|0Z8 zG&@C2Zwtm9rK{>B#CfgLVJI}Ub@!?R4Uer7+xyEI9Sl%jglG2CPsl4GL#)cHdeWpg zcNtQX!TYi(-HnYqSFX9za#GyXE2po~n6{gw1N zM2^YJoI=n&>O}$f6e|wyIVPRyKQ2_ypEl22dNJSp_;%Ix6&z{$`g_t$PBcZal*JcVf1<2D5jreNut&(>c++M8OBj}9;FHVu9a z(OtE&6?dJb{Xq2Ml?v~# z-ZQ3BJtCMb_j7{fz(^_xMp;*vH?2&nHw;r`&qVkBFd9-&zd>kH$=p?X>Dhwm=al*2 zP%ae*18Xn_p$gHUCpaT$_fm`oRxsunUKSiwJn05+2zdW z0$NC*vEUv(a|}Q$l|tg?5OwpH(24X9C$dL z@`R~Ky4vM|pa*}-eeCkEf_$GpVJpopMTJ>g_lxI)qSS!yZt)aCW)bVl#y5D3Nu1~H z)N(LO21>FS#0>q~ii{0ZvEQLL1I?84q!@bn>3~F)R3;2k(>AeT{1fzy)w-|r9+t7Q z6S2dicY2b{LS@rE zB{ND3caX^3_2Jsc%4tZuPyk^@Y&1GCFXZsBD^cMr}de#v7uS=mBt5J&GYX0;4 zghL64oCyi_@siYjy?im?`q%BkP}SbtbU!zN_{}Fb8~)X|LBG~U%AhtND??DU2P<`u zT8OpSS628ll*12w?^0E@CDCI{R}*gc<&~YMetD0Exc&{R;?lH2$Eugg2zZN@IRq!# z$4U33{}vxG65y1stwwjvJ3cdsfpZ3Q4+BYMqXTYg^I;s7b%g;FO*({(3N5G_cRvhy zvj7$L8lVS0RX#+fmnvIGNH8iZjM!D~FL}T9Qt4=mQ!A_+)AI^<>+|wF6#oBS0EfXd z?`{tsHl(g{ry0xF=N{k4+=-`!*8S828Mkaf;;5%pYO&_Y3yD`Mzy@bi)vkkOl*!Mt2TGS{kHF=?0M+A&ia> zJ-WMlq_k`UL~@kU4I&^V&%QrA|G{3n@B6yW^Ei*=edYtZm;yXJaV2$Xjf78;_9z9 zP8zii8=siRR_6@VK)y|fcRzf0FC=_}GrJ!g+TeZIXWP_9mPnVLH&-qho3juF-~>CVp)z8Bsm2Y6V7BH#Tx0F@;Rsc7q}iB$;C8fKg=^o zSfhYbV-eZA_V~qFl19FD#EB=2rCU5j&SlmSvxT_dP3H(>7zp_Y)jWjcvFv=jSxd>S zus)*g#uX^(ojeuYv5X-B`L{(%lwh`l`|lu4e8N|CTpZN6cn+?+GUt$ugQ1K>lxNtDFTXk^LX_VRl;hUns%d&-b+n_a zE)z2wMfg_?%xz>2g}6?)J?r##f=18gj_V)r-%&NKPBm+dDeE%W{I!4F!4MD>IIfTu2ZfL>nNYTqWrjtQq!71 z?OkJB}@A4L3JR7@w*0EF?tmGo?wNO}wcHaE6$Nzc*QX!zHGCFZ8toftOqZ6=JJ;?G%uYR2FHR*fl=0b-CGV{^)8RB-9ta8DGo7GZ&vxuzkm5qi~Rh@bGiH3iX@ijk?xj#Cmm?h!y&XkCN#6& z7t$5_iE(O|4jZyiT{9PJymJu~4!P;rt?)~^9)du*%7J=>zi)=3lT+c>HP#}v8JIio z;I5Bna`j>J;-X`;s@Poq?77S3_`|N;koe?P_7Dv&#LZM!xfFo@f zxMpYD;n@=^1_E)d)dGt1=R;k$lNuG+1s`(Lwc?O-Hl6!leT!v=_^j9OIq($# zDb(`sQ;Bn@bKVd``Ou7vz`2KC_%ly`V;j_!K8K_lU=oG$KNyG1dd`negM^oV{uFX#*UcYk)mOIc9_E5EtA zy`9YDkNo@h>NvB(QuGjs$@NMjxyp;4l{fQT@F2_=>I{{t+P2u)6~4A2AgvY+worLv z@9f>O`2&YTq(ci@aPl*}URkW>azDuD`r*Wh@eKj2;;l_=mG^89V#w*elVe+R&CGPi z9w#9c07NzLNix$^7g}?vq7JHK40m=_0+4=hUV+G;yeg`5;Ur4f`HYo;{kd*{2xK<@ ztrbnuC6eeSBlzFNX#aUfGD? zSzO1A-^zx~QHz(WLU19tFt!!5=b5Z*uD9Y~JHcIpH|7ArOg>$!6TKdupGw$IIdiB{ zG>M}h2rY%UH@G9wrGc&Fb@I;`vdu;p>%MwngT-*2ca!<9JF@?S#X9h07qJ@)DO6@> zH&+F?u75Jg!+)iTPynm2ZopPY2xmyGG<(1<3JxwWv$Di-t$zWPpj^aRUs#n%)Q@y? zDq>8!+42&p1lutjfBT2Cs}HODL8ySPHOsezr~Qwb zyxUj*WQRM^uGz-m#Hp#KL-BF9es>tFE#clLj7GJ-9(r>huy<>!)^A~;CqTj`8~V_0 z+K@toh@KP3n_`Qsn6gwu-J#KfyEU*w(;7JW&^Mwh^h1EOcE!c#7Ot+yv}#kX3%@r1 z>75p~rwfXVIo7E*c9#{q4Td7atf2N0Q(7a%OaU-Ji4?%Jfo&MGgmx-eu#-oUP`2OU zi&LuL`cy)~fj}lR0EsD{$D~Lm!|hznmv1st9I86^1@y0CyLOYdEKmk_(2(gk`N28VOU z4-1yEz>DK!Cibg`oG-Oc_n)vod8?k*WoU=5VlG&rl{I?7HCkHE{xwW*p(}^VoLw7; z#Z-u`i?d^02Z(ei+cFey*P;Ji9h0%>$NeB9+YB+nt}Sb=0&|(%(wNQ5&+oi&mny+x zvR3e}`!YM)%L>Fc=j;-<_?h&XyE1Z1c+`3ucsDJpKL|$Vsfh1th?} zPT$kt(q8ZOCW~8k`KUUg6Xrm`(cb=K1RX9%X>Au`juQ{wgU;O>2fu?x!8cylhJS1e zr&qN1_j|67JsF@li}I@Fm@77&sc{C?ZNyqkkeQ=*s4ziWUmSl0MSolzG5lThA?^U# zRmfXm;NZHu=eb1bLrWxVp4c@>iHF1UyOTq%xwBiM?oWD931NJ-;QWf`;@YB`A%uC>&7#do*Es` z5de68{=yw2Y=k@$_>9hUBkL(Gx96fyL8O5pL?F>ROv<@c6G57RAHY4Ed#kW2fy=3& znyGS(J*@+1{f$jsXsCfBuYhCaY5qz@0H@rGaqL{2S6yj~N&ABbM-iVY>Bj@NZ~1sN z$?Tt%Y=!iI`(@(4#%v`sO2M{+-qvq&X{hCqz{TD=z?O^aT`rXf3y%PMp>rLXi_M0* zfe(d!S8?-ht*J9tq(QY;_K0ue*Ro{CIgP@$jCO5i1TJZzXiO?2Fd-zb44B#>6YgLY z6ioB?w~f7G&zU3t7jbhswXY4|T1wNfqpjPX6thYvP+@CrrgdlS#*Z5u-(ETOTF9)e zmQ2siPF`5wBk$e_hu&TNyxF$zWw0RUud!B!njdxdu|&}bCRSV^k*ki3*9%4#&ZJLl zb5WbC$E$+c7dUqgII-pKM=W*Zl-F=0HP;~1W&S`6bkqAL3)}0 z#Og3nu4KK}S$GbPhKwgT0gM4O156G!Dju9z0t1jOHxe~L9UjvPleUpgvM_J1rB{9t z-0U{IhGwH*5b#-cy&Z{*RBKaUE_J{jfX!$xNun|amk77)S0p&9QU$Oz!e3(h zr%hH%3K$rQyxviB`?%b7K_msuzX{OJsb_I=0|G6L34t}z0KZ_JVssXGhH0{XalD_SgRYQ69H>JB*cxfdh9 zyG?;sR@aNd*nk%fEuc33|A`>T{ZGJ@%*{}f8BgAt2q%4IqM-NX~KVcT~3gCqm5#R~L#?R|@q|M@MD-$sd95p=<%nWezBli`1 zLty4^sOppdO_z&y^Obi>e+X|u{(dIy-?Oiww@*SO%0b?I7Ee{Jg$pkjvCh&!py$>~ zxf)2mBnNbdRe5918Nx`U&q`TPGJT`N=>*aTVl_^uBsYxq< zu9Jiwui8U2hWV^_A5OjLjxG8aBJn2p3L_Q^IRRezteR%t2bv($dnS^#xNE zHE7t}XU%c*b+N>xoYl2ez6*99PHNgaPdVlLlNA)Z=fYE)Q=xbM@|HU$ znn}^Sv!C38(SF`-7Ik_@^Dy6oTgQAwH3daf7t1XS4Xbyy%4CiIk4HNa9=OK z=v;PDf)0!4Ybk^WnrBFita+7Zv3}rG%xJf#T2VK_Zjvf9=#T&4l{^l{j?@ZwGbDKu zVc_GOE+(FqisbQQ0#FTpVwZ< zh>i%TOCl0TgiyoKa(pK)&|`P24w*I@=B7LLCSD7^mVH6+zP>e=`G-c+-}fi0Xr$XD z>ASCE{QSYR>!H{4Om6P=3x~*m2e(%ze*VG74!r3R5Qp|vH%tSjw=kFLY=PR<K@y+8w$H%Bt|p9}SSq)K#krn3!xvTzutGVyJgNEg9bVgi#FxOs2z0+P3 z(1B0CO=6!^WM+oYsc}8uUG!g<4XyVjVvN32DfT$}CjCU-tf73!n{Y5khX+feHwT!o znVoH;wkezwAV#`&|5R1yH7lh;_R9=H!g3lwtG-w|P=q#RV?!SQ72wY|=UQG1NT}r~ zDp_b`&oCuW9$<7$#1)p{MkC&78MSS=d~dPLnP4mL0s>5%WphP}*TR8{`El?})40#9 zHluvg7pk`9vH}oo15~>9Hzyw&+URY|f~!!8GVd<~xn#|@pxuT0FjyDtlT-t@>Ei2o zMbqK)~2A%N}do;42KqNH57L$u+NHFWA&Kui4|5+cWIBb64Gy(mC5eTo(XDe7j zZ;R+_k$+b2PMO!Gb4(ZfF>)W;ecnFI!ysZe)9GG zLRh7~J8R7_wfX7a?eT@M*4XAxk54y$&fT3P6K(Uy%q~)e#Casd;yx2v(}^xnB;6g0 z02{=diDp*x*2R*Ht;o6j9HF3qx(mU#8m?G2y!JVxeTM}FeXBCEkqK+V@d@K%d?D_P-AAnsNdweq!5$ z+1Avd$>T-I9rG33sQt)2vJC=Yu-^}w*Zx=tK1m?ZOKCxreqA~$&Bl^sm=19`w7M#o z1ZiA73BmlgJGVRkPU=~rfJ3kjcQH5;CUzQ|Q4n%#j=aG%ym^zP|(k}S?MOiU%s5N){$(6<51CO zd0B(JI#~5z-?i`R?S+kTLF0GzOauX?X&D%Fkj7@r^I>Y;g%N8^8d~#^?aDlrPPe-Y z-cr4}U)shBq>2SURovPBM9h{0S1lyLhV{&wwNznYweUGtNAq)ZMg_Rr%Rlh`Z-_2WmK=y(7UkAzjwEDTBNXkh1yPhI-L>2vbq@_JP|aUpiY_Mbl$}o?zSa znkxG_GvUmyg_Q5qRrGw6mHoX9JaOQFP}843DqoOBWj-094+H!l!=mnia1@)8B>yz4 z!yaah2NP;}EyewU63&^wYL1j2#n z*!AoSy%4<&Ce~HMYV~BiXa*DU^IKvRj+@)d&GWza#}^g5yYAH(T;E(znkk^EcVW3A z0syAQSS(E`YWOad`fV^}i+jwcUeip|nx_u)x|m*zg}geJoX1C;(IUSfBfzU}Ho(Q- zK^3%%(7-+0zSzBV(5o)W@XYq>NV>Q&cBa3zXv=w>Dz0GOT*Ei{<4JDY4#qQ)4s^0$ zaE^VImCj374^C0Lt$I&zGBuMzmafxy_kczNt2MYG_BmP*mVHUdfu3EGEvT)Q>g2m@ zdT&!3-Sz0dd z->#|FzgD1-q9~7 zd5J_~R6L}EoTe?|*RMP-n6Kc+6~3%oF!BOpw#zT*OB@^0{U%%h)4sec_HykYwGPCF z*FWp#^D~!oDO`5&FYDq_Uvm+yI;@>v^ldZL^ZkW%s8g5ila>p9@RMa;37#v%wBj`j zUqPewF!JclbAi4d;ICbZepONourh#QchlPcaicsS-?&&R&7KIf^r$`$8#!pVzcw$) zMmqJmbyW15uW}oCPi}iHf$U$w=gE%p1w2`cY za>lG9gNCJQU#yNcyk1o>zz+;ps{;?$0+)mRu6>>BBzEZ{qHpq=c|$4Qe{g3CrU-0s zuLF(IJ+5o3y`4S29R*jU$^`1byiU3;Vt*u>*vs7ke~ZU*3+XAt=H1dA6T(}rqiiy! z0pk&g02?loIoThVL6lZ1@|+YI0NfOwmmYS{OX2|4Wjbf7{|ZN5;H%<)EF{3eUSK8% zw8X^EW*LBeRV9xm1!A^Rg@7h#Oi9*CUkh#A2}o`UAx4P?_At4KnQ##e>K~NglMTkI zJ~yjjHjb>0R3SKzsSXPdXQ2GCYs&NpY{{a};iAJTta(N%pKhXPl5#jtNV)K_z#Erv zKqE0%2rOUE>#AoTjlDgduXzn}RC8V6PfA?$G$3p89;UVxhPa6eQ4(`j{jlG|yfGpDU0p$4>#kyl zhsQg99q1!}G)S`?JF;!9S~Eak+Gq@CjL`;@4lm#hiZ@@Fav(XiArs#gc6D1Q^b7Qz z>oA8l9ZJ5t09`%QzVNek$(OnNw5&Z8f)Nt}E{~QbE3~`Yt+(1w>+|KF{|_$`jd^uk zcgJ3e(N596+0C5FZ`F)A@;iKHBbfdWl35N$;P2#7y-WFuZ^MvFCGhTNY@JXg;2o`W zRtiPxe`)cm@?hBktl^)O?|6%t&Njg`P8%98e0amF?d0*;U(@Fj)FBzM@a#etgA5G> zyb`cjUjboslqNaJxo<(kHi{5L&#Lz3N2k^`4Y9*uzKmGqH*f!ZF41e}(|Mybv?-pk z3f|0$E+(Y|(vebfsz1aPFia`Z!bM{b&2LM!8fzYh!fia&x|?nl8yu zh(*t*eMY%Y9=97O=Uz>@=|f$;O3{-?^D8|Wva-)KEdootjMCP*nMp1FzROG!>UV@v{R0o=zCQ) zM92!o+o@9Cl$&SYw{X=b345&e+9pi5bE5`tLXV0@Dix^$io z9JIhfR%o$dHV8ydPY0qEDl7zC{xTG@!q>s~0Y^a!!JxT;v@nwwyJicQ=4fPQ+(5nS zdp+2wV=#P;qQuq$aof`J-yOxN%4tSic6Q7Qu6J|A*QhZw;R@jbWV9!s*qSb<-S=p* z>)2sc#PyqF$LcN6i@~5RtrhJ?ZI@G?R<5Qi`-g?*&wwM_OgJLC&Y4xo8#P9E*M53; zv%kN3<3&UqEVrPWd#x?|uk!F#_uK7lwd;G{C*hf?<{swg$^&7tSvS^3T7HY5v$gA6 zMC0S`|9b%zowQ(Rn6TzlexK^BkFRepI*GgdHeq&l6X{?pKUVQ3$s+K$O}ppj>Vw=f zczx*++JRxrF$WvIXUnyHbrsd)40G@fJS;ZCvw1SRS8}2Cnz_=>fdW@^^CIm+{GCx= zJvnfR;5j7CARPuM`*H52mQ9Gzk2SR)rH`{)7sZc$pr(Fj;Pn2#cu4eJxIcH)7Aav7 zorHieaVdQ%9oxL136{S6C2Ar_#F@WAY>Xrnt~tdgCKjlAU}3hqxw-Q(Y>Ho`qFF3k zu*qGTwjg~lK8v!o8j%$Tr5Ds?dyO3n1t`A`%#YR`bVSwWY$XuAQFu40`Xx^Z=PP?P zG+ab)hk7Je-Ae$68^Z94>GOleD4~riVQp+75m{R!cF}9XlbMKU%~=H8O}0Rt%j8=l zi=J8n#ai?Se#@xQJgtjBe@hY+)?ONEHwyT+3K})dPQc-`Ob+yio1=Y|Nw|v-{Q6VU zqF1kOou*J_7y?ou_pC6eCF}{PCuecaAk@IhP#M4lF8vIA`DgWL>&T@`8e8b}YmX%& za}s^%?7$rLP+M^9CpD#kk_-L)$s69z({#Gr7pNr+o4P)1i#_Q$G+7r8`t$F4{k^57 z>pYs4pHJ>{$mP8s<`P=s!D0=+o4JsU{BM4By`n;!zwex2J>>Y({r;;_$oYQ-k3DNP z9cGl?F0hBr4{z>{>s{e=UqAQCO3GpnKe+AMjCXCNk|ClCixXQe5W{!YmE^pZy($=x zmDtQc?lpk$i0vg3-wI4_hJYuKgc0crdOlyH17xS^3XYBb1`nv&5!$_yXt{n7uIPY0 z{6ewb)DX*{O=B1(?Vk_3kxtNKRwtxZL@ZeZdh{%7WgbpY39m8pSuZX*AcTf#-{){r z=}ZcYGB95#djPB0CV9%8Xht}!a4Q`K(%ozv3UOn#z+pvD2#35?5s2k%e0+zH_>+QO zgbfr+SCP|)yo`6Xm|+im4i4z; zlW_T|_$4YO9HJ1Mvta(!DOXTLL8%6Z+vDiN#m&t5M3ASCkI%Aj!gUxRE{*Q%(3=Kx z3o`+BXuVca#Y5+x{6R%@f|#?9n|DBqxaqMQCWg_#XW7T&n9OC_-?Qj)%@T69^^j!g zcBL0SSRSToU9OL7C!9=LV6C<+puRo%e-l+Vth967g z00f@Vv1M{`{6}HY4AEfhvCZ^m)2H!Dk;kLaPiVycqOq#2A2==xc%nk_6o5ZYKSL@R z=^{U_I$&iQxM#QMM_t*P(%|0Vy14)sBn^;acQm;=m^k6#(stw4`EkHo1*u(boN8tE zdbcAP&2Befqe~L`El^&+?3T{(9s3I$J^6PnI7_L--aeelX~2PIC%?Tc{kmnj#S%)k zlp!1!7hHc>sA;Z{-eV)x+nQw9Ehp9AttksD6Z#_t%fF@CU!7y5#D5gupGO#*WM<_MJNL2U068Z=K+)8L7&##NwxT22MAes>Y5r zn4H8LeIYO7KytR>zT=&Xk>7ty4{x`Bee9F=8kS$_aLpNAbu;pc0iCp?bH5i`s%32S zLL0rMwrXf;wOQmsP6JOxgIl`HBk1Jc4xRK~%YMG@9kYs!io?K@{s1|xTZkHeuutjC ztXZDQb+3jV*qy)q68Z;b`H;;Y{I3m$bZP48>bqH)`gau4!LK~uaY#FrFZjKarQFl3 zVPP%sw6}^T-LwCCP0COAw}4jE;$NL|eT@%sW!aLal&a5-Y$BcN&9QMZ1iLoqHPp{# z(;O95)c*?ydFMwh4hxg^Im$nQwCGMtq!0Zjk;j(z86?#ts3Bq}-B^^DClQ2HhBdvx zjh*>`w?qBx4eZC~-(h(bCfO=rCYSk};P!FSHP@H)V+^?<@gKvgxTq$EMbTZ zZs0SLFhJHjKUtc(097hIzX|!ph-xevK?C(qe<_?e%eTzA!*R)pDi8oP^6tUH7^u#6)xwAzGEZb6h3AQufumb-MYuOV% z=G>0k)%Kd8Q5;WH*McEG=c?e^jH$eML6D-1cfX*3M``__!E#3HdK45tx$WfZvoh?^ zC!URTD01An!25`o_>6LaJsLwrxUz;SZT>J&#k5HysI`P(-5P znEz=kwW1kM=8&8B_0JNF3j?bv2Gn`>=ls99^LI;6{c_b$x=#*%t7*bQ9B^mT9O9I7 zUQA4g)iELX?aJf{$F&SJhyj(0!epy1<*6govHFi4J6pDCTvU3#imZhait?mQC(~cG zS4s-OE`l)*)8MVw%CnGh8hcSnyCX8)!E#>@vbX?fWPBnc9tEv4oXK8F6>7IpFRGgs z6NaRFg~O^-?<%jTg80pS8(#{L*C$n02utINslFh_-y2sD<%JgK;HA@Pwy;NpWiuWy z2(o$;azppXm{P)1SPOf(L*+GTkb`0G)HF`Ew!}vlg?TBD{oV)S-LJW{gLKAPOvP2z z7;$o*1Q;k`QzK2(=F#Z)LP-1NQ~x{bO!aChEJW_N$mm93_`fEsaOgo#>;M@2ZV*6t z=Qn@-;1oceGcV;Ia;-pR>++f11=e0#wbPKDS5$BK8mFz84|mE9gUmwDwzOOx^D-8E zOsT1$t$10pdg}*Y2hGAyAI_p#?kh=zPjgC2PJRxx9ElWtqhGW;3fj+(g^on|=;$vND4+1Oa zC%=9bAAW$mu*KiyYiJ9Y{^3X!`f{LR@FeR8X1b9j=C>i;b`ntu)=X zu)cn0jxkrP$|VE?Ude(wDeM|h^P~v9w$eF>PnyONNVX++h&7g1CIN)ygvUW3`JErp zpKzsQj&CHZzGhi3Yj>c7;Jkr9U+5aXDqm}|b7q*gb5fPZ-a4m24US9b+X+x{f~4)V z=}8*rPh?WEM91+o)F@e>D`HbsJsd=6(dZaJ>F8?xZk5UL>xrpwm8kT})SOfV=hWh9 zME?2zI}SBB@ScETy#4B6Q@a)n4s-4AUe5R&U0ggWkR7|Jn7x>*Y}Emd!BfMJk;ijx z{Yw;RYesc0E=|20-F$EvV{JzI!zX|YHnZEWOA@wA*A7Wc$sj2s*DnU+*VaOXC%LZk zKYi_2e=IqMKpEV)Eck&U>+@nhqFaw$Mn&uZ_)ynb0r-tgQS~A0u&C0=|kMyYusAWzK@DlY~|6m z0d1j82Ol|UX^X3{mP6>4-LObY9X{>Fo?He)b&)>;1mg(+Bsn>(0Abx|JHW`OXG68A52Bjbq|FYG0}tR4D@^33 zf`VMkvnfja0YRPpGM50;1gt8%(*a5!priE=Di4%|4|z5XZ*xuAKAW17acsaF2v7BZ zeiZ-_ZJ)w8v4(BPebldj@M=ULBHb)LYG1Ky2n54hbre<}BXiTk3;P6P4YuUk+I{oO$qs0LJ5AkwHDV0m1Gue`IoE1?PsJmHDQQl5IS6l< zo*mb}(N|Dp!bI;(Y3p}{m7G0sv(dwl^9&d z{9>u+d3WEZ86Vem|2HjH$UFCkAKJmcI++;x;V2gV6S1C&y$(3Zb(Q7AKa!AZf+>r> zzXZ6SPEo%b9_spUCJS!dAEO_)e|Ie4N{!%_FyMd&2r|(NT1ojb!IWtHMR)i5UJOai z&CLyZ_V3|NyFgk(RtBwIf~B&)vQ}%P!LA$2lay5M8LvqashftKA9Xi%fHXdwIONwDo8O$5{ z`YvwTKdRO-sSh+>ie0?}?8JLLfETtKT+uxx#KM6^N;Y<^@3$}_lqj`Ck>fp449FO< zAB_4a=ZMWSzSq-Ca1m}2Gl(F}^QPw%5Xz<++Q6S1sC3dOOqAD9rYD_|wtaFjj+?4{ zsP?q5D7=A@6^O?=`;ABcKecZnG&8yiNl_}*_#5+wZPmPVSK%h86z7{Z=pNU#_>= zcRXJ&tlEdJsXrwq*Hj>ZFf?z6^`62P@%>xY1v)*$+0vu-Z!ipkgffU75rQ!5J2}mK zL^seIYY_09=Fc!QbS^gqM~i+5n2=k=VFx%PE%e2CN=%2D>EZ{>K`Q#$1qTV>dkTj)d6-c-RwW_eiDf-e=jd7^C+;$BCe}yKIR6*S}}~ol1xN^MPH2h>5m{wy*N-epuLS zcvxNtIYC8~EFmrb#T9g3j6dY{KVI$!_{G0veJ$O$8d~i>X5HP{$vPHSGI#M|in;In zwx@t&W@4W!d$l+4apdBICK{U!D0n^+7k6_ z-I+u4(slFHpeo>5H-e0k54+j$&fd=ZW%qyc>yLr`S10}&@_uV1_DuMFXK~sa z)oQ;M{<}yMSE)+25jy3iaUy*g2KFa90>5lrH1?zmImq#<3ca_SQht)g4ra9wZs|5c zsjPX+l{K2F8+Cmh9Wvh1q>&+27eRq+9$Pd>P;uCgL^cQ|Bul8xQBCt17Hhg7Y^q$? zwFBn3EUC@*>9o_8LGhwUAq``K!Qhb6CI>?u2f#&SG%RnyiPrptH6ye&*bp|Nhe&QTz4je%ic0(@i!%op<#){rRlYnUKID>M@j_u>4<-#0T@-79?oFn7 zd3mdAYgZ3E&il)Jw~f*g%mvb9YhoD8Jq2Sfg{pxiesZX199yDEo?)j!4UP!0-P12OVi0|dGs90wt`;a%;Lq@N8un2NdXum6+usvZKk&V+HJo0GlSV!i%pjpSH54@v@{Itx~H*s;;45cqhxz}fF&!$ax!-2Ll8;=hxd z6Bg5^{=Z#+*Lw>>w`u!&q~$1J4c&`NHX%OOdHrYVlPve29{V37+zpVB7)EK!;?&a8 zKfizd8lqSWeMk^P(@{wvHvL*c&TbFyR!{!^;jCyiV=l`z027O7;^1q#ltM4+vcI$& z==or6Sak~lJQA}Go)hYpg)RxvsL0>n8ui~UJRYp)gz;vsTuYxa;0eyvJL()S#l>MH z>>hKyxB4nc>Rz1BMKqHJ|GVanRh7pdAJ7_P0|Hqg>^5LvxG<=TZgpP}=u-$!l4X{k zPk1fqIw=6j(zIVGdl&G8br%iP13lN}u=zkuO(aeyu_Fo}uRl)=%k6?}5vq<54ye7G z;QZAJRYm>x_9<7PQ!22wk}>%j*Jc1klJfGy-ZPc~im2RRT-t96h8k)c`u2>RnHp-C z4QXR$YVk+5dP6)CnV5B))hEiastHz&-ic5p=vwqfFxi?^y(&>YztIbPD0>;_`P#0T zeo;YN*On(;=A``urnMYTOs;TTbn8&Is(4&CTn*SIZCC)GN5n4Bei5N#b?g*Sr!O9n}V7Sr5GPe%ubdn#X)mr;i&j7D{77 zM431_*M11yiSIQg+AAaXrcjqtwCL1<_C|vJswH5dV^(-O4PI{lD<>sqw5ecV`C%h#VaW`_RxpH+kp@sE&)g8QA&OUwIR+s(c+y$_NJc^7XI_-&xM zWsD+h0!za2Vhi2t8(#bJN>qykzfp@t6dQCBbgYVcb%umCI$dI_oNsx{IdiC^As)b9>)%mI(_^O#)n4a|D9#HC_*4!(+w%6GgsA6;K8#vZsxs&`=O( zl>%TFzEz%Eq*hbpVkf$Y)NQl_zRl1eRgizpL<)Et<2irag?$o&dMNg z-hXZMk5{1bx0#KI!GJEO5y}wbV{=64aiw}mRvtkmK8+1Z(FXll19_7sTnyd`z#1Gb zzR=etYQdMJM>A5;?RYrsbbfdB1vfzbVNi{u!HkG2DWBx2yk?yX22*5J?9{3=CrqmQ zCf8;h6WFE;;$1(t651N-Pz^nb!Ih5}aQ;v)eKT|YeR6EKfmdqnHLytBn+ht@htJ8x2{gEnoNLat`*Bf;4>-&iLU6kTe z%DGHv?!+Xt{k4;$C(7EPiom2tt zu+I6Q*-SbvyD@c%-mAw`qtMOATV(%T&Es;3yIec}Ll=MW_U=hvKu5oSx3ncHcXqD$ zuzRO&LCd~{AVn(aWXxoeFWLFPYDtmxG`V*!*Yyh>HpeSEoJs`+50Ao=2o4o6ID0g# zNp(5&BB>8Es84BDdd5p}cYKMQn-q2(UH#11K6K)+;{LmHYEzT~=DFy&raff%TeiYH z|H^SijhT}oz&O)0&hh1}VR-`CGfkMV%lCh#@Prn&~#2i)PG>3Ixk<4mgm_yDR2HH%nS1{4j<1Axpw zqaPl>THamvUtx~tOIGi0v=6U`Q0STRrFsMHEMITSS5clH(n6`|%)bd|;k zdg3N_=q6gDBBxmY(FAD{gT+0?!~XVClD&%gEY}L5tJ`;T+kdWJK-u|kU#z?ToaNzV z2g^V0q|nm>R8pe9*igl~bnvZ9X4t8W@V zSeydS4-ijS!>-41xg^T#9ksNL?yN~uEc^YbJs7X0xAzY|U;Mfm`FHE2_BNJMsE`UA z38hh01O-s=RXgXpi-Wy96}4sA1qG;@2q9rr;j!OJQ#E7LYo9x!3YDKPyw#OCV>2r1 z728D+_^y4?GTT(N^I|OVpyu@3W0w2ZRP`g78ar!`vns9FPzm2JS)Nr{R1Yu!B#jyT zK5<{^d{=%5doeoAF2B&uzKnE${J2VRx6zl64C$hS7paoVKApov-UYeRQeW$*&)qB{ zGT&G)mZh;_vyu)8klUIgbzZjkp-e`G2d1g1c|QChL?lx|!@cFrPS=P>c-TIHA7g_- z{_a7ylcD?T54R1ENsc^87E(@0iLH$xN7?t+yqCd$Ex~Af&xS-UM*Z3ZI3v_G)o5@-v_b;k&KzIs|a%F*{HBU25L>e0R$o_<^2iw zl)!%+Zdm;kqpi5rP#u~tl~2}8cpbuuavQ!n^agxB4+;)8{~bc-=EViTZ+oB6AUcym zO2`Y4_-*c$cf%Z9I$c7d%FInmHeL()_^wH}l~a>yo3hS$twgKRhUTC7hqXP@iALr1 z8h1p;w9kmx$PHbDCgn^Ehu_hF8fs!dqpdP6SNZveD(ODmR=!vG>uLDl{k@Mr^KAW4 z0k4<+g4d^PXSdSeePRN;I4v~^9up&9HWa6IgEpVJd{2`vwaGkGSFZvt93$s47Uwo; zbZ7pTuYU-szz>d>$TUIY7*BNVOA5>uXP^tNH5y8e^Q{uQg7JcKRXu@ecix69PK8j=_(ZZ?+-jv z+r8%acMvn#N4hKPb#9x5HrzjX?jgJ01<~cs+{@g4GGAC7g-h$53oNBwZN@Ypjgqo2 zA|-3YDoyvkCWtLSvQ<67_*ji}fN&gumlHSCP1QF4yD%Awas2jw`vMR9FU0Qi7I<)D zFuofA;>-Thf7c=BnU9zu`k=K(f+b(0>x_gBX7MccI%`gtQhWnBFHNN005LmTqXJ0y zPv1As<@(@_6AO`!f=}rmeC-r&6IZjc?o@LS+~*THc{bGJu_| zDQ0!^Zz|N}``KOp`Jr~`*CeL z=-%` z_V&PFndmf3&L1R-a01%uaj^ghGbqvBmI)iA@3FD|e0&?0JcwBQdOV^jr&M&dFUCF? z!Y!WQ&{?J%1WyypU4OtmU*y>>xc}DwP3QiclBpp4Fo97QAl|8s`1-o?gG@?|g6K0K zA|9(Gn-vScLQmx@9mBTZf3^Qd(|HB5!M<%gF;g`Xo2rqPqNv%TwivY=LWP*6R@ELg zt4i!q)Lu1X@7lbmt<>JLqEE&owY)l&QH2y2|+Yo>_P(-Tu30Z6!%R~1Brkyo#C zbW$_Zl`Q`k+Vbh-Cp=t6%=z;>5g!*S;bvF1@2(4otdS)5K`F;U?l*i=Wfjo~s zN?xR~XvvDI!U-8ERktdP?C$PRPu0JR$t#+h!P`suTf)ruHmdV@l6l|Dy=N+xjNN-K z2dLAO0jcfjxcU>Wbj@<|nA zSz*Zt(=fhIhQMT*6SDP9M~{1m!kDZk$;9DeY5*7+MIjm11ZI9wW4P|L22@p61z8Yt zvaJSLH?)u+*B-6M%<8Ou^LEFg!vT?sRdHhym5Zf&73psHBA0D6ITYSjKVDd5GoCn>rTe=*fQj5x5(Z^(2Yd z{6TR|M;*(CrTxhxqYK>*Vz9!?`{tfNhb2-o5uYyVj^j&e@`{0+zx8Z#QV1+b>Im(C zAi9T-RYDag^@ot!7x%*soaf%2@0~iguT&7WtO##0hZ=mCnWxKczW#1*Zn%GK_9jG* zi?M0evqYTF4=;scCcmvhn&ZFbKVZQtMr+|@IdR`xJ4gZ!~D2V(bGD`2QsfAtNPtZsz1z2IQ zYwJyvZotu-qS1qqJ4)!VUt zTpQjO+}zS8?sm?x{9IPnA$^T70oa_AI(s|Io|D-OW{fIyC)trSG|p27aI|?-V-}-8 zX@s@o&wB~1F!9F>T?R}a@}P7h2oqu=I>o>XuAXey$j0{P_g3YYXe}t%45R91#To~f zspQylWJ$8V(tJZM9KY?g-eQ^Sw)`{)4fz0$H_k#C{Carb?qZq@NIDYC9Fw;I(s(Hb zu(O(@?l~Aur;K>19d7?Kzeo6&@)0V_KHLwNmvy>(`bbFs-{9!YBc^kL9robeOI>-! zKhv`mYlyBp7Br12^M9BEcNO_Yv z^KUtrWC1vfNkBFBXkqPAd)9tuHgd7iQUYBVk-Z$`a$CH5q2<(Cxwm~yIg~3X0Geyvm9%;h)&80|J2aoL|jTPOax<)k14KC8=kl#z;9( zI}3+8GS{ImJ#3s7Rh)3bC-nZn+|3^|=Zev2$oK~l&gl(yn%WwqvT%No3Lq8D91#q4 zQLND87lgug0#3U92;-P7hw#}LeaK*;9k(h;_{x!DJ-)bVO5UC3W;1d7(d|v$>~+qK zMOV(h^_;+_%V@o(o##aH`6P2KdLAFh*jOJPk9neI&!aBdWRXJK0l1Hktd3)01ZZJb z?rZz>9jkJm$TbvQM&uTrAfJVd`&C)68$Yi5k()$oCBRovJowxQe*89^teGA`hkz~N z&ZHjUWa+S(5vFJ=C@U{8#guGuTG5`bPvvPc>kqNCao^$R9#pv>q)znCKHps#P<~-Z zsr(W3$hUpsyZEycCvo_8#&tG z6+gOZ_rv*b7h~Q~62+C8qD(0_T|AFOze{Rwep}>vnayJ=FwrOdxjJ)DLXRSyEAgGQ z@GqYHy9^OV4PTR4qvEwyIboFK%BMz=>k)_l(i=14*QT{s_W{;TG991_NYW?)YkdC&cmDu06*!|H!fzjCKHLu2w1o2{ChjK^ehtZZNh=;8+-9qr>3%Y^Alz+S5JtYhLLrG%!@N?o^zPgtYLdg3n zr4AO#F*drpi`i{lBWZ~r7#SKHx@Vp#+4)!Sz__8`_}3`=Kf{R5rt#IweG~IPDlx0F z^UiD{dBe*5ECvW*K=i;;U*+C=+_JOx^d|yyAz(i~9`J1TnFR*3*OI#PJ2TwXW2`Ag zR)ot6l#O;9vNhB=TJct%qufN+iD|*>DWsfJasw%Y^)xCm7DKQ=F`_P+9?_hDeVDNZ zH@3=m7YahmsV}(L z7h;B9OFYiSG{AAd|K1oq?y;5ocCog1HbIlp(?9I?HV>-fY5Wa$qEIpop3C-7BS{%65Iz?mtgpRT73LP-sbfiK>e;M$TS%cy&MD*j%q3!+N5u z8I;((jRaqFf8PlJDA+n&Qd@}vR?k(*db^yYxLP$)srG^nMNnRa!~qd%Bg2tQ=xOyH zplfD62(PxvniF(h{9R6n;4S8(;dsW?;p6Ik{f`)EglMs(pWb_Z=ejUEnZ>6&qNj7uJ+;7uqsO9R1&m&i(m%G5~GWJn28?;A-g>0_|bNsC- zmoF0ue!nhZq2L0rG6YiCNjwx6dHCRUF!NUoWlwzk)q$;%s90fAHh_BBMJ|WB_<;!5 zpLG1>BcuM!E!pMO<*MeLq+;kFRea$8t_yxNB(SV}^*sUnENRPr)p6I-y0N^pM6}Mm z8?A{@f>pbULI1n(*}s(gei#^3<j$FKQqz=YNjbnG9tjB=LJbD6Z!Ilk zK=Fq>L<(3KrjcWT^K4lr%dsN&>3v;WuLWG8VFK&b#HU<`&{qV?mgJcnS-WZ99RLRX z;6E=~UQXuRTyHN++IzHE`|K~iY-*+d30Xv2gu%P00AUIiwy^?LhkL7G1`X;<{&PXE zZH$i-35ko?0)vR7p~={LtXhfr(Q(X`X9C~7Tt7VYYGvs-KX~`=%k7nX6bNFk>Hg3` z4vxgP6wQ5V8<9KXq=?-W|4L&gCLKq0>v-`w7C;4K|5zkvg#NyG_%&x~CGh8gm0atl zly|GP!A@0W&$d14*S8$`i-YT?jw5B@+5y=&VtkH#7e^F;m?@_v4qp>oSjJYqr@^&p@BC*d^tu~b>_#7oX1HO->bjX#t`agjWuM)7+- zi`}lX?Ie>6mt6}@sxL0az{gx=41vHu>|mY$e^7H3G9ra)(-HlHU+l)geHx(j(_ zl@hHJ*7k{sAM-uy2sSX@et`(!_(*VT68VCRm=n1NAAAh#>iTA@tb&`UR_9T%Sctwy z-C==H60buh+~;TJDox}27Hdk*56<@mDxe;pWlLR1>RHHSRp!V)!Z~8{X!bmgS55Hpcj;lJ=8>gu8f3DFZ1 z=;r&N0Rdt^Ve$BF&A*qQ9ufwVIZoNPQG(uZOLw};%6B^bixBVrv&O4vC`PBGd zg&WD79R5~gYRwOm3i?oO(9qF!vKpaWWB9t8!-K-hVRMTwWG-2!L=_@tik(elk%kB%}3}{=-xC z`%b|XaIKvt{zshs)N$uDo6~gA!zQCWaTYrvd*fM-n+aXFG34p)?@_g&Kb}f5GE3$t z^LwvGUvP(y8zmfFs=5PJA>2xz9jtP{FWGN;5)pG+k-vT6&DYvQeH{32=$xkWBda^p zcVo+xffu``H%B*>y0M;m$nzY>MPJi&J?#KF}LMS}0|{YiA7>I!~s2%#GJ z$N(S}@3CF)4s%FRpqe1ic|)G^%lUe})+;r0{@x2SMf9HdNaJ+hz@S@!`%wdJavQ6- zsiTa(YsUYs53T>KeArsvjzRy!=L8mGBJE!`&zXDxih>{5fl!n}KG=C6(uEr2Xr?F; zx|6AFrS~$KM+Yy&X)P#mmi4AeaE?0nZcUcPS&6R2xev|Tj@P5pUrJQg3Gc8v$TdA7 z)anB`Iw1n!fignee|C=}WQxL~LSiNnG7cLTA@~rh(vZa*nrTeK^^p!0`=(J%Yv786 zzB!Yg;BKHNIxh_n!uZz2ax_@0M{>Wis~3!z{#0FA76I5e-2G0<;ZI_qbX+5r%>10h zW965pC+Z8fbn#HHu)HiCesy1RRy_npX%TiK^6Ey6Y^!wu;QQ@%>zoFkPE~9l=$eG@ zS$}tXwi)zy`XH|3{9_rGAZ5cZ8gT>=KYgaa&Wa3~Ii>)TR&YH%LzmQBY*BL(C*56$ z1?d4O?8xl@=9WYv?0vg@?)fiyO8i zdhIY{Y7a4cwL0!Xq-kyVjeX+k8w*a zl?{ftYui@7!pj}UQuV2Kjf&H-#W$^ibJ5nxyP5h3D_ySoYJkFrr`UuC({rA7VGNFf z_GY9`Y~&t!>(0`;GACppWDLE%vH}^|ekohL-ssEK5GVeNv626nW1pCdUP)5A?wGax z-FnOs*l_O|Y9B?JyAhyZl^hbHrIWswU&>O_csnwH27DzaB?Wq&eCItrT-=NOb+xyC zG5bR$@apJEaqHFO^73`m;v!QqLCqu|bhiK7DCo~e?CJ%(^E*zG2W^sdEW{)LqNp&2 z++YcTK7h%I50Q4)YgGm9JDxRFYEV6~3c<$vz(Oncd<2Q8@PuzR4(BX?vGsXz@v7%2 z)A7!VZZOt&%-F3oPIV2}EZQ;Myt*9F@%(Kklf19vf37vFpVqWhK17%z&^7v{Fr9`} zE{Nt}J`qL!f9gf4DKgH_GF-osS)m<3NQ829>u5^*T@ip3z-2#P4^~j&$GSWNgka$z zc&x8#kA&cG?UX5HSAVVpRc^b>;bq4ZZ7X#`YIRa=O2Z61O6;BESjNG3u#Moadfc<; z1Z1#+8ytgQNiBbcvK4a~5rVxai!4e4G&i_Vn9U?xS55uAq%al8gN8u_Y5VUM_t|*G zQ+p4~e43j^K3%N9Y(cRXl+i{%vXKd!zx50<-G@EAxO_MY3^+nw0O_VTGRq1>9wiS= zeZZGo{72|6%K~p%X&|FZvmK|`|6~3{T|c?q>rcmfcU(5yo>c|;pAu{k0`d1Uu;zv1 zKqx4eRnqB^pcR0=+DfpSQkGEi{ugPLJ58S;%}JgDlx2PZ(%YQ8hg8sawTf_ewcrC?^)=dW_cVGDPelD3(E{a8bFH=+Evwdq_*RCvA&Wrc1t)uSVIO@nI@5 zSS!`RIe`wa#`Xo%O-=--!v$!hWzvc~(3yPLAjC(bEI(R@zF14~d>@_D7bqr)-6mkS zMAjo8F@K*BDMn%^xl%TiobJ*~D8D9wVkK-W3?MHL!W0{XuwUKf&e~H&Yl;{M-DDq; zDu@`$%bCvw-T>Iz-p~KE>^TLsFZ`skTiv`6bi7EDE&mzA)k1W68rRXBa_S)+yIC}q zZ_%-5&^sg0z`Q1^8!LO^0uJZCb>De@ zsD{^3Nnqi73OxU`9%Cg6?y{hMWiVn1 zCV#yv@jfTamA=N+J@8x59LM%*(B~&g-a2 z6+e9avzZftP=II*1>2%$jf@0kxC$G#;0~k4nRxY==*k=!nz3-9`lGV-nc&>~dD;@g zvg(jWU*N(3oGECfIoEdLxe^9GgxUG1ZZ9%=fOTxihDWgo;Fk^kRNY3MEQL9?AG#AnIfM>@+z|MU)Zrq=BB3^DA6)&P;JI z25pZ!Xv1H!6 zFyQWYwzfyXSs#S_-aEazmrRv&&9SQg%d1;9c!c@i=Rf<>!7TJ7!jJ?8h{zHrEfJ;y zKr#Sgr-aCYA9w&_+J@qyR78eLer~*m3~C8A>2i)w%`q~qd?tGKN((93bh*|F{^4O& zqzXfPDo3is&l8)z4ZHTmSiOtk#Zk}W?kja{iTmwU`8(g3{iK~QdojFDRz0&mQhsM) znlfTg>g8U`3OR?>&c--ZR*6MjDDkW9!mWMl0YUb=FO_*401B=;g?rM%b%h2x_H)tt z$asCh`PTTK3$--b_ui#C@UbaHC!o-%+0&kbC|whU@zYVdQi+N^K8}`-H@X%`#|pSg zGPqB?Dk36t(NIvh(DbY{<6&tEKS1%C6;?Rv zhq(J4tC-o@r1GW##JhmAd&M;K~zPB@WfVP_(JbVSm?rWaEZ!5 zI#CN_^pxIzVe}z+sg2-WygfW&vc5nf>&Is_m2&3HzsuL}N1^_E@FQo7Om)YRwliT#!^>ERd>PWqHDp-HPKm zg1e{^XSEL)aRpR6Gb_zP#JHN*90p72Rjs^4&e)hE$|Fm_QYY*X4V};}D<>8dx{}X0 z9-1#^?W4Wwx5rHKHsKgB-KWU?8SgnN^4?vb)a4InqMO^uN}WbetzZ=&C4^ER33K2} zgS62oFukNeHkVsCsoJGkS3OF37D!1yP61Dl&G*Bmrl?KfHpr~Mr0t#7MS?sa7^K*8 zdD!WHMB_x`i4r8^)vng;_!`As$Nvl+)71gN4Iro_mX+^a({GLBN6(BnWZHj!T|IaT zN*~SutzVr>n!A++IxgJMYP`K7x+&}YYmEIElJ3C&DQ!VC6a1xKdfUdV9Us%Q9Qsx$;b>ukbY=oqPzjSZkFetO9tV=YHN% zAth8K3!o(oH+FLUm4*o7A>~T=cE>{hfma3rlTl`DHrcZrP&yB2e{%-xaziGd6Ab#m z=y}+@FW;Sb#ADV!-++L})9)H>BS*rP(6_b>@j!mlM!~;2r}PS<>S()o1t6?-*`2Q} z%7j`L3e}fpXQqsJtzGyU0WtVt=1|cP$_)GEFQdXSZZN5pP1WGL!U?d-h$YDl23k#O ze{GQW5G)0QT>L-M{4HVs*47YM4k}4q)IM^>uKt9v)bvrjagh0RM$Fk{AWI?+s=9Kn z6Sj*BH~a5e3j;_ENy-P<7WJ<9k#icgE>BNWQ^uFXePE$3{*Aia^BX={{aJhz)8ySr zd^^u^+og5C4sB3_ybB3in@Mu07_JrnX02`Cy37_E?#kR+>3DZwW-TN7=kxy19^o)> z=K1C~J}J6ZW~;GM5&L3$C0MDIMfX+8Pc#g(P_KkBEE9MAcpjL=WEB~prUhI2XVB{U z+EzbGq%p4}Mf(1E{;qOKq59a$b5g(86b5eo5mRSC;6v;<6(G`fi7L8CrQ}{ZJj%y$ zXB?A&;$QoD|A2}|j_W-MfY(;Z7Be(rOVE>d>%~7+Sn++}hUP88Zgx!kQhA;AqgULy zY51a?|9_C1hz9FP0wO_p8ks<^HA-oN7@iAF>Xyb?U^0W^S- zuK`J)?555l(vXp1}{e8X)k9e>3u z@Wq|C2R#R6p2pPYG~Q3l_s_ivpDP{yc@9<~1dH5W@T`rZElzpAwGwCGf!g*o&ZOb; zA^o%B9@WSV<-y7VLWR6s6^eLP&h|+j{+hasiBoj2#3syZ4fuHgA<=A?UJUMN(Au5$ zv=D<0h{B5G_3C>n!n3}<3q-OianKwnYS}e|n|nYfjNPo+s&8)La>HMgBh#3vG)5Z? z0UjfU{0}(YwLgJ#HKf=p7~#`N5QT-1r&VaT-vxyL03`R`ANOmM#dYfxJ*si=av$w? zK(6UGp|>{#>PRS02wWJ5Xe)r$XH`m=sOb}~q4xwVH=-nh2tB{~=UrE9LN&)#9~++gv*k8}9;HZjU$uak~yi4lnjjSH#5r zUkgy%PS~C^A;Q30nbS%k*;bUWwnda|z-7SW_9#7;iu3bCj+0KSVGZwtOvyPFPiuq-Gj<}v`76d$v*+FRK zB5+5g*B9Hyl+53d1a~|QU}{tAYAE13dz)m7Eij8$vzz#VPOMcQ-O~W53;0@cq>GS= z5<7E@?%z)kQPIS^rw2ctH3An(7OoN69&r6Oe?h^o)aFG=Axg)u|6-iR3n zh(pDuu)r`|rZo>AXWF zc(zEPbv7@|w!9kHYU00oDBX*=&1-*_64`r*USn-5}$GW%Aq zT@?_pD>lp9y3?}A*Ld4LeS2QYu))T7Xj2-$d^+mKbZqW{^Y?ytYkYgt_aiVGklQ%# z{5{ZR#9=0eIHbl}iDK;Wt3OABpYL&cjI{1Ks=BS9Q0C;gdq3H%_N^$nPI^=erUY8| zQ1Y>xwu_!UhYqu+*VB{5y4itUp}EqdpWTJRd_3LhS}Jkjl?0SOhPI_L?SStU+O>{l zI2~rQ6uokR$tU!Hzc#8O3X>}L?nFeiyDyJxzU7PT>fO&gSA;FVUhEgp|EKCHKuMBV zQxVLH46c;20E$AnF4SVyn3k+_NBJv$NNfQqBkjq`6o6>1AI{8;y4fH2p4!sevLX{ZG+iU$gD`3&PJ`ARr+|%T+Nsjv6q^vB{LREJoRgLy zBL;-%r7QL4inmAw6VX>Yq_As#O=cpNL#_abqygbwOkV4bu#Sqhc-FKDun?Z2#NdPB z`oW@j?8mf7QQD9S$1y?*!w~Fj5G%v-7qrqt8Ch3R>gb$Poc!%Y-01#AUGdm+25oLX z>_~K40R_hx_29i)s+O_Tbr1GJiz>hEP!Mstzi%BH}ZXSHsvg@`_wKk1?fol)nr2?)86k14EANNZsP!iO{I>j;89YuZ z?{y7#n>_`okBQWzC&R}_q;m9m`Z?1bxr?3_OJmVlGZZ9y;?H2Y+xZROzzGk`lyK|# zc+x--hAr*dN9H~+sj7R9)junJj%J%n_>i2WL>XGI2D`Na{!)<^BI8*^O6qcxXeKPm^&J(#uqZxowgoYp-V@JisoXV$# z^-V8^))v)PI*y%Iw+gcVNmaWF%&*-sXCx>V!+wO4i z#|Q%kqO!vGwj6C~5>d2PEmK}6Ts~GS(bmI8u_&xHI$+jteTNQt_o~BB{ zI4N~r0SqSf2H`2^gPptLnLQWjor}weiQsz0z{kg-qecfwut~#!fWn{Aj1^b3eLVQtE7XgG6{2>!Ax*)+x2O_k3c`BQY58`va( z-)jGi40o+|*cs4*kd`MR>L@Uvq6SW}_-Eqcg~MKF`Dx@OxpzQawTeUoWRYttypIA- z%BsUcO}E+f^uugl{(MzAUY*2P34g*J$n`!RT`nan!*)m=etqL2{lf}pzSkD@snbaB z-|c+g#k-q~9Kqob^}^Q#w$iYar$~w7pb7#QE&KIlDfd&##hOwnoCY7kKFf2Wf819| zi2FH{*0gVbw^*y?4x;z@?Oz%pybLf{V;+U_?g6*zbZL|pg}^WynMMiXPx7LEBA*0f z$T;$86iV`kMQ2S5X22mddALh(EAv-(GcDMz=nsluSU$loplF3$sXx0gWte;QVve%L z;h@r)QbF4w8J+w4yEZ^9wdbxtZjO0&ry2xrOTbsCcBum;iG7Cea`lZG{tVfdop8O? zoB8#3SATf8r}Jj}>$^a)EE@dH%20Zddv=`ltUaGzo%PRjEzAoqA!#1v0ekLXuiZ;a z@v`ybUIz&=Fz}(6qFr;j(s=p3zdtJ~%u>F@pdYhL$Ae5fpuLRa#G`N3Dtkm1n|)Xv%C&nSf@lPKpNXXO%`e^gLBU z!;UD0NZ&|b6305FntrayWNpRktb##nXa1p_qf%{9Mc0p2`z;LYq+%f%)!Z>^kOQ_g>QkmkbKQ-TgdXseBf*Q$2nM@?7;a`tJvmEVAz3)(1TsR6P3QGkxCXI4*)0 zz;cC zWDZj0spyzwyxWK9+Uv$*(pl)d1$oQwU5<&p}+Pm^i-?m9WUUe9ij?8&p`%1@TRA9=7wP2fG`UTF<2yvIq9h5L1VW__ePDfVl zJ=fo}VzLhJg*NonY}-(YFdtXXOLC;Y$a1*f)VlZRnTms%K63JAUD1JSwJ}?9REaWR z)w_lcp%(rEYZ1-Oj)x^1Nf4~WRA4ie>ClLwDo%z5^QhmU(M z+cLG?Md_k_W$lgA9!Q7J&CTWKdvqh9dc(6Hhb?3NvLKJsz}NVn9vRgo~a!yrVE12?Pf5Pq-uwt9RF(iGj zB_-1h*)0WaYHn8KsA({V=s7-B-)9q*u%QwqPM3g0v8n@kgF#y#PFz`De93yfxNSvB zPeSBi@Ul(#Kb18lqKP7B->4^~fQn^(sDjfUOd#rWbJIlRNCA`QpChsnn zB=)NEE?0gUUJ(&t3`1v?at7bcOW7bKIIi!^t+8oXdx8;(ryM(nGY+A#({kYKeR}fS zt1hM{BUa64DlJo2O>E~XFzeCl1?Q@kJhd(x6n*_vB#37>QyC^|;RTQ49Jl6oqBDD% z+1I+}&*r`8%nT7e$%-THAM^e=WjdS23{o6sIfl>DAyv;5D7u)wl&g*e6UFhhqAd)8 zg*Jfpqo_BJ)kuMnp%b|SM~Xi$|G1D#;Rvs^xWLn4fb{a}+vlD4ziW3kclfe~;N~om zprrcDz}tJTX6p&xuM7?G>y;(7W%7u*qn6B;EjVFska~(wIs)cOc;oMie$=SLkRU8z zPzzN`GW7x5)1&W}3cx4(WwYl_t1is{sXZ*bPhrbX1b~x5Ow^|`=tU|*LZ_Sp5wVlxMvycl^}82{^VIbusO{Us?RG6U5T?hg=!taQUa29O6NlPygSA2R|lMD|)&Y zas@WK_58X(n;pp&g@Z8^*P+$JyyLATCbfG4!d zrwr!a@}3o?*%QG~)bz4o;@p%_AtI^PZ-+m4rk4OMM1W(4HbquN<-4-L`Yid73jB%7 z)p1*Azw76s&(rlnEGa57R6jatQ5>!8ZECZd?`s2J2cw!k)E4-IE_lv1{pG$qyxw*w zz2j&FMXWRKP=!*zAnmFLH6VDNi;+%yeO7fNBPz+YdpAc-B({bnVI)Agh(YC2^mIzo zn#D$n3Oqk&Yb{&!wll0KO}__3sjKr7 z_PX-v#%R^L{OzQ?zo)la9L)>i3~z31yla70~Zm2E|~Gnc1(6Eb_Vfw6nI=UqG`$`MhwKb^?tlUSE3CZfTrs1!5J#d4RjrbglokO&8c2<+ z_8kD_V<@MpFhL{#``_-_yW7cIoqr5kkHN{~riK(QMEf5xWyhs7IEih`$S)0&9E=B=b6U)50i>={q)I(Q@| z%;$xqNN|)vZ*EC%C9#4RH4p=W5a)+L^CArrt`@&YRdx3-AGX}ziKhVAr0b`*ls!@a#zUqQx>yGXSr}m|`#`??hc~mgzk>uc z)2YK{a^$a1--Qg##Uym6xgss3N$JWNX6FlXc`M6C$_UowkK5|*lIAr{e z*Jn-s8ann6SfxhejZx*?CJZno;j&NYZgoc@Wzn}~g?AlbR0G)l%Nez2Mk;!GDhx0H z0*rvXB^OUOu>T21@D+-;?t0ZZv6W*SD5!|CvA=E(*DX7rAQZ(5-Lk)2_q3;LyI<0_ zYQy}E_-|DnMDDd#Ch@z^@AJZ~MWZM9+E8aeJ#=U#Z-$=~@I7$O*$Xwxadu81 zEje-o9PI9<&&gi>X-=c+8BbAzYrb&Y$#>6IY@MTnO2s7x1o;Ouw3PdrXUpFu{n4*~=KPP`BX0$zFJM{$9Bt@3~6Z<=ne{iWsR zV_wbR0#1E@1WFmmvKduKJ1^ZJ}aH2?OC#F3^_FW#fF5UMbimS%&>SiEK` z4O^9n^qGCVAr^#|zxqw%K313ng%=tk`VV)vF$Ml|x)|w`9_OZ0xv9j(&rC`?mw`*p zEzB@+>{fdfR0^wF5hYhCC1oX{tVB{SK{p|)>+S8`TX~Pn_Kj_B3S4Ix>1aIT5#)V- zd}bq*1|Z0}FOGkyt^VB>j^_G)@C^v%2ytHt7j{w=c3BY-5ve;bqV?auvRraXE$x0C z%aYnvKGjlL<~~+)7xj!%8DyFLL&^sCPxMFr0;_*1_b*dN&#^L%(dL<@ki28J4Q4cy z=pKJ?J|y_f>OnT)q<4q+%smiIe8kRCI(?_ak@?enK0jt#4b*b3UB&+m?yss@BUjIFubcBC13> z6-5=5#+A#Oo{r9Y%I*`T0%$q$gCHszG=_m=d{ui$Oo8V-?tHK1sinf$#m)M9Us*U_ z{2(se(>TSZT|V<({4wLvX}inNm$gSsH|IImC*e>-NAA}Wq;;-r5Z5dwYr>7Vw$;}( zp4@GF$Aoniqs|Zsi-+@b(>;CSzGEE@XioZm_g%+{l+Cddq6qIRr%iTt{A<|8Ok-%Q z`MRgaGDwmwt{Cqjmj&k&>RmG(X7f8cX~zFM{;Z4kF|&O*#P=o9zV>Yif9ha zq|EY42U1fgn&aP2tNHExlaf6JNW3=bhY43ri!w(moT7?@O3MJ_xRje#rJ%%OqXeMw zTj_RziX8=$bWD3nP6%^~6@Sb^ZWz39YAF(FfW$IPoE4+X_Y&blQV*0W2)n{LKWt}Y z9@WoG`a-uSFZ?C5XH8d8IqgilH=+mI+*e6|SFeB5v@-jf7&ozr;jlI@gI$@k-0`*Q zrGWAOIbB{0pVXA`OJR`y(lubSY6*TNoUp9|YXBYr8 zHiO(1dy(j2(JDSEFGOLARxiI5=2{r@A~<>zlnTRkcT;-?P(5eaXbP z2w?H^u(GMiqa?@51|M9ie|jt#QEl++>E>;fGR}~#rsMYhqe+E79%;{1Ab-jLKR|m?o97*_R9l`y!+g{Fv=w~C_Nd#O zxS*@^+rMRHRRO21L3pOsi@jTb>ziPZ!7M`nfcRla1C%-3j_LgS0IVny-CN!evS0&! z6y7yX0=5W_%nc)&5e;70RUH5;D5^}eDylmW`teEIy`S3$!%De)NsCr5I397DZrfyi z3BT^(4o@?t>;|N8SKy%wQ=MljQI}7QUpJoLnc$NA-u>XVFX#GIw^j6{I7>cFjjRcw zh4EYc#~&W}t)rPGT*qNl$|hNSG*=-ufwyYUUk(D5T5?Yxkpkj?UF}&rP-WSkNy=bB0pBUzHqPfcvzGX!aji-|S}@qvE4YO;xe|#DMKg zjf%b6rH+8G;XwCkkjOmsR{* zpfH61mT5HrKi9YE2gSRMNoAdd+ejBnyr@__;OQmIC+mQ>R9-j@wROt=3i9Kb;*{DuBJO9FZZYM^MJVEn!62CVCZwQX*j0z?7CV&Q&V4W zyp6|-jMz&kbX-r~mIsQ+D*Ejq?SI+2T4bZLJt|CebhN&{{wLlYIomsyH8#0iwkUxm z;Ejd$_z`YGBHz9Eli}bz-^Uy@SJ&5psFbTkqpR?1%b*`CzcQb0&XCHVSeuxo@3si2 zzk)MXaKA}tS$_MGCNFAfBs~rF$7uFh&7MEV$jE51Y_Ir7c9Vi`vfCVP_JZJ8_1j}k zUM6_LNd6MJdaf)#hTZg#d%W?v_(y~kSsaLi&{0GT*|xfB*`%4b|2y%QgyF7*!R~21 zJY-l7bKZ6Z5i+!uZJ5xB>-l=P`R#WjvYZ?9mX&g~I)UduOdNl_xd|IUv+;{PuEUgt z@AAx#I#Y{Ubko0(aGLy+QyoZCS}6=-@v;Yx>&O0E7O=jgMFZU47JH$4ei`}kd=3~79i7Wc$kghxn=|6=R0 zTIbPf;8j-8ADYua>k?^0vR7NuF)O@edP4%C`OIJ`$Q>?8ZUYSpk|fZbLZYE%HzJBA zRW1b!D~*8>xTWpLHeU+>Oq9x1J4O2vM9&FYT^l`4F|+utSq+Ag`w&^@Z(XxlEgX1t zU(rvB>t0=RuFuVu92M6v05653yX27soZ4X!Ldw>?cO2lSSWmHors~sZ0xg zK}>or*YZYQJ3rHIfBe|?=~!0g5-DI~e~2!;r(5bxMV-oo_-Id4PTN&smCXBpCFL03 z>^R)A=giBL3t~_qMTKM!9Z-tfD5cWgY5oo$0luEzUsUQb;gT;-BTz6EYp)c`OZkSK zl`@#On#973D05aKIzi+=r-k|XRR!<+?{qPA=w$S)?TKqKMMBqzQWySQ)8{4t#ePJE69VDxa1X`!3 zafgo!@?if;Ir4=t@=aYbPsT2K4jBa_vS3rQ4S^vRN38YW_C^J%&QFcB>R(kVGi;Z3 z&M!&60k+zocutbxG>s0gZiG+ln;dtMjtzFt0rBwrVG|6$tZL9Aj5al<9kwl;r&Webik}( zxqn@3K9|)Fs}H8fcKfZ?Yi{46{iw7NT*5*GSa$RJ#~8C6p~vg#5p;d>yT`nR^3Agr zVF)MJ<^0FTAtZ?3o8BuSm!V}0^n&VCyqqG%>P zFv?V`6D^=R=zheVSG-?So$+x1pq|Y!`(1#l0rcai1O?9Him=GS_%7(9_fR_Fi6?B@ zp$`j^v}TYbI=vC<8m^wczpu-aj#u6_E%pdga5F|Va`-up?rg3uir)mR+ef2yqExIi zYWG_z_jWPmdo2Q*U*XY~)RYfD)sh;jBx{RAsDlht#R&dZa(^wEqgw2SFnh?iBjT}2 zXy5dqmZ#su@ml^+1T3Qu|0N_kaT?16Ta;5i2%;J_omo&>A*Qm*?js`Mi=%vrn2mlU}TH4 z=lWrJ+z^J5R#E+H12}}mbiDvR!?1Wzv}*o>J5*1}V!58el^O^RIVbv5yKUDjG?Yj< zMzhzJph2v^EY?BQei^OABVWv%tXES1GE$i&3v5GCvy;+dTI``WU0ce?M*bnhv)g`4 zGH-N2nJP8)eLSr~O(U9$=4y9raM_#P{4bh!n|YJINc&whKVyuE(b>+2jw^IBv2ksU zuWElVUi>9d1%i?tpo~srhJ@00lk2>A-Xr3RONN6e>|*JI6QD1WvA?^JG{zWBX;K6Ynbucq2CsighFG?9Mb;xvEWs8blX%Gx^a<7lm@zzSa88Un)^7kda;p=Ok%+X|}EOuqisT59T{|2kM>18BAv* z{Sear!VZj9=wdSlfk7I4Wp7;)&{j=ozSpMnSzo`_;iCd~Va+h7;9)EFWe)%4@Ovnc z_cVNE&zOSm(+=a^N6CqzpM06sLNlYkTK|#LM(%xErQ)q<;ML=|Nn#{gRCz`%E5^>j z;m4u37RB@}74$AU2i>j=TD2dh+?5ai_5PP> zLi@>rG@niMx{G9V5BM^W{gEvr5~vS)$tR%`#zKhIT)pcq7PhvvGo!xt2u5=s-0(S0 zeuq(QLYL4X5`A(f73L&(Y4)nyD9`c){%75r6CHrDuF&`v3n(wWM6r$S0=6;deFUEOzHS%)MGa6bpcIS~3gn)yOn0!;eM*0meA z>ebWh4$>4?a|QjpC6rvlaWMihn1Od0h+;r*;4cKCl>zQi{&V=rzu)ezdaQmDE{;jN z_UxpsTqW|u2ft}~+X?Yj@Ah{&gDx!_tz{LJ1Ijo@Gy;Z~pN$JVC>CijgAyt$SKRlZ z-}T?r?qf2DmDQdCd1X9h)AF}8vB=!M3Tp|)G5|0+&B>WgNKJfWMZ!Gs;)z^>Mo8p2 ze#nc$c2?CF6r08BW%A}+ThN{S{Qh|Nyw}DHhOqxe9%FS7m>)`^u8q|Ljvq*Ng?479 zutB7|6MAbtzT2U#ZJo7!Y{o*?fK$M5;=3JnM`cpK1e1~ZspI3oD_W~pS#WFX$EW#$ zK*1r=GfnZx9dZs&MdWuo+(n0j9r)r~y%XL(NNz9#3naps)l&NVf(x75WO1gpO)Njl zRi$s^8pr1QdOa>d`Dno8%sL-}c_pkC%parf{3h4M*bJjC!(gw%hc-!g+j^QCad78b zAnYLNY7V)Nnz)qJGJ$m8+_IIhdUUo`;Sy&F_~^{T2QdgwvWI3tyX(y|Of-tV;Y?_~ zsE)tr#16?akOm3i<)nNY>ak8wggA$dzzk76H`P~4#6Yl=dfpmdKwAeU~ z`JWNFC}d*u4PV$z}7-`vTAz$dc_5-`Z^9llqR)y4=V&o~DuBE*Rh zp$@`wFb)AM>Q^v^OCzyjUD?TNTQwEOU}Wui@w}ciZFnyL{QxYTd_xwj>5_)9bW+IA z*L2S#(4~;Ye7ov!$Zh#(naXe6LXU0iu(R!eb_@rQ zX63KW1Wj|wh~inioyMVa3o88p#`7A^sF!BC0RQKO$40R~Lf!;wAq5AxwyYZkZpbol z%9M}ZAjPmHFd?~6R-R;J@ef}e7$E38a^Q1{yC4g57u4ck+euub{Ca}_(pKg2!NSDZ zG~3zQTz^W=%XUiNC`;QD7JE#b>inV!jhQ{er3GbL2@lXdc>sgqil6bQuDN27c`H0d z@A%$EuT@VH5fyngXFp4g#&<(kK#rcqVAY-vYCzE6{yZ!F2kZHYy6~)>w*bO%fCR=z zD>QlKW8C<3G`ZjxVbzU{MIuR+HS-a>@81n{2cE9GVlT(Hf6x}>4iJB|8_vI?bnt$G z0*l%7zMGnvTBODo=kQi@$~*_z-|F7yprUrSb7pq8`+a*$OKS&`P|9YM*gB04rqxg< zC}`HhvX;CQv|z1r{Ka8=`43a<|9S zJs8J<5kaS9{~8hIm%5rnh`{x@#Xo-paWCpy%#cOnBg*F4q~YOVm!nQMKHBh$b0@Xd zwACa@KmT71MhqNg4cz&rUd1CEZPccRSy1RC4n8C-<9ZOj381q?n7i`xO%@lY6&PyrTJ<Ko=r))u>{m>A z;CylG-zc(|wZTtkR(2<+6;nXps3m5tUVGL)_r7WSG?6BzT-!E_L^e|+j_)9D7sX7L z9sMggPyJgsd$(f1qw%qMfb*Z$1^~q2e%4A#Qp43)x`zW#0bAQeeEf$TWNmg*&_XE1 zlX5!VhYdTJ?!$_;IjikfP5xP-A#U56nvxn>`R_&0{$4e(>-=+cGT(oX>xUfvn@789 zN2^LG?Qdv}n72ayQYe*FCQA@;hV@o@kjt`XP{k!{N?M2^G+HrUeW^5y^y!3)Cx#7k zu$**Nqcid}r}O03wEqngqt+sROVOzSSvL@Gtn<>!S6kcEv3NEoM46cX`7TN-=V4C7EDZ()em9fP*p0bb&f@ z<@un?+d9#m)N~|o6dR*}Ogf0KT3r=Av$IcY03arQ zL#jKo&#+_r&pJu zVHge$p;E2{2vZOGrPfUsfrmU$~xuFkNXjbe)HJZVU8|Bu}Hr$fQqS^sd2BY z6lv3%PzbQJaKb#|V8?MN`9N|f^M?$J8pf?;KVW2VF(_3gm%tJBF)2fbeG%NF)&{e; z-)5xz!4ZY7xSLY9@%P_qK2Yx(g(n6VV#aI49QDBV7s*eRMs{4ob2zR|*aovkwhpZ%tJ@HmW?<-zpriURqa4KLd`h%q93 zfwMeqoc2{0S{P27_U^oQH~gM{J?jVC=dApyv~lx}uzlLzDc$FEQ-?po!hCN+I5j6J zb_{{AB+AQ*We?sBGHea$0xi9GKT?!7xHqPljHvJqXG%|Oo%T{d^lREo{Ri~v9G%nb z`8YQRhQ1M3>aD68^yPiyA8>C_56hO&G^(ick<`{KFf7c|3x`#U44eAR@XUo>6imE!dnbwMdGSSM)1|qLhwXC*h$tA&yAT3Os1HdXF zvSMu!?zk-Ms(W4maPTwi0E}E*oyfw|I}>3)E+zmvR&R}5+-J?|M+Xp8hP(l+x-w4W zKUzCSA`0(2^fb9iTfEg&?K1KmnM|ffxW2I#&+e8J%eja*sb4Gbrx$g0bO6OgOTU5t zX7VI*plVS6X7Tj~BiNYAd2>bWf=_CEv^r5-TH9uze?S5#ziQaw#~rUwzvhn9fA@_{ z)D9L|Pp40hM+4j5WN+io`{2z7g9=sM+st6NoC_83xJQuDxtU7@1yJvX|<0F+AIIi zj(2x^h56fxEvmEg$yKSU2XDm^%RE_0bMF^?iy>cn$+D}nfz3M+kMXIc0rk`%%A7E? z2`C?w>-o8AshfNsT*V&S!2PX78)Dbvo(Q7?cGO(@%|{vvGXg>^jJQSx#3DT=g9)V= z`uf9XSkX5)7z9m2bAetVHDi9l4nAX|ck50$>YpdRg!yS(u*W`a;YMla$EeG=<7F?% zUv1F-p_Cjv%ke5z8v07QOl+{VhT7IHO;DOr*pu2FddyrsLv(ko_RcG-0w76cWsRq~ znU-@>u}F120fI*(dD%D zC{F5&LjdB(9(G0Q+KqCDcanmiR?nX>f#`{YKSuLQ)tz}0vI+l1!h_cWuyU+n=z`1n+Jm#7kOLoBU+C=281d=K|{@M$O<5YP*O>T!21h^ zt_m4(f9seoj(>hvtN&aR8(blE`Jx;lmqUj?!3{!V$^B}e>#9H1i2OPm{(wQ>)W(Mj zeNm!sYwGMUSnQTx`BkH9T+yqbzD7zR^;@GE5&^@oCEV|YaPFYz^DA@6`n9>hyQf>;-EMSZX8IA`Koc6NW$@=6kGS!SURS-#=~j%a@uRmxNx5^QF(P% z#qjgnJC)Yt6!klNjsFIw+`S89L{l*IExsr+KuNz1?1{`~nfW}T`-xe8a%zXBXDA*-{?y~|^7?plG# zvwv5#Z_68&m~Hpg8x9up#?QNppG7bI$mJFZQKN=;sw!oRVB`@4!z(U%t5fB*c{TC` z7|sf|V-v+8gg_w7F~lFkF7+g@Mj}W=v-D=efV?jUBQ66DPP~~lWbe8{;xoTBV(fLR zEKFK>1UfZ6?ts4_?F?2Hx(vi)V^E5MqJn}3|86CU?}XQ7ifdD%M}MtWzcWQZ?3N{6^ z-w3UZL23hcZU=v^rJ#&Dm?1Vdn$@Wq0W|)#)$Gi#R&#)KFJRJUW{SmNLO2X&AlDLX zg9i>!sZoTeBhl(XMGP3J`^)4GUPc=Rv}dMkxs%eiTr4t}G`O?u2jIjaY?-g(?$p$j)oliQ zbjS45)~2&j!Fi?r@!dqXU`4*TZnpNw0NS6CF~2-GNq+ult^Grd+HSzyY+F!jZdL0D z(4$rQfV(9yz{-A} zVJ^ZbARV}&f&|D#a|@@0;PlQ&LFb10rWzZg?AY6AQ+N~+QT*Mia--&oXml&+5yJ{P z=vHw|K@`gwP@e`D+^xd$?S~a&_LrxPk0r9TjAGcz+y}6a5|8E{^Mt#$6s%g_etjng z<%XW41QE05iXSG-RL)VTL`2E3VzP8klkMBj4t!pg-xP7dk0rR(=s&xG=wqc-us4a_ z9;NF1AQi)$7OUx)28WcCVuXdseLnX-`BS`6TN9&&k$|2?pQPvnM_DqS75P&)>qUiX zer74-9HjxU8L>o$QV^bs^;N&*bu9 ztU^dodpq@C#P&(K8m+&iJoSi90O+&FH^N^QAR8^v@f}Y+bowFW zV->W9+7QA6B%H=@GxPj|78!EY%dU8G-U62|jk zN-Me2#W5nQN+A@r)Z2r(VR$*qfrq8Rysnh0N+1x*YhZjc@VvbVqV-FMuS*(o+{#$7 z{LPrsHPd1aMi3e!7zr*aWkU)Gun#u#<3*eu<@$glLJQ6X7Z4hF$5v8$J8`_F#&c&^ zD&|qP5aWT==tMQ%D=!X)gEW(X;y~XK9?zs=E_jpIMG$Gwe$Av>vxh>H5pDj?(#iEENYSB$008qjTLMo~P4*lqmA~V3S@Jq|Hat9H zZ*_s5)JYqp_$cHDZ?|<{&kbtxN_h+VoIe*BsTjkCrZQk(neJs zO3y(Xru+2^#5P+Cz(P~*&{`kwlV72c;lUBtog58Ne*W#R%crjdcHXt{yl&Y{vZHwK zA8iT5()gb67WcjWtM>7z?erjs+IEt9I@$T8_)}l#?%JlNuYZ7Z0e#!NQ(<)8YG;2! zn0ozFxsQg&o$entQHep~X#a zOTZ5a7s2=W7Q}5GA3tre5Vi*b_rGhU!y9Vs8Kem(&vI7>C;hy_Q4?q+#2oA-7R@Rv zYI-F(TlwrlYPep-H(Vn^U%iE%JZD#xmM=} zotL7lv)kX4gjM3$KCupQg@d32mG8~2#{L&~0aQ8$PSby)S&d1TIl{1?1#=N0+SwvT zv)Z4*pZQYrUk*2?7@pHKfeWy&$l2Z!B(R++eH)XAQUAH&YMhfvZKFH1+oUXk(9m;u zVCg`Z0ZuE@HPG{>?+AJQ+qfT;pd>AwQduR1dTZ9UXe4jUyFuj~i3r|qm1|ys-QNP| zHiY@39STYj38f}gO+s)-jyLa#+0M>rgM^^Axlz@F=@l3LlJnZQx+(-)5mKgCPT$|u zr1}tecU1Q46_5|)^==5kFBE*XJX)sC(%tM+-Ez11fzpmjG3pWU{+c=uPflJc2CkuMOYbi= zL7~Al*Jtl;bv?9wGVNKv5l8|AWr}KQ#xrKPhb(h{bOqjKKpuJC!147tbS@Bb!<{YI zWwl&bA`H4mM3*YLk(HIakfx)#;Rw5rNKf~Mn*#|q($a*OG05Mcyk})~D~y@pv+!3O zvabd#jZqo0uIF_>ATDs?M&pI2eJ+EL9;+q@29V?1;5Ehf>F9z#|KmDh9Qvj4^c>O=5pN8N{Pr?NeA?OnY zJAx+IB70de8CCpD!pN^iq~JCg;hsKu_Vd%H4(P3jeE=7_^Ido^G62Dx8Nc}dT7c*# z7yp2k_Krm9M%z1=os6=gzBS<+4Pfi-=X;ixO7_OmiLLJWWi_#B-9>8Q;rr9BqkvFg zhb!tnXnxxn1$rd9nCRDNCGaz_6HBms`;IJ1fFO}@Hcn37+nu}1lHp6{k@7P1#h?yO zW4|(yhewUI5zG81L?8M@Bux#UU1dkhva9$*n=Z%7_?u3$NTH)m{w(jf&g33Z`Aq_j z`pfz0KjYPY=AwzDIbef%o*yVxXaBi&@caG3rTC)1t=Q8I*QRF<`?ZhT^`Ik5fRkYV zPFutVU2b@SNr2$~9pMa0Vnce$qf5sBxHWzZr4**Rhp%c{mnWMcl9v<>NUS>aPYA^F5v0w#hl1NVUe`09#SoF8GVsus>;bFQp*rgWMb@R?oW71;_%ZF< zml)Zuthha|07l+wOg0jruG4}?l2JW+81H*|FY>=-I7bIJ7QU{N?tO~*YWfj~0gKdy zB?#E<0N3ZuVyRo6PCa-bn+Gu5INTlI$c+?0ZOoi%akFvGjvpi}XX;d0pZmRes4e9G ziW@mUzcO~&ADxbZgW>&yRkt;t{u$uS!9Iivm2<&MF{PLUU1oO24c2%_*aFpx-0Sx` z;WVPq5zKQ4WR?XOAYO_j7Hg_Iqs>BEl(QRbGGoUFMbw*(Xc zh~+AZY@(ss$y$Er33*4(w%`o|Fg7K*j?OeC&;Jh!S`Cnat9tkxs+4U<1?&PU?5n7$ zMB{Y2$a_fGf5s*V{hex@l4{G1taH%3WgtU4-mT$Q@!;S?{BuigSJ!gHZP$w>m;mp* zIC?}XYDYyB_Xu2q;i}^g5fU=y;_(rPVdGFDu&Ik`E>`2o3R;wC?20e*&g%0>E*7!6 z(tpE7E==05qrGI9_!wlZR9wh($u+Blovha)@yZu4m<$D8P=guj9md4!#(Uy9cHH;o zh23O4w8}Co8|i@qa*#UCs&}vY@(7-OCJEfhNaY7Of4`KcSqLlFEEAt+%jy`%Uu+sE zU>WaHSU#;xGRb1&$V!{8<26oXU_ z$6`fgr3BbY$AKwez4~zVfx}=zr$qGH)UQ*=j!^nL84`tzM_L2=_ z>LnfflR>JiL5*)^%y2>lJ)trZVEhSa_I+*}MOFoXQp#5kkTmcS#Pl4s2c#!a zU%!5BcIv`#;LuP3gkF!>!q9K;D~r;E=hqeo8JERZbp!;9c}N@I$Wu%2`Joz1Q?@~=SDL>(<3c1pzc@eYD#PxJ zFv-^P&6hUH1~?A5?$FJwM^<m?7+J3-sq}Q&Jk_ zMS2y{U8C_Dte@jIBmBeZz@*w9TsR^j2|)4jjaA=*`k>&vvap=6l*c+f1@@SU4Th+w zn6wF%V8HXQC&tE;X|_U_3?GaC_p{6TbW81c(k}8e{xcgeavl=yL>zW?$4CC_Jnm|a z*vz-1&rF^CHayv@H^g4*2JY7@uOew9ZNNc#ypisc-; z)3Gz!rO#0fifnDC9vJVF(ro zG^2rCdutG-BR~rh1T%GM<49M3?Q}kNC{6KeaE4ybvxhi=TPa3Hj+)3BH+&Pk2S04|RO69Su@5L|J6x z*nZJ|As?(8>p?B6g6`Zo>OKJy=F&6XHcW3cWV{{|Vd8j6t^Zt*!2nyJ5PrYv> z_SP$;M|jIIg2jD?g*JA!!16;pM<)Vk%bR3GV?YHcaZzn{xC!D_&ulyN6-UMw^h`0v z{clz4*|N=uYZ`ejta{RJm}hr!U%h6fJG=Q5Tr}zZ{#rFn<2q=w8*GLVg1n zO=QR7aS;oq^gEzsyZMm&mqO3b=)|qKu~AY!-EW$J$phqdeGoP_w#JveW9+?Lj1OVu z4v@Yv3r~C|flD>HO%cgYh z-0j@?Gf8&Es@`q;n5enxx+-b+hS%$Hd&nGhLJB<@a{u#S)BT>xN`mjlQ0m{grOe>G zyrz4AcXECfR3ESm>{!P{+HI<9Bxo9?c6eANR7H zKiPv{CNL#3S=wdga>^3ecB*dVbiu*uNQA2^5J#;BXGvIZ($E5Fifh8V1Wa@LKA8_c zRq9griIHIG0c~$ZU*Wv*mQC=?VJw^WHGUN%KP_mi8@W}4IRVa$mS!)9IMuvwFlzz( zJFh2>yBTNW00u+Vs2HMwc3!tJlE}WT$#(>0>YUp=S*V<^?yuWWX0`U*TxX%qe^N3r z3ovD!ym5~4W>nngG_M4~n{n_bA|i;(57{}E&!7HxZ;f9U+Fy436O2^oX0207hz7CU zWN5wryrgYxZmn`)pmHcbNO^U`0lyp{UVmZINvjt>}I{+)jK! z9r@2`lm9qJ&5>I}H-^w^M+@zgVp*p~p0b`StDUZ<7ODXF?YJaW?XdLn1nA$-lR3%o zb=L?A>xp6+Y!z_a-MdRKKi!C=t@ZJrmY?pDlBUD>%7Nl&%IR8nZ6qCdEZaNTxZz>z zy+;YtcbD2b{Z~iE#=h`|`UTQ{;;#B5M4wUS+c7)Q=62S#X7sS@{>Vp8wiAK*o9Dn< zpOtlD-prh&M70D2zW5fA@*&EC#087;=r`%|AAT(on}B}*+eCD^ zoLLN4r-I|k_OmWyiSLNkC}Uz^fXc`UK#G-h*o%VZt(@>dBbKfxn|n7jPi6uu;;o?g zMzbdyUVLY#-dJg>d(IRCdIb;zULy)6NCQ?c!IYyW2QybdG|kc-1uhh4<6a3d{qe4p z>uS>Bn@r9Z`y0Paxa+6W8@u24@g6fOVa$=1=?S+48(F3$aY?|3OSLd9A_E5^uiD7dp0F9~vcbE#_; zqyBKJQE8h?SRnLp_w~Rco$6IT?~}eJdAczgxrsfgr$nq8qSitJ!W%4xR{cHS$eO8J zP3Z}}S!koKP3TmkgodBYFxu8b@eY#%6XkO{?< z5k-lX_a@&2aSyIARDS#h(ZK00B8$X7UV2{>bmd_gN&uPc-fShQVvokJzW3NX9MQB$ z@1k9ZZ;bf*_hu~8^OAT`vC+4Ti;KURj)ma8*0LGx_L#k!Wq$=&w&BwxSzv;4q@OlWme zQLidLzhH2u%2Wck&TDi_rqM&bPm{%7hYB~T+u|Ht$Tup-V_Ym z>OHb035?T^s2$YyQ=dkw$swf7i!i@!wUf-q?M{09!i&v4^ON4%Q(EWYozwkx>O$w- zQ#LkQ6P+;HNvC%n7h(MBJ1%sc`CNNEupsP&eXMe_m8vFzD|qu?=fQZAzSI3D{`G&4 zYP`;XhG?%VRm1is2L}g_SpPQLR%zvRTr7e{`|?qj8d_UDR_p13Qw`#-+QqIpIeo&y zq`6xS$x!BG?4B(A2{*YrLahfjaNf8;TPJwyF9yRyR7x5hh)Yii;0e5xnC z`l~?WrYXBBS8KMP?!mDq@%m+w2h1jCy=dwhW;tq!$BFARq;B5atTST6D@h|(%Ou7# z#bGn(o#dIXi)D$89rYczt#%r|5)p|s7t2TH>HfuXLcOz8`bU0KdBXa}1{b@rfzq;= zO=c7UQwFOcl80;%E%}sEpm*&#Wqz(BzoPQJO>ON%iN*vEzW8N7%iMSkdtW&>wUgh` z-bAlvYo`Z2LoVSX6$JkT5gu#FX9&<|C0t0q%sfwYv17l$%Q|MpBG9|WQWgR!GjrKd z`dZw`ys%ntxv(cq=)*>YghX6_)!fzWCHwSFpKEzW3wCcQ0=s{g z6kt(z9~s->PHih9D;qsfy6fc6XLR|Fb&U1*`$f7&v83xl_T1`_z`IViB=P5+4l8U{ zAfEk9lpJ2ziQz2xd^K~{o@K6enQZZO z$X@I}k3YHn5Yt=H&5Q7O_C56(0mfIRNG6x2tRtmQZp=80FCW>~MiVVvUo)umrl9cb zYIVIAXLZ2PYfGmT(p*HMdoDroa*h(~(z8Inz`&4S8laov$cn@=_g``=1y^-+h`H{F zB}LF%|NXz`~O-Pe2kVQE*I?THCeNVArH`26@*THvUIL9gpOk7tqG==;6 z%3b?LYilA_F511=T`w_Ir6qhIfH+_R9rS&3&>AU{_68^cKgs!D;o+V9z2%8=Vuo}L zrudaD=@V_{Hvgy7uCcMOHoMSOuHD^(7YcW0DDm{byZJ$&KLx42%4(7B;o*2OK$2>a zgH%yd!oTrv%wf+2U)$IAwO` zLW7Lawwz#JVi4}%nB(<4g`9DP#mTAp7prfAP~Qah>sx%A+)2K(qudv#W`;fF)s!Dh zxDgoO`LW5O>FY<{cQ#s^>fg42A$QPZ~qx9+niw~6wCggg3X@jL7`xm?U z`aTc}pXlZ6WP1B!N%;wx;t45|KV~`tPaNi#?gTOrS(q}&_V^JnT^N4A!Fc z8NN*ad59+QR?>-b|F2sYT#{MZ?R)h#E@5q zKyefsM--5B6B;zC+Wzt2*Q#1jfd5Vjb)Gsury%pCJ6~DV zRI3v3&6|moC=+k~^!pzig$2$hHhLIbPEe0}=kYlBSv5*Dk;zo%u6|~t#K9wfffVkG zPJmE<(AjJF)d$2X|1%Ej^j#05qy7m-B}G9 zmAtNYy}TcoJhr>ASgcw&O-^xC&=*Y%t(f1a1aULm`^~Djs(#tvk!Q7A#f3jtxYk|i z^dm`?mM|Kvs`koaP~^YvFJbmc7e+=6Be(K94}clzbpBrFPIJWniduY57oM#B+QRw9 zfvSO6Rq#m?kY*%@ODZ=E<1BsV#t1#$u07sXG~f^4b+{t-%z?6Twd0>`x7@wp?HBFy zeEgEKIT@J&P1sRUHmQ{EZ8x4Ur|Q>0>EM->0_2_iWuT~4xq0EKq=aO_hoRmKcX&x< z{j}akMIEnea5vZbX~zyl)qDAp+^lxN??eZex59|=x0qHnz)|m~yP&v(B}{vL8Wn=5k*%7k^~Hgft3 zu^z1=?fSs9E*FakdWs9?=2asrH7!jJzkPN8YHkpsKuqp|84o$DG_peVcFHh)TRS`m+hXk@AR^|2Rs-O&Z z)Nf?2I*1t8;k4NM^t#imw}Lc*Hy`x`+z)AeaeULjc%td86{6y~^@KtJQhr`}b!A73 zG};>B!g6>EWWT)J+B7JXWnwZdu(|Iv5#tVNwZxWTT2R+b^lg}vUR(_%iCdm8V9-sk z4#-}U3c5kOcel@KsES!YtMuN7xZZ+n(wO~uwr&sC#6;wL%T>X2 zx0tsM$2TjFLdDsLn+Y=>tS@dWc%?DD{L>>e&=?4>putp+R)OSTytrubN>)yaCr_GL z;+2<{C_Oc@YyInbLhu4$F_mll`te~$Lo*tM-?%iFW%Q~Fv`f3{2FFd6Cge7DGAFN+ZpXForhTloe`PZYf<@ZGUw z#?baEGAqH~fsLo{UFBRgKJkv`dm_Popym4;)Yk|zX;VpI9AD)-4`sXLPkPdGE~O%4 z42IQAvkptyq8F?4I5PyR&oX&e;TwoZ)8=>F+-#5JS?yGeVq1UQHvd&%Cl=x~=cn2{ z^51{|@n{I;C)y-n)I@1o1p65BjdqqKp6S=i_tgp)iM(mOm04R*O)bV^jWg|)yc(5kpRP`JP%zHT z{1VuEc0NruFI2I$cSr@y(Buit7-B2&ET{k}M=?>F7lsut?M(mry~|WuP{?-%PHG(aQTbm*xVl>!IF04icQI+W$s;4* zX7_Z#;NVXMWe%vN=um1PX|6n#-#8Y2pKU8E$1Y@P$LDySaynmCdoOHxvHSEGdy37> z~KYbkQVfJ2^)6PrUq;0b?4gIhWP~DDJpUxuq}Ky(-4T~>?uH^Ke)I4 zv<1IpX|e@!J!}__%Dj$vGK8X4V?-qyN6ZBzaV%m&u8+7R&TY9g*3I`S+CH`cxgkCT zcHyV)1l+CGy4>cF|N3^76uRVtNlVKpgs0-KjI&X~l0~HhY;|Y=S?Q>0>1{?D4&)3o zj(w>K36o8hCZxai6!OhN5ywAU+iT`3;Z*dU5CF{}tdr~^*%RIy#9M##g#UxGjR)4ZPd}Z?7v~{S z=`@%2>5aTwQSVqgJkmc@nxO;c7oK{APxSE@fuI$_xlgSq+_*Q>l z8VGB*5>HL_F}qzy2({a+xO0bWW$p0w-+j^O;_7M;STo-9!zC;7ACwEQT|PZN+?l%G z;en?Ao-p(uA>@V>yuo54_p0m;Cnyn9ke#5&t*fW03&;8wxbA-ffjC$nXx=x0|8cEH z4tTenXL8cj%KzEc6ucN4io@)M;F+(4oLAR6URj{yg60ngfqv;MK(5tJ?eO9??Fc>j zvNL#5%EHkMZhr}CAu|-*qEZ%8Zt}xB2xc%e+k}Un0loWpZJ#_@$(6EXB7AGLx^l!d z9B{%-JO?@-!rk)9ydsrP-^_4M6DwNk;bGAnTz{mh`pti4u& zeDSk@Dxl3{|q)--vmW98=N^vu&Etru@2HCcy zv#{Ubg%KcQuiklGTiNN&OcDl6_WwozYoPMRJ`oxF4=|lzkEXHoF1z;7@BsSY=}$@| z`7W>?k~04*>fF=EE!KVteR{8;a!V09PXNcP6MnLw_CO{NNZ1Q z($0vF0(ogdmZy&^7R{cucWczeUqU81)M<_8F*(X>D)q$kZ0K-#;0Ov8aQzCxVFh0H z2h%X}M(IlWD*|Lm{k{pH<4JhlqH-T_aw)Cm21CxR+Ss;b^>P`lY+JVF&EM+r+k9%@ zL`3mDPkbie>NoPVK>8f`UX;u=CJEgy@(S$o6w=^w z;!3~82HEe0)Fc8@CmV$ zl^lRB1NgGO!C@Ewz2izhCPBXtzvFs4+IAm*Bp@kQJtmOsuw}b+O6SD$B1)+B?I{#X z5p$+|rC)=hN87EG5~8O%)s4~et*{uWWT&a=KU~vZO~kgJ;aWX#H*k?Vd?MzXLXlUrj}2$F zzwOj=fn`rH)_4}^HNEWRhG;M@^e_{+nCS>(W*Xtk8*6Tx?C*#1&TKZJ(vX_!a&f4@ zHht3WkdUwxw~se5d_nkuJbxE&zTy=ZIQ*dv#w@FapqJ?CyCkkhqXyD^J$v97GtW$KwA5vJFl0rnQ!tQ2^?-J|iMnV~sN;6*f`k z%qm0(imFmQ(beZ-t9(VZO57?LVqSaYR23_tBBI7v0C3KY6$ zeFq9-jForUZ+F$|V-RDGv)R1wdt<2HaEvi+->+BKpn7t8#}Iw3TlioXg>EdUwHiJ z(Ppzz#97~t<5-xjHD7!C?bY?oCm(-gok7GYMKY#p&AYzc?{>BP);V``vsxZ4_v2uV zDWy2;Vu}FZtxIXDep4<*0SJjpF_yZn69w;G+cr(p%omHM?VGN1&KPUG4_GU6ZVObE zISX^)lv3tm37Hv)s%@4KOVyO98Z_(e+Pe^kq3!zHJWCd4to3(YUw1x4n5M}ZD}{?l z%^oYIs4{b@CRbG?QWg+VVHQwj1q4E1hRjJ+Ks8Gd)@d3_;lA%ujbySm=UmmDbw{pP zbTJ1wrygHIhDnHQA%!`;H(AICbSCD_p?xaA_{=mH0`+G zmcq{a`O#6^^#B;!;C*P?CJtk0gLfg0`<(NBw<~ql5%s}`;NuhxQA#NolMfy?1OPl- z!Qu4uG^d<%dGW!0YwXjfmpSL!PkVf{_`>JjTCHz>@Pi*NkB|P|H^2E`{_qcnVUGY^ z+Xe4yqLOp23Lc!b&e@k=e&q-6ykihU2wZZCu_m2=?d`8W`t;Go#bZ!()~P~HscjmT zGMmjoHO4rb%`TokZJX8y@2uVL_W&@(sgzRZQ4t1FQRbA1j1Ru5eGt(*+qG>If)Bpw zX1)nc+g7vIL403?4C$>nF0^fq2?>L5hH*H=As8#Q3rAHEr4&V|C4H(e4*TWF$rMLX z;h2?BSz?^pzR6spU(FOJ>uin()4TVci?A|?)*=U0U@nF%OIAi!R#1^j3=3dgMYn4h zaM%yC+02ju1Y?Yd6wU{(<-h&&Q@3+G_=t^R)>2C0EK=&oQNa_c=~-~Qb`QU^Dgc5a zAcLR))!PmJwcAokDZ?;^5Z3E85;^azb-Vq3jL{l0#z^6uO9+9P4bjcbI;P~UWfkxI zd_J!~DiZk+Ds)4Lv&F)Bmvi>chS2yBj3EK-x<2RVn`XUUIcHB#PONoJaYIFdG|NI z`t~QEe){~`vucG1A&{Zk6bArRAQ@A-bLY;JM~@63BDTTr_j@EHL(Ao|aGAz|2+5%H zHh5pjS|l9zL*Ms+c=i0sTH~!9_WP1ct#$zsZ!IVoV+@)7ey=QKP06w5tu`TmN-ZP} zZA;c$=Rkq^kfAfhSV~b)uC-3Ag~gDy&M|WZrK`bliW3kj0;_6GIuo_V?swa+ZJqO% z7f-sbSJ5II$1&!Va=N~{_SV(i8WF0{Amu6LaTs>iIRvcnGN4kp7=tW@z*d7=&Lzev z=fW%{OUV^_(HaT_0GrKbx81gFYYfe1i;9BF?P9H?c4cVl42y)fjz}i5-aBIu0Ho#% zsj4X7?t>MwUw1zSz~MS@P5Arr71>L-EFttdq7|nK1d`~ zm=76N!cc8zl9)f^@_}#LH5GtG(Bk#;tI&8=sR3s7#U`S{rHTcebI#U9=bEN-t|6)- z7UOIMP>h14lw+K#t5vxcv~k@=RYVW%D~AFht01ZZ2UK50iwY)<2TvX#vs_(W8)HvS zm)q^G>ssd0A1yfLI7M6RFB(cU>w>(;`uCKjychB!g=9~46 zA@U*IzkBcAy?f7|Kl|h-pEe@Dz0b9+<)-mM<4yTD&XtiD2O$-hILHS6k`e@z=6B*)jbD<)|iUuId4G) zfSRUJkr2Ey*fcEy_z(~vv@IE1idZ5A1<+iwF;)Roq{>uO)mlL$Km-J5{h0PdzTQ6uJwM1QVNg5V4XA81n&VM=fupuX}oi7*X5Mkqi#x5 zHTdR~t+lyi?>w^{YT1Mf7es2iPK9TS#nI7nhZtjuah&zNceZPIN~z@1wXLwd z#$erCgYy-Za>hKkfB)&@CkI2aF%C&ZtI~rk6)vLn=;KONAYusNV2e^|npO%Usxzh* z{*tw*P@PD%MjDZvvvoqW&K)v64xPVRcVCL=f^*gt<|a^{ay_r=o*ZL3^adMh8dJ~R zQZgurfEKparc#WmNC8nWw@ErpGfk=M`u%RVzF8lioUT?^qO1u8n2?zB)z#(6$?24Y z0SwVN3@$X|IJ8|eP19_?*zI-zoO7;-x+>HPOk+$+6Okzgy{D*vbC!^p%OSoEh>%iB zIhWb2C4<)RI1No`5vi855gAmr#;IyaLPp_GWq)9x5sh;;rNmg#Kok)G7Oet80YpU* zn7#R`sVKW zosv^2On^-jYH`o$+3C@24hRTvdUA62?s?bs7Z*=roMwIRynW;KH$~*);_+-YYkUxu zT#B=+ioW8cq&!B~ztHw@F1}YNX@UC&D#Bb4Bi9ccDp`WE;p+i z=7QA-gJ_Vhudhze?hLzq2C&wZT%2?J{eC`MjN{O??KqB7V_2CIAF$(^;LSv28o=d}W0~sLxP{z}&`SWl8|X7%ru#s4-Yg%GE6n&gGm` zfTf@w4z$W@*NBNavf9j4L|LuWJsm+5iXf3i6BtMehrU}~-H>r9>kvy8+)ud)VXV2g zCJeF2ek3&8VcPEpHK7R=JJKSw-41OVw!2tU3o~aC-feb1gj{%hc1=iA(FVz_F55$$e_H6;oS=3EkkpcMrmN`;S(x+(IM3p0=bO|l(RX2>NimZzUSxtykH zy&H4EEcyBKwQ*D*s*5@2Y?7c2iP@+nLwPJUYOKb;PnnG+BF9`nMJcQDnD$8MoP{A* zj>G#w0E$VDVPnD@{bXDYQ}y$4oBWl?L3ved+2P%R?9)Lf7O z1vb0=e9^1Gvu8JiFpcSUHE30S?>>EWdVId!Z4uEszu9dq;OXOtHB2gXk;uI@>1*&M!k|iW`0)Pe)R7V0swAn^uv2DBEei&mm#)v8*7-tDc zLRb*1BZwi3c7=9MkgA_{-1%_2oaa)|s|3BWkN@%PrEH3~6002ovPDHLk FV1o5oi@E>+ literal 0 HcmV?d00001 diff --git a/tests/images_res/cat3.png b/tests/images_res/cat3.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe01b3f3e02d667802322f3875e6e91e5cb8bba GIT binary patch literal 64331 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 literal 0 HcmV?d00001 diff --git a/tests/widgets/ExampleImagesWidget.hpp b/tests/widgets/ExampleImagesWidget.hpp new file mode 100644 index 00000000..8c0a134a --- /dev/null +++ b/tests/widgets/ExampleImagesWidget.hpp @@ -0,0 +1,203 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 EXAMPLE_IMAGES_WIDGET_HPP_INCLUDED +#define EXAMPLE_IMAGES_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "Image.hpp" +#include "Widget.hpp" +#include "Window.hpp" + +// ------------------------------------------------------ +// Images + +#include "../images_res/CatPics.hpp" + +// ------------------------------------------------------ +// our widget + +class ExampleImagesWidget : public Widget, + public IdleCallback +{ +public: + static const int kImg1y = 0; + static const int kImg2y = 500/2-CatPics::cat2Height/2; + static const int kImg3x = 400/3-CatPics::cat3Width/3; + + static const int kImg1max = 500-CatPics::cat1Width; + static const int kImg2max = 500-CatPics::cat2Width; + static const int kImg3max = 400-CatPics::cat3Height; + + ExampleImagesWidget(Window& parent, const bool setParentSize = false) + : Widget(parent), + fImgTop1st(1), + fImgTop2nd(2), + fImgTop3rd(3), + fImg1x(0), + fImg2x(kImg2max), + fImg3y(kImg3max), + fImg1rev(false), + fImg2rev(true), + fImg3rev(true), + fImg1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), + fImg2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), + fImg3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + { + setSize(500, 400); + + parent.addIdleCallback(this); + + if (setParentSize) + { + parent.setSize(500, 400); + parent.setResizable(false); + } + } + +private: + void idleCallback() noexcept override + { + if (fImg1rev) + { + fImg1x -= 2; + if (fImg1x <= -50) + { + fImg1rev = false; + setNewTopImg(1); + } + } + else + { + fImg1x += 2; + if (fImg1x >= kImg1max+50) + { + fImg1rev = true; + setNewTopImg(1); + } + } + + if (fImg2rev) + { + fImg2x -= 1; + if (fImg2x <= -50) + { + fImg2rev = false; + setNewTopImg(2); + } + } + else + { + fImg2x += 4; + if (fImg2x >= kImg2max+50) + { + fImg2rev = true; + setNewTopImg(2); + } + } + + if (fImg3rev) + { + fImg3y -= 3; + if (fImg3y <= -50) + { + fImg3rev = false; + setNewTopImg(3); + } + } + else + { + fImg3y += 3; + if (fImg3y >= kImg3max+50) + { + fImg3rev = true; + setNewTopImg(3); + } + } + + repaint(); + } + + void onDisplay() override + { + switch (fImgTop3rd) + { + case 1: + fImg1.drawAt(fImg1x, kImg1y); + break; + case 2: + fImg2.drawAt(fImg2x, kImg2y); + break; + case 3: + fImg3.drawAt(kImg3x, fImg3y); + break; + }; + + switch (fImgTop2nd) + { + case 1: + fImg1.drawAt(fImg1x, kImg1y); + break; + case 2: + fImg2.drawAt(fImg2x, kImg2y); + break; + case 3: + fImg3.drawAt(kImg3x, fImg3y); + break; + }; + + switch (fImgTop1st) + { + case 1: + fImg1.drawAt(fImg1x, kImg1y); + break; + case 2: + fImg2.drawAt(fImg2x, kImg2y); + break; + case 3: + fImg3.drawAt(kImg3x, fImg3y); + break; + }; + } + + void setNewTopImg(const int imgId) noexcept + { + if (fImgTop1st == imgId) + return; + + if (fImgTop2nd == imgId) + { + fImgTop2nd = fImgTop1st; + fImgTop1st = imgId; + return; + } + + fImgTop3rd = fImgTop2nd; + fImgTop2nd = fImgTop1st; + fImgTop1st = imgId; + } + + int fImgTop1st, fImgTop2nd, fImgTop3rd; + int fImg1x, fImg2x, fImg3y; + bool fImg1rev, fImg2rev, fImg3rev; + Image fImg1, fImg2, fImg3; +}; + +// ------------------------------------------------------ + +#endif // EXAMPLE_IMAGES_WIDGET_HPP_INCLUDED diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp new file mode 100644 index 00000000..8f705cad --- /dev/null +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -0,0 +1,156 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 EXAMPLE_RECTANGLES_WIDGET_HPP_INCLUDED +#define EXAMPLE_RECTANGLES_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "Widget.hpp" +#include "Window.hpp" + +// ------------------------------------------------------ +// our widget + +class ExampleRectanglesWidget : public Widget +{ +public: + ExampleRectanglesWidget(Window& parent) + : Widget(parent) + { + setSize(300, 300); + + for (int i=0; i<9; ++i) + fClicked[i] = false; + } + + ExampleRectanglesWidget(Widget* groupWidget) + : Widget(groupWidget) + { + setSize(300, 300); + + for (int i=0; i<9; ++i) + fClicked[i] = false; + } + +protected: + void onDisplay() override + { + const int width = getWidth(); + const int height = getHeight(); + + Rectangle r; + + r.setWidth(width/3 - 6); + r.setHeight(height/3 - 6); + + // draw a 3x3 grid + for (int i=0; i<3; ++i) + { + r.setX(3 + i*width/3); + + // 1st + r.setY(3); + + if (fClicked[0+i]) + glColor3f(0.8f, 0.5f, 0.3f); + else + glColor3f(0.3f, 0.5f, 0.8f); + + r.draw(); + + // 2nd + r.setY(3 + height/3); + + if (fClicked[3+i]) + glColor3f(0.8f, 0.5f, 0.3f); + else + glColor3f(0.3f, 0.5f, 0.8f); + + r.draw(); + + // 3rd + r.setY(3 + height*2/3); + + if (fClicked[6+i]) + glColor3f(0.8f, 0.5f, 0.3f); + else + glColor3f(0.3f, 0.5f, 0.8f); + + r.draw(); + } + } + + bool onMouse(const MouseEvent& ev) override + { + if (ev.button != 1 || ! ev.press) + return false; + + const int width = getWidth(); + const int height = getHeight(); + + Rectangle r; + + r.setWidth(width/3 - 6); + r.setHeight(height/3 - 6); + + // draw a 3x3 grid + for (int i=0; i<3; ++i) + { + r.setX(3 + i*width/3); + + // 1st + r.setY(3); + + if (r.contains(ev.pos)) + { + fClicked[0+i] = !fClicked[0+i]; + repaint(); + break; + } + + // 2nd + r.setY(3 + height/3); + + if (r.contains(ev.pos)) + { + fClicked[3+i] = !fClicked[3+i]; + repaint(); + break; + } + + // 3rd + r.setY(3 + height*2/3); + + if (r.contains(ev.pos)) + { + fClicked[6+i] = !fClicked[6+i]; + repaint(); + break; + } + } + + return true; + } + +private: + bool fClicked[9]; +}; + +// ------------------------------------------------------ + +#endif // EXAMPLE_RECTANGLES_WIDGET_HPP_INCLUDED diff --git a/tests/widgets/ExampleShapesWidget.hpp b/tests/widgets/ExampleShapesWidget.hpp new file mode 100644 index 00000000..967bc439 --- /dev/null +++ b/tests/widgets/ExampleShapesWidget.hpp @@ -0,0 +1,108 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 EXAMPLE_SHAPES_WIDGET_HPP_INCLUDED +#define EXAMPLE_SHAPES_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "Widget.hpp" +#include "Window.hpp" + +// ------------------------------------------------------ +// our widget + +class ExampleShapesWidget : public Widget +{ +public: + ExampleShapesWidget(Window& parent) + : Widget(parent) + { + setSize(300, 300); + } + + ExampleShapesWidget(Widget* groupWidget) + : Widget(groupWidget) + { + setSize(300, 300); + } + +protected: + void onDisplay() override + { +#if 0 + glEnable(GL_MULTISAMPLE); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_SRC_ALPHA); + glEnable(GL_ONE_MINUS_SRC_ALPHA); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); +#endif + + glLineWidth(1.0f); + glColor3f(0.302f, 0.337f, 0.361f); + bg.draw(); + + glColor3f(0.235f, 0.271f, 0.294f); + rect.draw(); + + glColor3f(0.176f, 0.212f, 0.235f); + rect.drawOutline(); + + glColor3f(0.302f*2, 0.337f*2, 0.361f*2); + tri.draw(); + + glLineWidth(3.0f); + glColor3f(0.302f/2.0f, 0.337f/2.0f, 0.361f/2.0f); + tri.drawOutline(); + + glColor3f(0.235f, 0.271f, 0.294f); + cir.draw(); + + glLineWidth(2.0f); + glColor3f(0.176f/4, 0.212f/4, 0.235f/4); + cir.drawOutline(); + } + + void onResize(const ResizeEvent& ev) override + { + const int width = ev.size.getWidth(); + const int height = ev.size.getHeight(); + + // background + bg = Rectangle(0, 0, width, height); + + // rectangle + rect = Rectangle(20, 10, width-40, height-20); + + // center triangle + tri = Triangle(width*0.5, height*0.1, width*0.1, height*0.9, width*0.9, height*0.9); + + // circle + cir = Circle(width/2, height*2/3, height/6, 300); + } + +private: + Rectangle bg; + Rectangle rect; + Triangle tri; + Circle cir; +}; + +// ------------------------------------------------------ + +#endif // EXAMPLE_SHAPES_WIDGET_HPP_INCLUDED diff --git a/tests/widgets/ExampleTextWidget.hpp b/tests/widgets/ExampleTextWidget.hpp new file mode 100644 index 00000000..3d3bbca1 --- /dev/null +++ b/tests/widgets/ExampleTextWidget.hpp @@ -0,0 +1,70 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 EXAMPLE_TEXT_WIDGET_HPP_INCLUDED +#define EXAMPLE_TEXT_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "NanoVG.hpp" + +// ------------------------------------------------------ +// our widget + +class ExampleTextWidget : public NanoWidget +{ +public: + ExampleTextWidget(Window& parent) + : NanoWidget(parent), + fFont(createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf")) + { + setSize(500, 300); + } + + ExampleTextWidget(Widget* groupWidget) + : NanoWidget(groupWidget), + fFont(createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf")) + { + setSize(500, 300); + } + +protected: + void onNanoDisplay() override + { + const int width = getWidth(); + const int height = getHeight(); + + fontSize(40.0f); + textAlign(Align(ALIGN_CENTER|ALIGN_MIDDLE)); + textLineHeight(20.0f); + + beginPath(); + fillColor(220,220,220,255); + roundedRect(10, height/4+10, width-20, height/2-20, 3); + fill(); + + fillColor(0,200,0,220); + textBox(10, height/2, width-20, "Hello World!", nullptr); + } + +private: + FontId fFont; +}; + +// ------------------------------------------------------ + +#endif // EXAMPLE_TEXT_WIDGET_HPP_INCLUDED diff --git a/tests/widgets/NanoPerfWidget.hpp b/tests/widgets/NanoPerfWidget.hpp new file mode 100644 index 00000000..6fb404a2 --- /dev/null +++ b/tests/widgets/NanoPerfWidget.hpp @@ -0,0 +1,240 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2015 Filipe Coelho + * + * 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 NANO_PERF_WIDGET_HPP_INCLUDED +#define NANO_PERF_WIDGET_HPP_INCLUDED + +// ------------------------------------------------------ +// DGL Stuff + +#include "NanoVG.hpp" +#include "../../dpf/distrho/extra/String.hpp" + +// ------------------------------------------------------ +// get time + +#include +#include + +#ifdef DISTRHO_OS_WINDOWS +#else +struct TimePOSIX { + bool monotonic; + double resolution; + uint64_t base; + + TimePOSIX() + : monotonic(false), + resolution(1e-6), + base(0) + { +#if defined(CLOCK_MONOTONIC) + struct timespec ts; + + if (::clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + monotonic = true; + resolution = 1e-9; + } +#endif + + base = getRawTime(); + } + + uint64_t getRawTime() + { +#if defined(CLOCK_MONOTONIC) + if (monotonic) + { + struct timespec ts; + + ::clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; + } + else +#endif + { + struct timeval tv; + + ::gettimeofday(&tv, NULL); + return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; + } + } + + double getTime() + { + return (double)(getRawTime() - base) * resolution; + } +}; + +static TimePOSIX gTime; +#endif + +// ------------------------------------------------------ +// our widget + +class NanoPerfWidget : public NanoWidget, + public IdleCallback +{ +public: + static const int kHistoryCount = 100; + + enum RenderStyle { + RENDER_FPS, + RENDER_MS, + }; + + NanoPerfWidget(Window& parent, RenderStyle style, const char* name) + : NanoWidget(parent), + fHead(0), + fStyle(style), + fName(name) + { + parent.addIdleCallback(this); + setSize(200, 35); + + std::memset(fValues, 0, sizeof(float)*kHistoryCount); + + createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); + + prevt = gTime.getTime(); + } + +protected: + void idleCallback() override + { + repaint(); + } + + void onNanoDisplay() override + { + double t, dt; + + t = gTime.getTime(); + dt = t - prevt; + prevt = t; + update(dt); + + const int w = 200; //getWidth(); + const int h = 35; //getHeight(); + + int i; + float avg; + char str[64]; + + avg = getAverage(); + + beginPath(); + rect(0, 0, w, h); + fillColor(0,0,0,128); + fill(); + + beginPath(); + moveTo(0, h); + + if (fStyle == RENDER_FPS) + { + for (i = 0; i < kHistoryCount; ++i) + { + float v = 1.0f / (0.00001f + fValues[(fHead+i) % kHistoryCount]); + float vx, vy; + if (v > 80.0f) v = 80.0f; + vx = ((float)i/(kHistoryCount-1)) * w; + vy = h - ((v / 80.0f) * h); + lineTo(vx, vy); + } + } + else + { + for (i = 0; i < kHistoryCount; ++i) + { + float v = fValues[(fHead+i) % kHistoryCount] * 1000.0f; + float vx, vy; + if (v > 20.0f) v = 20.0f; + vx = ((float)i/(kHistoryCount-1)) * w; + vy = h - ((v / 20.0f) * h); + lineTo(vx, vy); + } + } + + lineTo(w, h); + fillColor(255,192,0,128); + fill(); + + fontFace("sans"); + + if (fName.isNotEmpty()) + { + fontSize(14.0f); + textAlign(ALIGN_LEFT|ALIGN_TOP); + fillColor(240,240,240,192); + text(3, 1, fName, nullptr); + } + + if (fStyle == RENDER_FPS) + { + fontSize(18.0f); + textAlign(ALIGN_RIGHT|ALIGN_TOP); + fillColor(240,240,240,255); + std::sprintf(str, "%.2f FPS", 1.0f / avg); + text(w-3, 1, str, nullptr); + + fontSize(15.0f); + textAlign(ALIGN_RIGHT|ALIGN_BOTTOM); + fillColor(240,240,240,160); + std::sprintf(str, "%.2f ms", avg * 1000.0f); + text(w-3, h-1, str, nullptr); + } + else + { + fontSize(18.0f); + textAlign(ALIGN_RIGHT|ALIGN_TOP); + fillColor(240,240,240,255); + std::sprintf(str, "%.2f ms", avg * 1000.0f); + text(w-3, 1, str, nullptr); + } + } + +private: + int fHead; + float fValues[kHistoryCount]; + + const int fStyle; + const String fName; + + double prevt; + + void update(float frameTime) noexcept + { + fHead = (fHead+1) % kHistoryCount; + fValues[fHead] = frameTime; + } + + float getAverage() const noexcept + { + int i; + float avg = 0; + + for (i = 0; i < kHistoryCount; ++i) + avg += fValues[i]; + + return avg / (float)kHistoryCount; + } +}; + +// ------------------------------------------------------ + +#endif // NANO_PERF_WIDGET_HPP_INCLUDED From ffe1a84634db0bec6031789f0d64ac6cf2534221 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 6 May 2021 22:18:42 +0100 Subject: [PATCH 042/159] cleanup pugl.cpp --- dgl/Widget.hpp | 2 +- dgl/src/Cairo.cpp | 11 +++++++++++ dgl/src/OpenGL.cpp | 8 ++++++++ dgl/src/pugl.cpp | 23 +---------------------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 07fe4eb1..f5a405c5 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -164,7 +164,7 @@ public: /** Get top-level widget, as passed directly in the constructor - or going up the chain of group widgets until it finds the top-level widget. + or going up the chain of group widgets until it finds the top-level one. */ TopLevelWidget* getTopLevelWidget() const noexcept; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 2c1f60d4..db11e60a 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -88,4 +88,15 @@ void Widget::PrivateData::display(const uint width, // ----------------------------------------------------------------------- +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept +{ + GraphicsContext& context((GraphicsContext&)graphicsContext); +#ifdef DGL_CAIRO + ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); +#endif + return context; +} + +// ----------------------------------------------------------------------- + END_NAMESPACE_DGL diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 6d3c5bb9..bca4b79a 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -16,6 +16,7 @@ #include "../OpenGL.hpp" #include "WidgetPrivateData.hpp" +#include "WindowPrivateData.hpp" START_NAMESPACE_DGL @@ -170,4 +171,11 @@ void Widget::PrivateData::display(const uint width, // ----------------------------------------------------------------------- +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept +{ + return (const GraphicsContext&)graphicsContext; +} + +// ----------------------------------------------------------------------- + END_NAMESPACE_DGL diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index aba3442e..f5d02976 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -171,6 +171,7 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* view) // -------------------------------------------------------------------------------------------------------------------- // DGL specific, build-specific fallback drawing + void puglFallbackOnDisplay(PuglView*) { #ifdef DGL_OPENGL @@ -196,28 +197,6 @@ void puglFallbackOnResize(PuglView* view) #endif } -END_NAMESPACE_DGL - // -------------------------------------------------------------------------------------------------------------------- -// extra, build-specific stuff - -#include "WindowPrivateData.hpp" - -#ifdef DGL_CAIRO -# include "../Cairo.hpp" -#endif - -START_NAMESPACE_DGL - -const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept -{ - GraphicsContext& context((GraphicsContext&)graphicsContext); -#ifdef DGL_CAIRO - ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); -#endif - return context; -} END_NAMESPACE_DGL - -// -------------------------------------------------------------------------------------------------------------------- From ab5f3d3b011096ee8966493828bcc609377d905b Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 7 May 2021 02:03:31 +0100 Subject: [PATCH 043/159] More code restructure; Add back onClose and repaint --- dgl/SubWidget.hpp | 1 + dgl/TopLevelWidget.hpp | 13 -- dgl/Widget.hpp | 8 +- dgl/Window.hpp | 14 +- dgl/src/ApplicationPrivateData.hpp | 4 +- dgl/src/Cairo.cpp | 6 +- dgl/src/OpenGL.cpp | 135 ++++++++++++----- dgl/src/SubWidget.cpp | 1 - dgl/src/SubWidgetPrivateData.cpp | 39 +++++ dgl/src/SubWidgetPrivateData.hpp | 9 +- dgl/src/TopLevelWidget.cpp | 12 -- dgl/src/TopLevelWidgetPrivateData.cpp | 48 +++--- dgl/src/TopLevelWidgetPrivateData.hpp | 6 +- dgl/src/Widget.cpp | 6 +- dgl/src/WidgetPrivateData.cpp | 44 +++--- dgl/src/WidgetPrivateData.hpp | 14 +- dgl/src/Window.cpp | 41 +++--- dgl/src/WindowPrivateData.cpp | 49 ++++--- dgl/src/WindowPrivateData.hpp | 9 +- dgl/src/pugl.cpp | 4 +- dgl/src/pugl.hpp | 4 +- tests/Demo.cpp | 202 +++++++++++++++++++++++--- tests/widgets/ExampleColorWidget.hpp | 2 +- 23 files changed, 452 insertions(+), 219 deletions(-) create mode 100644 dgl/src/SubWidgetPrivateData.cpp diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 7510ae83..631be76a 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -105,6 +105,7 @@ protected: private: struct PrivateData; PrivateData* const pData; + friend class Widget; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) }; diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 53773073..214d2888 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -50,19 +50,6 @@ public: */ virtual ~TopLevelWidget(); -protected: - /** - A function called to draw the widget contents. - Reimplemented from Widget::onDisplay. - */ - void onDisplay() override; - - /** - A function called when the widget is resized. - Reimplemented from Widget::onResize. - */ - void onResize(const ResizeEvent&) override; - private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index f5a405c5..b1a590f7 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -63,14 +63,14 @@ using namespace Events; class Widget { /** - Private constructor, reserved for SubWidget class. + Private constructor, reserved for TopLevelWidget class. */ - explicit Widget(Widget* widgetToGroupTo); + explicit Widget(TopLevelWidget* topLevelWidget); /** - Private constructor, reserved for TopLevelWidget class. + Private constructor, reserved for SubWidget class. */ - explicit Widget(TopLevelWidget* topLevelWidget); + explicit Widget(Widget* widgetToGroupTo); public: /** diff --git a/dgl/Window.hpp b/dgl/Window.hpp index bb039b6e..852c1658 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -167,6 +167,9 @@ public: */ uintptr_t getNativeWindowHandle() const noexcept; + void repaint() noexcept; + void repaint(const Rectangle& rect) noexcept; + protected: /** A function called when the window is resized. @@ -174,6 +177,13 @@ protected: */ virtual void onReshape(uint width, uint height); + /** + A function called when the window is attempted to be closed. + Returning true closes the window, which is the default behaviour. + Override this method and return false to prevent the window from being closed by the user. + */ + virtual bool onClose(); + private: struct PrivateData; PrivateData* const pData; @@ -247,8 +257,6 @@ END_NAMESPACE_DGL void exec(bool lockWait = false); void focus(); - void repaint() noexcept; - void repaint(const Rectangle& rect) noexcept; #ifndef DGL_FILE_BROWSER_DISABLED bool openFileBrowser(const FileBrowserOptions& options); @@ -272,8 +280,6 @@ END_NAMESPACE_DGL protected: - virtual void onClose(); - #ifndef DGL_FILE_BROWSER_DISABLED virtual void fileBrowserSelected(const char* filename); #endif diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 15b252a6..a636902c 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -53,7 +53,7 @@ struct Application::PrivateData { std::list idleCallbacks; /** Constructor and destructor */ - PrivateData(const bool standalone); + explicit PrivateData(bool standalone); ~PrivateData(); /** Flag one window as shown, which increments @a visibleWindows. @@ -67,7 +67,7 @@ struct Application::PrivateData { void oneWindowClosed() noexcept; /** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */ - void idle(const uint timeoutInMs); + void idle(uint timeoutInMs); /** Set flag indicating application is quitting, and close all windows in reverse order of registration. For standalone mode only. */ diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index db11e60a..fdb171cc 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -66,7 +66,7 @@ void Rectangle::_draw(const bool outline) void Widget::PrivateData::display(const uint width, const uint height, - const double scaling, + const double autoScaling, const bool renderingSubWidget) { if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) @@ -76,14 +76,14 @@ void Widget::PrivateData::display(const uint width, cairo_matrix_t matrix; cairo_get_matrix(cr, &matrix); cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); - // TODO: scaling and cropping + // TODO: autoScaling and cropping // display widget self->onDisplay(); cairo_set_matrix(cr, &matrix); - displaySubWidgets(width, height, scaling); + displaySubWidgets(width, height, autoScaling); } // ----------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index bca4b79a..4909dde0 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -15,6 +15,7 @@ */ #include "../OpenGL.hpp" +#include "SubWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" @@ -110,63 +111,123 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- +#if 0 void Widget::PrivateData::display(const uint width, const uint height, - const double scaling, + const double autoScaling, const bool renderingSubWidget) { + printf("Widget::PrivateData::display INIT\n"); + if (/*(skipDisplay && ! renderingSubWidget) ||*/ size.isInvalid() || ! visible) return; -// bool needsDisableScissor = false; + bool needsDisableScissor = false; // reset color glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -// if (needsFullViewport || (absolutePos.isZero() && size == Size(width, height))) +#if 0 + if (/*needsFullViewport ||*/ (absolutePos.isZero() && size == Size(width, height))) +#endif + { + // full viewport size + glViewport(0, + -(height * autoScaling - height), + width * autoScaling, + height * autoScaling); + } +#if 0 + else if (needsScaling) + { + // limit viewport to widget bounds + glViewport(absolutePos.getX(), + height - self->getHeight() - absolutePos.getY(), + self->getWidth(), + self->getHeight()); + } + else + { + // only set viewport pos + glViewport(absolutePos.getX() * autoScaling, + -std::round((height * autoScaling - height) + (absolutePos.getY() * autoScaling)), + std::round(width * autoScaling), + std::round(height * autoScaling)); + + // then cut the outer bounds + glScissor(absolutePos.getX() * autoScaling, + height - std::round((self->getHeight() + absolutePos.getY()) * autoScaling), + std::round(self->getWidth() * autoScaling), + std::round(self->getHeight() * autoScaling)); + + glEnable(GL_SCISSOR_TEST); + needsDisableScissor = true; + } +#endif + + // display widget + self->onDisplay(); + + if (needsDisableScissor) + { + glDisable(GL_SCISSOR_TEST); + needsDisableScissor = false; + } + + displaySubWidgets(width, height, autoScaling); +} +#endif + +void SubWidget::PrivateData::display(uint width, uint height, double autoScaling) +{ + bool needsDisableScissor = false; + + if (absolutePos.isZero() && self->getSize() == Size(width, height)) { // full viewport size glViewport(0, - -(height * scaling - height), - width * scaling, - height * scaling); + -(height * autoScaling - height), + width * autoScaling, + height * autoScaling); + } + /* + else if (needsScaling) + { + // limit viewport to widget bounds + glViewport(absolutePos.getX(), + height - self->getHeight() - absolutePos.getY(), + self->getWidth(), + self->getHeight()); + } + */ + else + { + // only set viewport pos + glViewport(absolutePos.getX() * autoScaling, + -std::round((height * autoScaling - height) + (absolutePos.getY() * autoScaling)), + std::round(width * autoScaling), + std::round(height * autoScaling)); + + // then cut the outer bounds + glScissor(absolutePos.getX() * autoScaling, + height - std::round((self->getHeight() + absolutePos.getY()) * autoScaling), + std::round(self->getWidth() * autoScaling), + std::round(self->getHeight() * autoScaling)); + + glEnable(GL_SCISSOR_TEST); + needsDisableScissor = true; } -// else if (needsScaling) -// { -// // limit viewport to widget bounds -// glViewport(absolutePos.getX(), -// height - self->getHeight() - absolutePos.getY(), -// self->getWidth(), -// self->getHeight()); -// } -// else -// { -// // only set viewport pos -// glViewport(absolutePos.getX() * scaling, -// -std::round((height * scaling - height) + (absolutePos.getY() * scaling)), -// std::round(width * scaling), -// std::round(height * scaling)); -// -// // then cut the outer bounds -// glScissor(absolutePos.getX() * scaling, -// height - std::round((self->getHeight() + absolutePos.getY()) * scaling), -// std::round(self->getWidth() * scaling), -// std::round(self->getHeight() * scaling)); -// -// glEnable(GL_SCISSOR_TEST); -// needsDisableScissor = true; -// } // display widget self->onDisplay(); -// if (needsDisableScissor) -// { -// glDisable(GL_SCISSOR_TEST); -// needsDisableScissor = false; -// } + if (needsDisableScissor) + { + glDisable(GL_SCISSOR_TEST); + needsDisableScissor = false; + } - displaySubWidgets(width, height, scaling); +// displaySubWidgets(width, height, autoScaling); } // ----------------------------------------------------------------------- diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 48edb7d5..b3704f6a 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -15,7 +15,6 @@ */ #include "SubWidgetPrivateData.hpp" -#include "../TopLevelWidget.hpp" START_NAMESPACE_DGL diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp new file mode 100644 index 00000000..17d1c3e0 --- /dev/null +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -0,0 +1,39 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "SubWidgetPrivateData.hpp" +#include "WidgetPrivateData.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const p) + : self(s), + parent(p), + absolutePos() +{ + parent->pData->subWidgets.push_back(self); +} + +SubWidget::PrivateData::~PrivateData() +{ + parent->pData->subWidgets.remove(self); +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index 12b893ab..3f1a348b 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -28,10 +28,11 @@ struct SubWidget::PrivateData { Widget* const parent; Point absolutePos; - PrivateData(SubWidget* const s, Widget* const p) - : self(s), - parent(p), - absolutePos() {} + explicit PrivateData(SubWidget* const s, Widget* const p); + ~PrivateData(); + + // NOTE display function is different depending on build type, must call displaySubWidgets at the end + void display(uint width, uint height, double autoScaling); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 2a862243..8dda54a3 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -15,7 +15,6 @@ */ #include "TopLevelWidgetPrivateData.hpp" -// #include "pugl.hpp" START_NAMESPACE_DGL @@ -30,17 +29,6 @@ TopLevelWidget::~TopLevelWidget() delete pData; } -void TopLevelWidget::onDisplay() -{ - pData->display(); -} - -void TopLevelWidget::onResize(const ResizeEvent& ev) -{ - pData->resize(ev.size.getWidth(), ev.size.getHeight()); - Widget::onResize(ev); -} - // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 85df0f68..4a1cd1ab 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -31,40 +31,36 @@ START_NAMESPACE_DGL TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) : self(s), - window(w), - widgets() {} + selfw(s), + window(w) +{ + window.pData->topLevelWidget = self; +} -void TopLevelWidget::PrivateData::display() +TopLevelWidget::PrivateData::~PrivateData() { - puglFallbackOnDisplay(window.pData->view); + // FIXME? + window.pData->topLevelWidget = nullptr; +} - if (widgets.size() == 0) - return; +void TopLevelWidget::PrivateData::display() +{ + printf("TopLevelWidget::PrivateData::display INIT\n"); const Size size(window.getSize()); - const uint width = size.getWidth(); - const uint height = size.getHeight(); - const double scaling = window.pData->autoScaling; + const uint width = size.getWidth(); + const uint height = size.getHeight(); + const double autoScaling = window.pData->autoScaling; + printf("TopLevelWidget::PrivateData::display %i %i\n", width, height); - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); - widget->pData->display(width, height, scaling, false); - } -} + // full viewport size + glViewport(0, -(height * autoScaling - height), width * autoScaling, height * autoScaling); -void TopLevelWidget::PrivateData::resize(const uint width, const uint height) -{ - if (widgets.size() == 0) - return; -/* - FOR_EACH_WIDGET(it) - { - Widget* const widget(*it); + // main widget drawing + self->onDisplay(); - if (widget->pData->needsFullViewport) - widget->setSize(width, height); - }*/ + // now draw subwidgets if there are any + selfw->pData->displaySubWidgets(width, height, autoScaling); } // ----------------------------------------------------------------------- diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index b6eefdff..5e3b1d89 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -27,12 +27,12 @@ START_NAMESPACE_DGL struct TopLevelWidget::PrivateData { TopLevelWidget* const self; + Widget* const selfw; Window& window; - std::list widgets; - PrivateData(TopLevelWidget* const s, Window& w); + explicit PrivateData(TopLevelWidget* const s, Window& w); + ~PrivateData(); void display(); - void resize(uint width, uint height); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index c331b021..a0cfc5c6 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -21,12 +21,12 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Widget -Widget::Widget(Widget* const widgetToGroupTo) - : pData(new PrivateData(this, widgetToGroupTo)) {} - Widget::Widget(TopLevelWidget* const topLevelWidget) : pData(new PrivateData(this, topLevelWidget)) {} +Widget::Widget(Widget* const widgetToGroupTo) + : pData(new PrivateData(this, widgetToGroupTo)) {} + Widget::~Widget() { delete pData; diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index e09c8860..3cab134e 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -17,13 +17,6 @@ #include "WidgetPrivateData.hpp" #include "../TopLevelWidget.hpp" -#ifdef DGL_CAIRO -# include "../Cairo.hpp" -#endif -#ifdef DGL_OPENGL -# include "../OpenGL.hpp" -#endif - START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -36,9 +29,7 @@ Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) needsScaling(false), visible(true), size(0, 0), - subWidgets() -{ -} + subWidgets() {} Widget::PrivateData::PrivateData(Widget* const s, Widget* const g) : self(s), @@ -48,44 +39,43 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const g) needsScaling(false), visible(true), size(0, 0), - subWidgets() -{ - parentGroupWidget->pData->subWidgets.push_back(self); -} + subWidgets() {} Widget::PrivateData::~PrivateData() { - if (parentGroupWidget != nullptr) - parentGroupWidget->pData->subWidgets.remove(self); - subWidgets.clear(); } void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double scaling) { - for (std::list::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) + printf("Widget::PrivateData::displaySubWidgets INIT | %lu\n", subWidgets.size()); + + if (subWidgets.size() == 0) + return; + + for (std::list::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) { - Widget* const widget(*it); - DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); + SubWidget* const subwidget(*it); + printf("Widget::PrivateData::displaySubWidgets %i %i -> %p\n", width, height, subwidget); - widget->pData->display(width, height, scaling, true); + subwidget->pData->display(width, height, scaling); } } void Widget::PrivateData::repaint() { - if (parentGroupWidget != nullptr) - parentGroupWidget->repaint(); - else if (topLevelWidget != nullptr) - topLevelWidget->repaint(); +// if (parentGroupWidget != nullptr) +// parentGroupWidget->repaint(); +// else if (topLevelWidget != nullptr) +// topLevelWidget->repaint(); } // ----------------------------------------------------------------------- TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) { - if (TopLevelWidget* const tlw = dynamic_cast(w)) - return tlw; +// if (TopLevelWidget* const tlw = dynamic_cast(w)) +// return tlw; if (w->pData->topLevelWidget != nullptr) return w->pData->topLevelWidget; if (w->pData->parentGroupWidget != nullptr) diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 0331c0d1..9061548d 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -33,16 +33,18 @@ struct Widget::PrivateData { bool needsScaling; bool visible; Size size; - std::list subWidgets; + std::list subWidgets; - PrivateData(Widget* const s, TopLevelWidget* const tlw); - PrivateData(Widget* const s, Widget* const pgw); + // called via TopLevelWidget + explicit PrivateData(Widget* const s, TopLevelWidget* const tlw); + // called via SubWidget + explicit PrivateData(Widget* const s, Widget* const pgw); ~PrivateData(); - // NOTE display function is different depending on build type - void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget); + // NOTE display function is different depending on build type, must call displaySubWidgets at the end +// void display(uint width, uint height, double autoScaling, bool renderingSubWidget); - void displaySubWidgets(const uint width, const uint height, const double scaling); + void displaySubWidgets(uint width, uint height, double autoScaling); void repaint(); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 096f580c..22a181d0 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -133,11 +133,32 @@ uintptr_t Window::getNativeWindowHandle() const noexcept return puglGetNativeWindow(pData->view); } +void Window::repaint() noexcept +{ + puglPostRedisplay(pData->view); +} + +void Window::repaint(const Rectangle& rect) noexcept +{ + const PuglRect prect = { + static_cast(rect.getX()), + static_cast(rect.getY()), + static_cast(rect.getWidth()), + static_cast(rect.getHeight()), + }; + puglPostRedisplayRect(pData->view, prect); +} + void Window::onReshape(const uint width, const uint height) { puglFallbackOnResize(pData->view); } +bool Window::onClose() +{ + return true; +} + #if 0 #if 0 void Window::exec(bool lockWait) @@ -154,22 +175,6 @@ void Window::focus() puglGrabFocus(pData->fView); } -void Window::repaint() noexcept -{ - puglPostRedisplay(pData->fView); -} - -void Window::repaint(const Rectangle& rect) noexcept -{ - const PuglRect prect = { - static_cast(rect.getX()), - static_cast(rect.getY()), - static_cast(rect.getWidth()), - static_cast(rect.getHeight()), - }; - puglPostRedisplayRect(pData->fView, prect); -} - bool Window::isResizable() const noexcept { return puglGetViewHint(pData->fView, PUGL_RESIZABLE) == PUGL_TRUE; @@ -267,10 +272,6 @@ void Window::removeIdleCallback(IdleCallback* const callback) // ----------------------------------------------------------------------- -void Window::onClose() -{ -} - #ifndef DGL_FILE_BROWSER_DISABLED void Window::fileBrowserSelected(const char*) { diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 8735541e..0f4f8a93 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -15,7 +15,7 @@ */ #include "WindowPrivateData.hpp" -#include "../TopLevelWidget.hpp" +#include "TopLevelWidgetPrivateData.hpp" #include "pugl.hpp" @@ -255,22 +255,19 @@ void Window::PrivateData::idleCallback() // // if (fModal.enabled && fModal.parent != nullptr) // fModal.parent->windowSpecificIdle(); +// self->repaint(); } // ----------------------------------------------------------------------- void Window::PrivateData::onPuglDisplay() { + puglOnDisplayPrepare(view); + #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) - { - topLevelWidget->onDisplay(); - } - else + topLevelWidget->pData->display(); #endif - { - puglFallbackOnDisplay(view); - } } void Window::PrivateData::onPuglReshape(const int width, const int height) @@ -287,6 +284,22 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) #endif } +void Window::PrivateData::onPuglClose() +{ + DGL_DBG("PUGL: onClose\n"); + +// if (fModal.enabled) +// exec_fini(); + + if (! self->onClose()) + return; + +// if (fModal.childFocus != nullptr) +// fModal.childFocus->fSelf->onClose(); + + close(); +} + static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) @@ -310,6 +323,11 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu pData->onPuglDisplay(); break; + ///< View will be closed, a #PuglEventClose + case PUGL_CLOSE: + pData->onPuglClose(); + break; + // TODO default: break; @@ -517,21 +535,6 @@ void Window::PrivateData::removeWidget(Widget* const widget) // ----------------------------------------------------------------------- -void Window::PrivateData::onPuglClose() -{ - DGL_DBG("PUGL: onClose\n"); - -// if (fModal.enabled) -// exec_fini(); - - fSelf->onClose(); - - if (fModal.childFocus != nullptr) - fModal.childFocus->fSelf->onClose(); - - close(); -} - void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) { DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 0c626d52..e11c69fd 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -64,14 +64,14 @@ struct Window::PrivateData : IdleCallback { double autoScaling; /** Constructor for a regular, standalone window. */ - PrivateData(Application& app, Window* self); + explicit PrivateData(Application& app, Window* self); /** Constructor for a regular, standalone window with a transient parent. */ - PrivateData(Application& app, Window* self, Window& transientWindow); + explicit PrivateData(Application& app, Window* self, Window& transientWindow); /** Constructor for an embed Window, with a few extra hints from the host side. */ - PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, - uint width, uint height, double scaling, bool resizable); + explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, + uint width, uint height, double scaling, bool resizable); /** Destructor. */ ~PrivateData() override; @@ -98,6 +98,7 @@ struct Window::PrivateData : IdleCallback { // pugl events void onPuglDisplay(); void onPuglReshape(int width, int height); + void onPuglClose(); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index f5d02976..29f02eda 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -170,9 +170,9 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* view) } // -------------------------------------------------------------------------------------------------------------------- -// DGL specific, build-specific fallback drawing +// DGL specific, build-specific drawing prepare -void puglFallbackOnDisplay(PuglView*) +void puglOnDisplayPrepare(PuglView*) { #ifdef DGL_OPENGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 86f46eb1..2248fd73 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -45,9 +45,9 @@ puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); PUGL_API void puglSetMatchingBackendForCurrentBuild(PuglView* view); -// DGL specific, build-specific fallback drawing +// DGL specific, build-specific drawing prepare PUGL_API void -puglFallbackOnDisplay(PuglView* view); +puglOnDisplayPrepare(PuglView* view); // DGL specific, build-specific fallback resize PUGL_API void diff --git a/tests/Demo.cpp b/tests/Demo.cpp index e484e3e9..74cdf622 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -22,12 +22,12 @@ // #define DPF_TEST_POINT_CPP #include "dgl/src/pugl.cpp" -// #include "dgl/src/SubWidget.cpp" #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" #include "dgl/src/Geometry.cpp" #include "dgl/src/OpenGL.cpp" #include "dgl/src/SubWidget.cpp" +#include "dgl/src/SubWidgetPrivateData.cpp" #include "dgl/src/TopLevelWidget.cpp" #include "dgl/src/TopLevelWidgetPrivateData.cpp" #include "dgl/src/Widget.cpp" @@ -63,27 +63,166 @@ public: curPage(0), curHover(-1) { +#if 0 // for text -// font = nvg.createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); - -// using namespace DemoArtwork; -// img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); -// img2.loadFromMemory(ico2Data, ico2Width, ico2Height, GL_BGR); -// img3.loadFromMemory(ico3Data, ico3Width, ico2Height, GL_BGR); -// img4.loadFromMemory(ico4Data, ico4Width, ico4Height, GL_BGR); -// img5.loadFromMemory(ico5Data, ico5Width, ico5Height, GL_BGR); + font = nvg.createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); + + using namespace DemoArtwork; + img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); + img2.loadFromMemory(ico2Data, ico2Width, ico2Height, GL_BGR); + img3.loadFromMemory(ico3Data, ico3Width, ico2Height, GL_BGR); + img4.loadFromMemory(ico4Data, ico4Width, ico4Height, GL_BGR); + img5.loadFromMemory(ico5Data, ico5Width, ico5Height, GL_BGR); +#endif + } + +protected: + void onDisplay() override + { + const int iconSize = bgIcon.getWidth(); + printf("LEFT SIDE WIDGET onDisplay %i\n", iconSize); + + glColor3f(0.027f, 0.027f, 0.027f); + Rectangle(0, 0, getSize()).draw(); + + bgIcon.setY(curPage*iconSize + curPage*3); + + glColor3f(0.129f, 0.129f, 0.129f); + bgIcon.draw(); + + glColor3f(0.184f, 0.184f, 0.184f); + bgIcon.drawOutline(); + + if (curHover != curPage && curHover != -1) + { + Rectangle rHover(1, curHover*iconSize + curHover*3, iconSize-2, iconSize-2); + + glColor3f(0.071f, 0.071f, 0.071f); + rHover.draw(); + + glColor3f(0.102f, 0.102f, 0.102f); + rHover.drawOutline(); + } + + glLineWidth(2.0f); + glColor3f(0.184f, 0.184f, 0.184f); + lineSep.draw(); + + // reset color + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + +#if 0 + const int pad = iconSize/2 - DemoArtwork::ico1Width/2; + + img1.drawAt(pad, pad); + img2.drawAt(pad, pad + 3 + iconSize); + img3.drawAt(pad, pad + 6 + iconSize*2); + img4.drawAt(pad, pad + 9 + iconSize*3); + img5.drawAt(pad, pad + 12 + iconSize*4); + + // draw some text + nvg.beginFrame(this); + + nvg.fontSize(23.0f); + nvg.textAlign(NanoVG::ALIGN_LEFT|NanoVG::ALIGN_TOP); + //nvg.textLineHeight(20.0f); + + nvg.fillColor(220,220,220,220); + nvg.textBox(10, 420, iconSize, "Haha,", nullptr); + nvg.textBox(15, 440, iconSize, "Look!", nullptr); + + nvg.endFrame(); +#endif + } + + bool onMouse(const MouseEvent& ev) override + { + if (ev.button != 1 || ! ev.press) + return false; + if (! contains(ev.pos)) + return false; + + const int iconSize = bgIcon.getWidth(); + + for (int i=0; icurPageChanged(i); + repaint(); + break; + } + } + + return true; + } + + bool onMotion(const MotionEvent& ev) override + { + if (contains(ev.pos)) + { + const int iconSize = bgIcon.getWidth(); + + for (int i=0; i bgIcon; -// Line lineSep; -// Image img1, img2, img3, img4, img5; + Rectangle bgIcon; + Line lineSep; +#if 0 + Image img1, img2, img3, img4, img5; // for text -// NanoVG nvg; -// NanoVG::FontId font; + NanoVG nvg;D + NanoVG::FontId font; +#endif }; // ------------------------------------------------------ @@ -94,13 +233,11 @@ class DemoWindow : public StandaloneWindow, { static const int kSidebarWidth = 81; - ExampleColorWidget wColor; - Widget* curWidget; - public: DemoWindow(Application& app) : StandaloneWindow(app), wColor(this), + wLeft(this, this), curWidget(nullptr) { wColor.hide(); @@ -115,7 +252,7 @@ public: // wRects.setAbsoluteX(kSidebarWidth); // wShapes.setAbsoluteX(kSidebarWidth); // wText.setAbsoluteX(kSidebarWidth); -// wLeft.setAbsolutePos(2, 2); + wLeft.setAbsolutePos(2, 2); // wPerf.setAbsoluteY(5); setSize(600, 500); @@ -156,6 +293,15 @@ protected: curWidget->show(); } + void onDisplay() override + { + static int counter = 0; + printf("print %i\n", ++counter); + + glColor3f(0.471f, 0.971f, 0.171f); + Rectangle(0, 0, getSize()).draw(); + } + void onReshape(uint width, uint height) override { StandaloneWindow::onReshape(width, height); @@ -170,12 +316,24 @@ protected: // wShapes.setSize(size); // wText.setSize(size); -// wLeft.setSize(kSidebarWidth-4, height-4); -// //wRezHandle.setAbsoluteX(width-wRezHandle.getWidth()); -// //wRezHandle.setAbsoluteY(height-wRezHandle.getHeight()); -// + wLeft.setSize(kSidebarWidth-4, height-4); + //wRezHandle.setAbsoluteX(width-wRezHandle.getWidth()); + //wRezHandle.setAbsoluteY(height-wRezHandle.getHeight()); + // wPerf.setAbsoluteX(width-wPerf.getWidth()-5); } + +private: + ExampleColorWidget wColor; +// ExampleImagesWidget wImages; +// ExampleRectanglesWidget wRects; +// ExampleShapesWidget wShapes; +// ExampleTextWidget wText; + LeftSideWidget wLeft; + //ResizeHandle wRezHandle; +// NanoPerfWidget wPerf; + + Widget* curWidget; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index f5b913b7..ab4f22e8 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -41,7 +41,7 @@ public: : SubWidget(topWidget), cur('r'), reverse(false), - r(0), g(0), b(0) + r(0), g(99), b(32) { setSize(300, 300); From 467f9199fcb73e54d94998f9d0cfb8230a39bff4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 8 May 2021 22:47:38 +0100 Subject: [PATCH 044/159] Add SubWidget area calls, rename some vars, cleanup repaint Signed-off-by: falkTX --- dgl/Geometry.hpp | 2 + dgl/StandaloneWindow.hpp | 1 + dgl/SubWidget.hpp | 20 +++- dgl/TopLevelWidget.hpp | 8 ++ dgl/Widget.hpp | 6 +- dgl/src/Geometry.cpp | 6 ++ dgl/src/SubWidget.cpp | 30 +++++- dgl/src/SubWidgetPrivateData.cpp | 8 +- dgl/src/SubWidgetPrivateData.hpp | 4 +- dgl/src/TopLevelWidget.cpp | 15 +++ dgl/src/Widget.cpp | 13 ++- dgl/src/WidgetPrivateData.cpp | 20 ++-- dgl/src/WidgetPrivateData.hpp | 6 +- dgl/src/Window.cpp | 2 +- dgl/src/WindowPrivateData.cpp | 53 ++++++++--- dgl/src/WindowPrivateData.hpp | 8 ++ tests/Demo.cpp | 108 ++++++++++++++++------ tests/Makefile | 9 ++ tests/tests.hpp | 8 +- tests/widgets/ExampleColorWidget.hpp | 35 ++++++- tests/widgets/ExampleRectanglesWidget.hpp | 80 +++++++++------- tests/widgets/ExampleShapesWidget.hpp | 48 ++++++---- 22 files changed, 355 insertions(+), 135 deletions(-) diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index e922d683..9b90ae7c 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -210,6 +210,8 @@ public: */ bool isInvalid() const noexcept; + Size toInt() const noexcept; + Size operator+(const Size& size) noexcept; Size operator-(const Size& size) noexcept; Size& operator=(const Size& size) noexcept; diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index a788dfb0..85bfd4b7 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -45,6 +45,7 @@ public: uint getWidth() const noexcept { return Window::getWidth(); } uint getHeight() const noexcept { return Window::getHeight(); } const Size getSize() const noexcept { return Window::getSize(); } + void repaint() noexcept { Window::repaint(); } /** Overloaded functions to ensure size changes apply on both TopLevelWidget and Window classes. diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 631be76a..6cbe49af 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -42,7 +42,7 @@ public: /** Constructor. */ - explicit SubWidget(Widget* widgetToGroupTo); + explicit SubWidget(Widget* parentWidget); /** Destructor. @@ -76,6 +76,19 @@ public: */ Point getAbsolutePos() const noexcept; + /** + Get absolute area of this subwidget. + This is the same as `Rectangle(getAbsolutePos(), getSize());` + @see getConstrainedAbsoluteArea() + */ + Rectangle getAbsoluteArea() const noexcept; + + /** + Get absolute area of this subwidget, with special consideration for not allowing negative values. + @see getAbsoluteArea() + */ + Rectangle getConstrainedAbsoluteArea() const noexcept; + /** Set absolute X. */ @@ -96,6 +109,11 @@ public: */ void setAbsolutePos(const Point& pos) noexcept; + /** + Request repaint of this subwidget's area to the window this widget belongs to. + */ + void repaint() noexcept override; + protected: /** A function called when the subwidget's absolute position is changed. diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 214d2888..f3c02a10 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -50,6 +50,14 @@ public: */ virtual ~TopLevelWidget(); + /** + Get the application associated with this top-level widget's window. + */ + Application& getApp() const noexcept; + + void repaint() noexcept; + void repaint(const Rectangle& rect) noexcept; + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index b1a590f7..3f268edd 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -169,10 +169,10 @@ public: TopLevelWidget* getTopLevelWidget() const noexcept; /** - Tell this widget's window to repaint itself. - FIXME better description, partial redraw + Request repaint of this widget's area to the window this widget belongs to. + On the raw Widget class this function does nothing. */ - void repaint() noexcept; + virtual void repaint() noexcept; protected: /** diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index 01af9620..1df6e9b7 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -246,6 +246,12 @@ bool Size::isInvalid() const noexcept return fWidth <= 0 || fHeight <= 0; } +template +Size Size::toInt() const noexcept +{ + return Size(fWidth, fHeight); +} + template Size Size::operator+(const Size& size) noexcept { diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index b3704f6a..05c5476a 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -15,14 +15,15 @@ */ #include "SubWidgetPrivateData.hpp" +#include "../TopLevelWidget.hpp" START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- -SubWidget::SubWidget(Widget* const widgetToGroupTo) - : Widget(widgetToGroupTo), - pData(new PrivateData(this, widgetToGroupTo)) {} +SubWidget::SubWidget(Widget* const parentWidget) + : Widget(parentWidget), + pData(new PrivateData(this, parentWidget)) {} SubWidget::~SubWidget() { @@ -56,6 +57,18 @@ Point SubWidget::getAbsolutePos() const noexcept return pData->absolutePos; } +Rectangle SubWidget::getAbsoluteArea() const noexcept +{ + return Rectangle(getAbsolutePos(), getSize().toInt()); +} + +Rectangle SubWidget::getConstrainedAbsoluteArea() const noexcept +{ + return Rectangle(std::max(0, getAbsoluteX()), + std::max(0, getAbsoluteY()), + getSize()); +} + void SubWidget::setAbsoluteX(int x) noexcept { setAbsolutePos(Point(x, getAbsoluteY())); @@ -83,8 +96,15 @@ void SubWidget::setAbsolutePos(const Point& pos) noexcept pData->absolutePos = pos; onPositionChanged(ev); - // repaint the whole thing - pData->parent->repaint(); + // repaint the bounds of parent + pData->parentWidget->repaint(); +} + + +void SubWidget::repaint() noexcept +{ + if (TopLevelWidget* const topw = getTopLevelWidget()) + topw->repaint(getConstrainedAbsoluteArea()); } void SubWidget::onPositionChanged(const PositionChangedEvent&) diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp index 17d1c3e0..98133fa9 100644 --- a/dgl/src/SubWidgetPrivateData.cpp +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -21,17 +21,17 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- -SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const p) +SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) : self(s), - parent(p), + parentWidget(pw), absolutePos() { - parent->pData->subWidgets.push_back(self); + parentWidget->pData->subWidgets.push_back(self); } SubWidget::PrivateData::~PrivateData() { - parent->pData->subWidgets.remove(self); + parentWidget->pData->subWidgets.remove(self); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index 3f1a348b..699bcac0 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -25,10 +25,10 @@ START_NAMESPACE_DGL struct SubWidget::PrivateData { SubWidget* const self; - Widget* const parent; + Widget* const parentWidget; Point absolutePos; - explicit PrivateData(SubWidget* const s, Widget* const p); + explicit PrivateData(SubWidget* const s, Widget* const pw); ~PrivateData(); // NOTE display function is different depending on build type, must call displaySubWidgets at the end diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 8dda54a3..aa3bede9 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -29,6 +29,21 @@ TopLevelWidget::~TopLevelWidget() delete pData; } +Application& TopLevelWidget::getApp() const noexcept +{ + return pData->window.getApp(); +} + +void TopLevelWidget::repaint() noexcept +{ + pData->window.repaint(); +} + +void TopLevelWidget::repaint(const Rectangle& rect) noexcept +{ + pData->window.repaint(rect); +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index a0cfc5c6..707a31c4 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -15,6 +15,7 @@ */ #include "WidgetPrivateData.hpp" +#include "../TopLevelWidget.hpp" START_NAMESPACE_DGL @@ -24,8 +25,8 @@ START_NAMESPACE_DGL Widget::Widget(TopLevelWidget* const topLevelWidget) : pData(new PrivateData(this, topLevelWidget)) {} -Widget::Widget(Widget* const widgetToGroupTo) - : pData(new PrivateData(this, widgetToGroupTo)) {} +Widget::Widget(Widget* const parentWidget) + : pData(new PrivateData(this, parentWidget)) {} Widget::~Widget() { @@ -83,7 +84,7 @@ void Widget::setWidth(uint width) noexcept pData->size.setWidth(width); onResize(ev); - pData->repaint(); + repaint(); } void Widget::setHeight(uint height) noexcept @@ -98,7 +99,7 @@ void Widget::setHeight(uint height) noexcept pData->size.setHeight(height); onResize(ev); - pData->repaint(); + repaint(); } void Widget::setSize(uint width, uint height) noexcept @@ -118,7 +119,7 @@ void Widget::setSize(const Size& size) noexcept pData->size = size; onResize(ev); - pData->repaint(); + repaint(); } Application& Widget::getApp() const noexcept @@ -133,8 +134,6 @@ TopLevelWidget* Widget::getTopLevelWidget() const noexcept void Widget::repaint() noexcept { - // FIXME partial repaint - // pData->topLevelWidget.repaint(); } uint Widget::getId() const noexcept diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 3cab134e..3a16c1e4 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -24,17 +24,17 @@ START_NAMESPACE_DGL Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) : self(s), topLevelWidget(tlw), - parentGroupWidget(nullptr), + parentWidget(nullptr), id(0), needsScaling(false), visible(true), size(0, 0), subWidgets() {} -Widget::PrivateData::PrivateData(Widget* const s, Widget* const g) +Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) : self(s), - topLevelWidget(findTopLevelWidget(g)), - parentGroupWidget(g), + topLevelWidget(findTopLevelWidget(pw)), + parentWidget(pw), id(0), needsScaling(false), visible(true), @@ -62,14 +62,6 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, } } -void Widget::PrivateData::repaint() -{ -// if (parentGroupWidget != nullptr) -// parentGroupWidget->repaint(); -// else if (topLevelWidget != nullptr) -// topLevelWidget->repaint(); -} - // ----------------------------------------------------------------------- TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) @@ -78,8 +70,8 @@ TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) // return tlw; if (w->pData->topLevelWidget != nullptr) return w->pData->topLevelWidget; - if (w->pData->parentGroupWidget != nullptr) - return findTopLevelWidget(w->pData->parentGroupWidget); + if (w->pData->parentWidget != nullptr) + return findTopLevelWidget(w->pData->parentWidget); return nullptr; } diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 9061548d..5d58721a 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -28,7 +28,7 @@ START_NAMESPACE_DGL struct Widget::PrivateData { Widget* const self; TopLevelWidget* const topLevelWidget; - Widget* const parentGroupWidget; + Widget* const parentWidget; uint id; bool needsScaling; bool visible; @@ -38,7 +38,7 @@ struct Widget::PrivateData { // called via TopLevelWidget explicit PrivateData(Widget* const s, TopLevelWidget* const tlw); // called via SubWidget - explicit PrivateData(Widget* const s, Widget* const pgw); + explicit PrivateData(Widget* const s, Widget* const pw); ~PrivateData(); // NOTE display function is different depending on build type, must call displaySubWidgets at the end @@ -46,8 +46,6 @@ struct Widget::PrivateData { void displaySubWidgets(uint width, uint height, double autoScaling); - void repaint(); - static TopLevelWidget* findTopLevelWidget(Widget* const w); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 22a181d0..b45b2c1a 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -149,7 +149,7 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } -void Window::onReshape(const uint width, const uint height) +void Window::onReshape(uint, uint) { puglFallbackOnResize(pData->view); } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 0f4f8a93..bb7bce36 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -48,7 +48,10 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) topLevelWidget(nullptr), isClosed(true), isVisible(false), - isEmbed(false) + isEmbed(false), + scaling(1.0), + autoScaling(1.0), + pendingVisibility(kPendingVisibilityNone) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } @@ -63,7 +66,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi isVisible(false), isEmbed(false), scaling(1.0), - autoScaling(1.0) + autoScaling(1.0), + pendingVisibility(kPendingVisibilityNone) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -83,7 +87,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), scaling(scale), - autoScaling(1.0) + autoScaling(1.0), + pendingVisibility(kPendingVisibilityNone) { init(width, height, resizable); @@ -132,6 +137,9 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); + puglSetViewHint(view, PUGL_DEPTH_BITS, 8); + puglSetViewHint(view, PUGL_STENCIL_BITS, 8); + // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); // #ifndef DGL_FILE_BROWSER_DISABLED // puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); @@ -174,14 +182,9 @@ void Window::PrivateData::show() isClosed = false; appData->oneWindowShown(); + pendingVisibility = kPendingVisibilityShow; const PuglStatus status = puglRealize(view); DISTRHO_SAFE_ASSERT_INT_RETURN(status == PUGL_SUCCESS, status, close()); - -#ifdef DISTRHO_OS_WINDOWS - puglWin32ShowWindowCentered(view); -#else - puglShow(view); -#endif } else { @@ -197,14 +200,17 @@ void Window::PrivateData::show() void Window::PrivateData::hide() { - if (! isVisible) + if (isEmbed) { - DGL_DBG("Window hide matches current visible state, ignoring request\n"); + DGL_DBG("Window hide cannot be called when embedded\n"); return; } - if (isEmbed) + + pendingVisibility = kPendingVisibilityHide; + + if (! isVisible) { - DGL_DBG("Window hide cannot be called when embedded\n"); + DGL_DBG("Window hide matches current visible state, ignoring request\n"); return; } @@ -262,6 +268,8 @@ void Window::PrivateData::idleCallback() void Window::PrivateData::onPuglDisplay() { + DGL_DBGp("PUGL: onPuglDisplay : %p\n", topLevelWidget); + puglOnDisplayPrepare(view); #ifndef DPF_TEST_WINDOW_CPP @@ -284,6 +292,21 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) #endif } +void Window::PrivateData::onPuglCreate() +{ + DGL_DBGp("PUGL: onPuglCreate %i\n", pendingVisibility); + + if (pendingVisibility != kPendingVisibilityShow) + return; + + pendingVisibility = kPendingVisibilityNone; +#ifdef DISTRHO_OS_WINDOWS + puglWin32ShowWindowCentered(view); +#else + puglShow(view); +#endif +} + void Window::PrivateData::onPuglClose() { DGL_DBG("PUGL: onClose\n"); @@ -328,6 +351,10 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu pData->onPuglClose(); break; + case PUGL_CREATE: + pData->onPuglCreate(); + break; + // TODO default: break; diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index e11c69fd..76a405b4 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -63,6 +63,13 @@ struct Window::PrivateData : IdleCallback { /** Automatic scaling to apply on widgets, implemented internally. */ double autoScaling; + /** Pending state of visility, used for the action to be triggered during Pugl create events. */ + enum PendingVisibility { + kPendingVisibilityNone, + kPendingVisibilityShow, + kPendingVisibilityHide + } pendingVisibility; + /** Constructor for a regular, standalone window. */ explicit PrivateData(Application& app, Window* self); @@ -98,6 +105,7 @@ struct Window::PrivateData : IdleCallback { // pugl events void onPuglDisplay(); void onPuglReshape(int width, int height); + void onPuglCreate(); void onPuglClose(); // Pugl event handling entry point diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 74cdf622..0241e03e 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -37,12 +37,12 @@ #include "dgl/StandaloneWindow.hpp" #include "widgets/ExampleColorWidget.hpp" +#include "widgets/ExampleRectanglesWidget.hpp" +#include "widgets/ExampleShapesWidget.hpp" START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- - -// ------------------------------------------------------ // Left side tab-like widget class LeftSideWidget : public SubWidget @@ -225,8 +225,8 @@ private: #endif }; -// ------------------------------------------------------ -// Our Demo Window +// -------------------------------------------------------------------------------------------------------------------- +// Main Demo Window, having a left-side tab-like widget and main area for current widget class DemoWindow : public StandaloneWindow, public LeftSideWidget::Callback @@ -234,23 +234,27 @@ class DemoWindow : public StandaloneWindow, static const int kSidebarWidth = 81; public: + static constexpr const char* const kExampleWidgetName = "Demo"; + DemoWindow(Application& app) : StandaloneWindow(app), wColor(this), + wRects(this), + wShapes(this), wLeft(this, this), curWidget(nullptr) { wColor.hide(); // wImages.hide(); -// wRects.hide(); -// wShapes.hide(); + wRects.hide(); + wShapes.hide(); // wText.hide(); // //wPerf.hide(); wColor.setAbsoluteX(kSidebarWidth); // wImages.setAbsoluteX(kSidebarWidth); -// wRects.setAbsoluteX(kSidebarWidth); -// wShapes.setAbsoluteX(kSidebarWidth); + wRects.setAbsoluteX(kSidebarWidth); + wShapes.setAbsoluteX(kSidebarWidth); // wText.setAbsoluteX(kSidebarWidth); wLeft.setAbsolutePos(2, 2); // wPerf.setAbsoluteY(5); @@ -265,10 +269,7 @@ protected: void curPageChanged(int curPage) override { if (curWidget != nullptr) - { curWidget->hide(); - curWidget = nullptr; - } switch (curPage) { @@ -278,15 +279,18 @@ protected: // case 1: // curWidget = &wImages; // break; -// case 2: -// curWidget = &wRects; -// break; -// case 3: -// curWidget = &wShapes; -// break; + case 2: + curWidget = &wRects; + break; + case 3: + curWidget = &wShapes; + break; // case 4: // curWidget = &wText; // break; + default: + curWidget = nullptr; + break; } if (curWidget != nullptr) @@ -312,8 +316,8 @@ protected: Size size(width-kSidebarWidth, height); wColor.setSize(size); // wImages.setSize(size); -// wRects.setSize(size); -// wShapes.setSize(size); + wRects.setSize(size); + wShapes.setSize(size); // wText.setSize(size); wLeft.setSize(kSidebarWidth-4, height-4); @@ -324,10 +328,10 @@ protected: } private: - ExampleColorWidget wColor; + ExampleColorSubWidget wColor; // ExampleImagesWidget wImages; -// ExampleRectanglesWidget wRects; -// ExampleShapesWidget wShapes; + ExampleRectanglesSubWidget wRects; + ExampleShapesSubWidget wShapes; // ExampleTextWidget wText; LeftSideWidget wLeft; //ResizeHandle wRezHandle; @@ -336,19 +340,71 @@ private: Widget* curWidget; }; +// -------------------------------------------------------------------------------------------------------------------- +// Testing StandaloneWindow, for custom local widget drawing code + +class TestingWidgetStandaloneWindow : public StandaloneWindow +{ +public: + static constexpr const char* kExampleWidgetName = "Testing"; + + TestingWidgetStandaloneWindow(Application& app) + : StandaloneWindow(app) + { + } + +protected: + void onDisplay() override + { + glColor3f(0.5f, 0.3f, 0.9f); + Rectangle(0, 0, 500, 500).draw(); + // getWidth(), getHeight() + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// Special handy function that runs a StandaloneWindow inside the function scope + +template +void createAndShowExampleWidgetStandaloneWindow(Application& app) +{ + ExampleWidgetStandaloneWindow swin(app); + swin.setSize(600, 500); + swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); + swin.show(); + app.exec(); +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL -int main() +int main(int argc, char* argv[]) { USE_NAMESPACE_DGL; + using DGL_NAMESPACE::Window; Application app; - DemoWindow win(app); - win.show(); - app.exec(); + if (argc > 1) + { + // TODO images, text + + /**/ if (std::strcmp(argv[1], "color") == 0) + createAndShowExampleWidgetStandaloneWindow(app); + else if (std::strcmp(argv[1], "rectangles") == 0) + createAndShowExampleWidgetStandaloneWindow(app); + else if (std::strcmp(argv[1], "shapes") == 0) + createAndShowExampleWidgetStandaloneWindow(app); + else if (std::strcmp(argv[1], "testing") == 0) + createAndShowExampleWidgetStandaloneWindow(app); + else + d_stderr2("Invalid demo mode, must be one of: color, rectangles, shapes"); + } + else + { + createAndShowExampleWidgetStandaloneWindow(app); + } return 0; } diff --git a/tests/Makefile b/tests/Makefile index 93c6e6c5..71ab42e7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -93,6 +93,11 @@ clean: @echo "Compiling $< (OpenGL)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ +../build/tests/Testing.cpp.o: Testing.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (OpenGL)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ + ../build/tests/%.cpp.opengl.o: %.cpp -@mkdir -p ../build/tests @echo "Compiling $< (OpenGL)" @@ -118,6 +123,10 @@ clean: @echo "Linking Demo" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ +../build/tests/Testing: ../build/tests/Testing.cpp.o + @echo "Linking Testing" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + ../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ diff --git a/tests/tests.hpp b/tests/tests.hpp index 37bdd6f1..466141f7 100644 --- a/tests/tests.hpp +++ b/tests/tests.hpp @@ -31,11 +31,13 @@ START_NAMESPACE_DGL class ApplicationQuitter : public Thread { Application& app; + const int numSecondsToWait; public: - ApplicationQuitter(Application& a) + ApplicationQuitter(Application& a, const int s = 2) : Thread("ApplicationQuitter"), - app(a) + app(a), + numSecondsToWait(s) { startThread(); } @@ -43,7 +45,7 @@ public: private: void run() override { - d_sleep(2); + d_sleep(numSecondsToWait); app.quit(); } }; diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index ab4f22e8..07432212 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -21,13 +21,15 @@ // DGL Stuff #include "../../dgl/SubWidget.hpp" +#include "../../dgl/TopLevelWidget.hpp" START_NAMESPACE_DGL // ------------------------------------------------------ // our widget -class ExampleColorWidget : public SubWidget, +template +class ExampleColorWidget : public BaseWidget, public IdleCallback { char cur; @@ -37,13 +39,32 @@ class ExampleColorWidget : public SubWidget, Rectangle bgFull, bgSmall; public: - ExampleColorWidget(TopLevelWidget* const topWidget) - : SubWidget(topWidget), + static constexpr const char* kExampleWidgetName = "Color"; + + explicit ExampleColorWidget(Widget* const parent) + : BaseWidget(parent), cur('r'), reverse(false), r(0), g(99), b(32) { - setSize(300, 300); + init(); + } + + explicit ExampleColorWidget(Window& windowToMapTo) + : BaseWidget(windowToMapTo) + { + init(); + } + + explicit ExampleColorWidget(Application& app) + : BaseWidget(app) + { + init(); + } + + void init() + { + BaseWidget::setSize(300, 300); // topWidget->getApp().addIdleCallback(this); } @@ -99,7 +120,7 @@ protected: break; } - repaint(); + BaseWidget::repaint(); } void onDisplay() override @@ -126,6 +147,10 @@ protected: } }; +typedef ExampleColorWidget ExampleColorSubWidget; +typedef ExampleColorWidget ExampleColorTopLevelWidget; +typedef ExampleColorWidget ExampleColorStandaloneWindow; + // ------------------------------------------------------ END_NAMESPACE_DGL diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index 8f705cad..d8b2960a 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2015 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,40 +20,55 @@ // ------------------------------------------------------ // DGL Stuff -#include "Widget.hpp" -#include "Window.hpp" +#include "../../dgl/SubWidget.hpp" +#include "../../dgl/TopLevelWidget.hpp" + +START_NAMESPACE_DGL // ------------------------------------------------------ // our widget -class ExampleRectanglesWidget : public Widget +template +class ExampleRectanglesWidget : public BaseWidget { + bool clicked[9]; + public: - ExampleRectanglesWidget(Window& parent) - : Widget(parent) + static constexpr const char* const kExampleWidgetName = "Rectangles"; + + explicit ExampleRectanglesWidget(Widget* const parentWidget) + : BaseWidget(parentWidget) { - setSize(300, 300); + init(); + } - for (int i=0; i<9; ++i) - fClicked[i] = false; + explicit ExampleRectanglesWidget(Window& windowToMapTo) + : BaseWidget(windowToMapTo) + { + init(); } - ExampleRectanglesWidget(Widget* groupWidget) - : Widget(groupWidget) + explicit ExampleRectanglesWidget(Application& app) + : BaseWidget(app) { - setSize(300, 300); + init(); + } + + void init() + { + this->setSize(300, 300); for (int i=0; i<9; ++i) - fClicked[i] = false; + clicked[i] = false; } protected: void onDisplay() override { - const int width = getWidth(); - const int height = getHeight(); + const uint width = this->getWidth(); + const uint height = this->getHeight(); - Rectangle r; + Rectangle r; r.setWidth(width/3 - 6); r.setHeight(height/3 - 6); @@ -66,7 +81,7 @@ protected: // 1st r.setY(3); - if (fClicked[0+i]) + if (clicked[0+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); @@ -76,7 +91,7 @@ protected: // 2nd r.setY(3 + height/3); - if (fClicked[3+i]) + if (clicked[3+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); @@ -86,7 +101,7 @@ protected: // 3rd r.setY(3 + height*2/3); - if (fClicked[6+i]) + if (clicked[6+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); @@ -100,10 +115,10 @@ protected: if (ev.button != 1 || ! ev.press) return false; - const int width = getWidth(); - const int height = getHeight(); + const uint width = this->getWidth(); + const uint height = this->getHeight(); - Rectangle r; + Rectangle r; r.setWidth(width/3 - 6); r.setHeight(height/3 - 6); @@ -118,8 +133,8 @@ protected: if (r.contains(ev.pos)) { - fClicked[0+i] = !fClicked[0+i]; - repaint(); + clicked[0+i] = !clicked[0+i]; + this->repaint(); break; } @@ -128,8 +143,8 @@ protected: if (r.contains(ev.pos)) { - fClicked[3+i] = !fClicked[3+i]; - repaint(); + clicked[3+i] = !clicked[3+i]; + this->repaint(); break; } @@ -138,19 +153,22 @@ protected: if (r.contains(ev.pos)) { - fClicked[6+i] = !fClicked[6+i]; - repaint(); + clicked[6+i] = !clicked[6+i]; + this->repaint(); break; } } return true; } - -private: - bool fClicked[9]; }; +typedef ExampleRectanglesWidget ExampleRectanglesSubWidget; +typedef ExampleRectanglesWidget ExampleRectanglesTopLevelWidget; +typedef ExampleRectanglesWidget ExampleRectanglesStandaloneWindow; + // ------------------------------------------------------ +END_NAMESPACE_DGL + #endif // EXAMPLE_RECTANGLES_WIDGET_HPP_INCLUDED diff --git a/tests/widgets/ExampleShapesWidget.hpp b/tests/widgets/ExampleShapesWidget.hpp index 967bc439..c0298e07 100644 --- a/tests/widgets/ExampleShapesWidget.hpp +++ b/tests/widgets/ExampleShapesWidget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2015 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,25 +20,41 @@ // ------------------------------------------------------ // DGL Stuff -#include "Widget.hpp" -#include "Window.hpp" +#include "../../dgl/SubWidget.hpp" +#include "../../dgl/TopLevelWidget.hpp" + +START_NAMESPACE_DGL // ------------------------------------------------------ // our widget -class ExampleShapesWidget : public Widget +template +class ExampleShapesWidget : public BaseWidget { + Rectangle bg; + Rectangle rect; + Triangle tri; + Circle cir; + public: - ExampleShapesWidget(Window& parent) - : Widget(parent) + static constexpr const char* const kExampleWidgetName = "Shapes"; + + explicit ExampleShapesWidget(Widget* const parentWidget) + : BaseWidget(parentWidget) { - setSize(300, 300); + this->setSize(300, 300); } - ExampleShapesWidget(Widget* groupWidget) - : Widget(groupWidget) + explicit ExampleShapesWidget(Window& windowToMapTo) + : BaseWidget(windowToMapTo) { - setSize(300, 300); + this->setSize(300, 300); + } + + explicit ExampleShapesWidget(Application& app) + : BaseWidget(app) + { + this->setSize(300, 300); } protected: @@ -95,14 +111,14 @@ protected: // circle cir = Circle(width/2, height*2/3, height/6, 300); } - -private: - Rectangle bg; - Rectangle rect; - Triangle tri; - Circle cir; }; +typedef ExampleShapesWidget ExampleShapesSubWidget; +typedef ExampleShapesWidget ExampleShapesTopLevelWidget; +typedef ExampleShapesWidget ExampleShapesStandaloneWindow; + // ------------------------------------------------------ +END_NAMESPACE_DGL + #endif // EXAMPLE_SHAPES_WIDGET_HPP_INCLUDED From 3f3ac855ada80c62ba4f5aed9a0d7593e063dfd2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 8 May 2021 23:08:07 +0100 Subject: [PATCH 045/159] Fix ExampleColorWidget Signed-off-by: falkTX --- tests/widgets/ExampleColorWidget.hpp | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index 07432212..a2379aad 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -41,32 +41,37 @@ class ExampleColorWidget : public BaseWidget, public: static constexpr const char* kExampleWidgetName = "Color"; + // SubWidget explicit ExampleColorWidget(Widget* const parent) : BaseWidget(parent), cur('r'), reverse(false), - r(0), g(99), b(32) + r(0), g(0), b(0) { - init(); + BaseWidget::setSize(300, 300); + parent->getApp().addIdleCallback(this); } + // TopLevelWidget explicit ExampleColorWidget(Window& windowToMapTo) - : BaseWidget(windowToMapTo) + : BaseWidget(windowToMapTo), + cur('r'), + reverse(false), + r(0), g(0), b(0) { - init(); + BaseWidget::setSize(300, 300); + windowToMapTo.getApp().addIdleCallback(this); } + // StandaloneWindow explicit ExampleColorWidget(Application& app) - : BaseWidget(app) - { - init(); - } - - void init() + : BaseWidget(app), + cur('r'), + reverse(false), + r(0), g(0), b(0) { BaseWidget::setSize(300, 300); - -// topWidget->getApp().addIdleCallback(this); + app.addIdleCallback(this); } protected: From d3c5705122bed1ac4283fbbb9a7631d40dc1fe66 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 May 2021 00:05:18 +0100 Subject: [PATCH 046/159] Start to pass events into top-level and subwidgets Signed-off-by: falkTX --- dgl/src/SubWidget.cpp | 4 ++- dgl/src/TopLevelWidgetPrivateData.cpp | 29 +++++++++++----- dgl/src/TopLevelWidgetPrivateData.hpp | 1 + dgl/src/WidgetPrivateData.cpp | 40 +++++++++++++++++++---- dgl/src/WidgetPrivateData.hpp | 4 +-- dgl/src/WindowPrivateData.cpp | 27 +++++++++++++++ dgl/src/WindowPrivateData.hpp | 2 ++ tests/Demo.cpp | 34 ++----------------- tests/widgets/ExampleRectanglesWidget.hpp | 16 ++++----- 9 files changed, 98 insertions(+), 59 deletions(-) diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 05c5476a..b694dd33 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -100,9 +100,11 @@ void SubWidget::setAbsolutePos(const Point& pos) noexcept pData->parentWidget->repaint(); } - void SubWidget::repaint() noexcept { + if (! isVisible()) + return; + if (TopLevelWidget* const topw = getTopLevelWidget()) topw->repaint(getConstrainedAbsoluteArea()); } diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 4a1cd1ab..c8fa5f28 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -21,12 +21,6 @@ START_NAMESPACE_DGL -#define FOR_EACH_WIDGET(it) \ - for (std::list::iterator it = widgets.begin(); it != widgets.end(); ++it) - -#define FOR_EACH_WIDGET_INV(rit) \ - for (std::list::reverse_iterator rit = widgets.rbegin(); rit != widgets.rend(); ++rit) - // ----------------------------------------------------------------------- TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) @@ -45,13 +39,10 @@ TopLevelWidget::PrivateData::~PrivateData() void TopLevelWidget::PrivateData::display() { - printf("TopLevelWidget::PrivateData::display INIT\n"); - const Size size(window.getSize()); const uint width = size.getWidth(); const uint height = size.getHeight(); const double autoScaling = window.pData->autoScaling; - printf("TopLevelWidget::PrivateData::display %i %i\n", width, height); // full viewport size glViewport(0, -(height * autoScaling - height), width * autoScaling, height * autoScaling); @@ -63,6 +54,26 @@ void TopLevelWidget::PrivateData::display() selfw->pData->displaySubWidgets(width, height, autoScaling); } +void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) +{ + Events::MouseEvent rev = ev; + + const double autoScaling = window.pData->autoScaling; + + if (autoScaling != 1.0) + { + rev.pos.setX(ev.pos.getX() / autoScaling); + rev.pos.setY(ev.pos.getY() / autoScaling); + } + + // give top-level widget chance to catch this event first + if (self->onMouse(ev)) + return; + + // propagate event to all subwidgets recursively + selfw->pData->giveMouseEventForSubWidgets(rev); +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index 5e3b1d89..4e9f597e 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -33,6 +33,7 @@ struct TopLevelWidget::PrivateData { explicit PrivateData(TopLevelWidget* const s, Window& w); ~PrivateData(); void display(); + void mouseEvent(const Events::MouseEvent& ev); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 3a16c1e4..f163d47a 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -15,10 +15,17 @@ */ #include "WidgetPrivateData.hpp" +#include "../SubWidget.hpp" #include "../TopLevelWidget.hpp" START_NAMESPACE_DGL +#define FOR_EACH_SUBWIDGET(it) \ + for (std::list::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) + +#define FOR_EACH_SUBWIDGET_INV(rit) \ + for (std::list::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit) + // ----------------------------------------------------------------------- Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) @@ -48,17 +55,40 @@ Widget::PrivateData::~PrivateData() void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double scaling) { - printf("Widget::PrivateData::displaySubWidgets INIT | %lu\n", subWidgets.size()); - if (subWidgets.size() == 0) return; for (std::list::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) { SubWidget* const subwidget(*it); - printf("Widget::PrivateData::displaySubWidgets %i %i -> %p\n", width, height, subwidget); - subwidget->pData->display(width, height, scaling); + if (subwidget->isVisible()) + subwidget->pData->display(width, height, scaling); + } +} + +void Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) +{ + if (! visible) + return; + if (subWidgets.size() == 0) + return; + + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (! widget->isVisible()) + continue; + + ev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->onMouse(ev)) + return; } } @@ -66,8 +96,6 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) { -// if (TopLevelWidget* const tlw = dynamic_cast(w)) -// return tlw; if (w->pData->topLevelWidget != nullptr) return w->pData->topLevelWidget; if (w->pData->parentWidget != nullptr) diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 5d58721a..fa2e6f87 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -41,10 +41,8 @@ struct Widget::PrivateData { explicit PrivateData(Widget* const s, Widget* const pw); ~PrivateData(); - // NOTE display function is different depending on build type, must call displaySubWidgets at the end -// void display(uint width, uint height, double autoScaling, bool renderingSubWidget); - void displaySubWidgets(uint width, uint height, double autoScaling); + void giveMouseEventForSubWidgets(Events::MouseEvent& ev); static TopLevelWidget* findTopLevelWidget(Widget* const w); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index bb7bce36..560d4c81 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -323,6 +323,19 @@ void Window::PrivateData::onPuglClose() close(); } +void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) +{ + DGL_DBGp("PUGL: onMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->mouseEvent(ev); +#endif +} + static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) @@ -355,6 +368,20 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu pData->onPuglCreate(); break; + case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton + case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton + { + Events::MouseEvent ev; + ev.mod = event->button.state; + ev.flags = event->button.flags; + ev.time = static_cast(event->button.time * 1000.0 + 0.5); + ev.button = event->button.button; + ev.press = event->type == PUGL_BUTTON_PRESS; + ev.pos = Point(event->button.x, event->button.y); + pData->onPuglMouse(ev); + break; + } + // TODO default: break; diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 76a405b4..b87a2718 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,6 +18,7 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" +#include "../Events.hpp" #include "ApplicationPrivateData.hpp" #include "pugl.hpp" @@ -107,6 +108,7 @@ struct Window::PrivateData : IdleCallback { void onPuglReshape(int width, int height); void onPuglCreate(); void onPuglClose(); + void onPuglMouse(const Events::MouseEvent& ev); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 0241e03e..99062ed2 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -80,7 +80,6 @@ protected: void onDisplay() override { const int iconSize = bgIcon.getWidth(); - printf("LEFT SIDE WIDGET onDisplay %i\n", iconSize); glColor3f(0.027f, 0.027f, 0.027f); Rectangle(0, 0, getSize()).draw(); @@ -201,8 +200,8 @@ protected: void onResize(const ResizeEvent& ev) override { - const int width = ev.size.getWidth(); - const int height = ev.size.getHeight(); + const uint width = ev.size.getWidth(); + const uint height = ev.size.getHeight(); bgIcon.setWidth(width-4); bgIcon.setHeight(width-4); @@ -299,11 +298,6 @@ protected: void onDisplay() override { - static int counter = 0; - printf("print %i\n", ++counter); - - glColor3f(0.471f, 0.971f, 0.171f); - Rectangle(0, 0, getSize()).draw(); } void onReshape(uint width, uint height) override @@ -340,28 +334,6 @@ private: Widget* curWidget; }; -// -------------------------------------------------------------------------------------------------------------------- -// Testing StandaloneWindow, for custom local widget drawing code - -class TestingWidgetStandaloneWindow : public StandaloneWindow -{ -public: - static constexpr const char* kExampleWidgetName = "Testing"; - - TestingWidgetStandaloneWindow(Application& app) - : StandaloneWindow(app) - { - } - -protected: - void onDisplay() override - { - glColor3f(0.5f, 0.3f, 0.9f); - Rectangle(0, 0, 500, 500).draw(); - // getWidth(), getHeight() - } -}; - // -------------------------------------------------------------------------------------------------------------------- // Special handy function that runs a StandaloneWindow inside the function scope @@ -396,8 +368,6 @@ int main(int argc, char* argv[]) createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "shapes") == 0) createAndShowExampleWidgetStandaloneWindow(app); - else if (std::strcmp(argv[1], "testing") == 0) - createAndShowExampleWidgetStandaloneWindow(app); else d_stderr2("Invalid demo mode, must be one of: color, rectangles, shapes"); } diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index d8b2960a..425dade2 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -56,7 +56,7 @@ public: void init() { - this->setSize(300, 300); + BaseWidget::setSize(300, 300); for (int i=0; i<9; ++i) clicked[i] = false; @@ -65,8 +65,8 @@ public: protected: void onDisplay() override { - const uint width = this->getWidth(); - const uint height = this->getHeight(); + const uint width = BaseWidget::getWidth(); + const uint height = BaseWidget::getHeight(); Rectangle r; @@ -115,8 +115,8 @@ protected: if (ev.button != 1 || ! ev.press) return false; - const uint width = this->getWidth(); - const uint height = this->getHeight(); + const uint width = BaseWidget::getWidth(); + const uint height = BaseWidget::getHeight(); Rectangle r; @@ -134,7 +134,7 @@ protected: if (r.contains(ev.pos)) { clicked[0+i] = !clicked[0+i]; - this->repaint(); + BaseWidget::repaint(); break; } @@ -144,7 +144,7 @@ protected: if (r.contains(ev.pos)) { clicked[3+i] = !clicked[3+i]; - this->repaint(); + BaseWidget::repaint(); break; } @@ -154,7 +154,7 @@ protected: if (r.contains(ev.pos)) { clicked[6+i] = !clicked[6+i]; - this->repaint(); + BaseWidget::repaint(); break; } } From 1acbd1acb0771077326ee68cc16c0145a130fcc8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 May 2021 00:29:50 +0100 Subject: [PATCH 047/159] Fix build Signed-off-by: falkTX --- dgl/src/TopLevelWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index aa3bede9..2c9f1c02 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -15,6 +15,7 @@ */ #include "TopLevelWidgetPrivateData.hpp" +#include "../Window.hpp" START_NAMESPACE_DGL From c10f6b543833019372d265d0220c1cedbf62fd37 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 May 2021 15:05:22 +0100 Subject: [PATCH 048/159] Make images work again, make Image typedef of OpenGLImage Signed-off-by: falkTX --- dgl/Image.hpp | 101 +------------ dgl/ImageBase.hpp | 30 ++-- dgl/OpenGL.hpp | 129 +++++++++++++++++ dgl/src/Image.cpp | 150 ------------------- dgl/src/ImageBase.cpp | 71 ++++----- dgl/src/OpenGL.cpp | 179 +++++++++++++++++++++++ tests/Demo.cpp | 30 ++-- tests/widgets/ExampleImagesWidget.hpp | 198 ++++++++++++++++---------- 8 files changed, 506 insertions(+), 382 deletions(-) delete mode 100644 dgl/src/Image.cpp diff --git a/dgl/Image.hpp b/dgl/Image.hpp index 634665ff..138a9147 100644 --- a/dgl/Image.hpp +++ b/dgl/Image.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -22,103 +22,8 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- - -/** - OpenGL Image class. - - This is an Image class that handles raw image data in pixels. - You can init the image data on the contructor or later on by calling loadFromMemory(). - - To generate raw data useful for this class see the utils/png2rgba.py script. - Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR' - instead of the default 'GL_BGRA'. - - Images are drawn on screen via 2D textures. - */ -class Image : public ImageBase -{ -public: - /** - Constructor for a null Image. - */ - Image(); - - /** - Constructor using raw image data. - @note @a rawData must remain valid for the lifetime of this Image. - */ - Image(const char* const rawData, - const uint width, - const uint height, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE); - - /** - Constructor using raw image data. - @note @a rawData must remain valid for the lifetime of this Image. - */ - Image(const char* const rawData, - const Size& size, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE); - - /** - Constructor using another image data. - */ - Image(const Image& image); - - /** - Destructor. - */ - ~Image() override; - - /** - Load image data from memory. - @note @a rawData must remain valid for the lifetime of this Image. - */ - void loadFromMemory(const char* const rawData, - const uint width, - const uint height, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE) noexcept; - - /** - Load image data from memory. - @note @a rawData must remain valid for the lifetime of this Image. - */ - void loadFromMemory(const char* const rawData, - const Size& size, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE) noexcept; - - /** - Get the image format. - */ - GLenum getFormat() const noexcept; - - /** - Get the image type. - */ - GLenum getType() const noexcept; - - /** - TODO document this. - */ - Image& operator=(const Image& image) noexcept; - -protected: - /** @internal */ - void _drawAt(const Point& pos) override; - -private: - GLenum fFormat; - GLenum fType; - GLuint fTextureId; - bool fIsReady; -}; - -// ----------------------------------------------------------------------- +// TODO mark as deprecated +typedef OpenGLImage Image; END_NAMESPACE_DGL diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp index 0a7ee780..8f71aa42 100644 --- a/dgl/ImageBase.hpp +++ b/dgl/ImageBase.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -21,7 +21,7 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** Base DGL Image class. @@ -68,6 +68,11 @@ public: */ bool isValid() const noexcept; + /** + Check if this image is not valid. + */ + bool isInvalid() const noexcept; + /** Get width. */ @@ -89,19 +94,19 @@ public: const char* getRawData() const noexcept; /** - Draw this image at (0, 0) point. + Draw this image at (0, 0) point using the current OpenGL context. */ - void draw(); + void draw(const GraphicsContext& context); /** - Draw this image at (x, y) point. + Draw this image at (x, y) point using the current OpenGL context. */ - void drawAt(const int x, const int y); + void drawAt(const GraphicsContext& context, const int x, const int y); /** - Draw this image at position @a pos. + Draw this image at position @a pos using the current OpenGL context. */ - void drawAt(const Point& pos); + virtual void drawAt(const GraphicsContext& context, const Point& pos) = 0; /** TODO document this. @@ -111,14 +116,11 @@ public: bool operator!=(const ImageBase& image) const noexcept; protected: - /** @internal */ - virtual void _drawAt(const Point& pos) = 0; - - const char* fRawData; - Size fSize; + const char* rawData; + Size size; }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 622ec89d..db1e0729 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -116,6 +116,135 @@ struct OpenGLGraphicsContext : GraphicsContext // ----------------------------------------------------------------------- +/** + OpenGL Image class. + + This is an Image class that handles raw image data in pixels. + You can init the image data on the contructor or later on by calling loadFromMemory(). + + To generate raw data useful for this class see the utils/png2rgba.py script. + Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR' + instead of the default 'GL_BGRA'. + + Images are drawn on screen via 2D textures. + */ +class OpenGLImage : public ImageBase +{ +public: + /** + Constructor for a null Image. + */ + OpenGLImage(); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + OpenGLImage(const char* const rawData, + const uint width, + const uint height, + const GLenum format = GL_BGRA, + const GLenum type = GL_UNSIGNED_BYTE); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + OpenGLImage(const char* const rawData, + const Size& size, + const GLenum format = GL_BGRA, + const GLenum type = GL_UNSIGNED_BYTE); + + /** + Constructor using another image data. + */ + OpenGLImage(const OpenGLImage& image); + + /** + Destructor. + */ + ~OpenGLImage() override; + + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + void loadFromMemory(const char* const rawData, + const uint width, + const uint height, + const GLenum format = GL_BGRA, + const GLenum type = GL_UNSIGNED_BYTE) noexcept; + + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + void loadFromMemory(const char* const rawData, + const Size& size, + const GLenum format = GL_BGRA, + const GLenum type = GL_UNSIGNED_BYTE) noexcept; + + /** + TODO document this. + */ + void setup(); + + /** + TODO document this. + */ + void cleanup(); + + /** + Get the image format. + */ + GLenum getFormat() const noexcept; + + /** + Get the image type. + */ + GLenum getType() const noexcept; + + /** + Draw this image at position @a pos using the graphics context @a context. + */ + void drawAt(const GraphicsContext& context, const Point& pos) override; + + /** + TODO document this. + */ + OpenGLImage& operator=(const OpenGLImage& image) noexcept; + + /** + Draw this image at (0, 0) point using the current OpenGL context. + */ + // TODO mark as deprecated + void draw(); + + /** + Draw this image at (x, y) point using the current OpenGL context. + */ + // TODO mark as deprecated + void drawAt(const int x, const int y); + + /** + Draw this image at position @a pos using the current OpenGL context. + */ + // TODO mark as deprecated + void drawAt(const Point& pos); + +protected: + /** @internal */ +// void _drawAt(const Point& pos) override; + +private: + GLenum fFormat; + GLenum fType; + GLuint fTextureId; + bool setupCalled; +}; + +// ----------------------------------------------------------------------- + END_NAMESPACE_DGL #endif diff --git a/dgl/src/Image.cpp b/dgl/src/Image.cpp deleted file mode 100644 index 90596fec..00000000 --- a/dgl/src/Image.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho - * - * 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 "../Image.hpp" - -START_NAMESPACE_DGL - -// ----------------------------------------------------------------------- - -Image::Image() - : ImageBase(), - fFormat(0), - fType(0), - fTextureId(0), - fIsReady(false) -{ - glGenTextures(1, &fTextureId); -} - -Image::Image(const Image& image) - : ImageBase(image), - fFormat(image.fFormat), - fType(image.fType), - fTextureId(0), - fIsReady(false) -{ - glGenTextures(1, &fTextureId); -} - -Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) - : ImageBase(rawData, width, height), - fFormat(format), - fType(type), - fTextureId(0), - fIsReady(false) -{ - glGenTextures(1, &fTextureId); -} - -Image::Image(const char* const rawData, const Size& size, const GLenum format, const GLenum type) - : ImageBase(rawData, size), - fFormat(format), - fType(type), - fTextureId(0), - fIsReady(false) -{ - glGenTextures(1, &fTextureId); -} - -Image::~Image() -{ - if (fTextureId != 0) - { -#ifndef DISTRHO_OS_MAC // FIXME - glDeleteTextures(1, &fTextureId); -#endif - fTextureId = 0; - } -} - -void Image::loadFromMemory(const char* const rawData, - const uint width, - const uint height, - const GLenum format, - const GLenum type) noexcept -{ - loadFromMemory(rawData, Size(width, height), format, type); -} - -void Image::loadFromMemory(const char* const rawData, - const Size& size, - const GLenum format, - const GLenum type) noexcept -{ - fRawData = rawData; - fSize = size; - fFormat = format; - fType = type; - fIsReady = false; -} - -GLenum Image::getFormat() const noexcept -{ - return fFormat; -} - -GLenum Image::getType() const noexcept -{ - return fType; -} - -Image& Image::operator=(const Image& image) noexcept -{ - fRawData = image.fRawData; - fSize = image.fSize; - fFormat = image.fFormat; - fType = image.fType; - fIsReady = false; - return *this; -} - -void Image::_drawAt(const Point& pos) -{ - if (fTextureId == 0 || ! isValid()) - return; - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fTextureId); - - if (! fIsReady) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(fSize.getWidth()), static_cast(fSize.getHeight()), 0, - fFormat, fType, fRawData); - - fIsReady = true; - } - - Rectangle(pos, static_cast(fSize.getWidth()), static_cast(fSize.getHeight())).draw(); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL diff --git a/dgl/src/ImageBase.cpp b/dgl/src/ImageBase.cpp index b5dfbb96..82151cc3 100644 --- a/dgl/src/ImageBase.cpp +++ b/dgl/src/ImageBase.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,82 +18,83 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// protected constructors ImageBase::ImageBase() - : fRawData(nullptr), - fSize(0, 0) {} + : rawData(nullptr), + size(0, 0) {} -ImageBase::ImageBase(const char* const rawData, const uint width, const uint height) - : fRawData(rawData), - fSize(width, height) {} +ImageBase::ImageBase(const char* const rdata, const uint width, const uint height) + : rawData(rdata), + size(width, height) {} -ImageBase::ImageBase(const char* const rawData, const Size& size) - : fRawData(rawData), - fSize(size) {} +ImageBase::ImageBase(const char* const rdata, const Size& s) + : rawData(rdata), + size(s) {} ImageBase::ImageBase(const ImageBase& image) - : fRawData(image.fRawData), - fSize(image.fSize) {} + : rawData(image.rawData), + size(image.size) {} -ImageBase::~ImageBase() {} +// -------------------------------------------------------------------------------------------------------------------- +// public methods -// ----------------------------------------------------------------------- +ImageBase::~ImageBase() {} bool ImageBase::isValid() const noexcept { - return (fRawData != nullptr && fSize.isValid()); + return (rawData != nullptr && size.isValid()); +} + +bool ImageBase::isInvalid() const noexcept +{ + return (rawData == nullptr || size.isInvalid()); } uint ImageBase::getWidth() const noexcept { - return fSize.getWidth(); + return size.getWidth(); } uint ImageBase::getHeight() const noexcept { - return fSize.getHeight(); + return size.getHeight(); } const Size& ImageBase::getSize() const noexcept { - return fSize; + return size; } const char* ImageBase::getRawData() const noexcept { - return fRawData; -} - -// ----------------------------------------------------------------------- - -void ImageBase::draw() -{ - _drawAt(Point()); + return rawData; } -void ImageBase::drawAt(const int x, const int y) +void ImageBase::draw(const GraphicsContext& context) { - _drawAt(Point(x, y)); + drawAt(context, Point(0, 0)); } -void ImageBase::drawAt(const Point& pos) +void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y) { - _drawAt(pos); + drawAt(context, Point(x, y)); } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// public operators ImageBase& ImageBase::operator=(const ImageBase& image) noexcept { - fRawData = image.fRawData; - fSize = image.fSize; + rawData = image.rawData; + size = image.size; return *this; } bool ImageBase::operator==(const ImageBase& image) const noexcept { - return (fRawData == image.fRawData && fSize == image.fSize); + return (rawData == image.rawData && size == image.size); } bool ImageBase::operator!=(const ImageBase& image) const noexcept @@ -101,6 +102,6 @@ bool ImageBase::operator!=(const ImageBase& image) const noexcept return !operator==(image); } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 4909dde0..dd5b39c7 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -111,6 +111,185 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- +OpenGLImage::OpenGLImage() + : ImageBase(), + fFormat(0), + fType(0), + fTextureId(0), + setupCalled(false) {} + +OpenGLImage::OpenGLImage(const OpenGLImage& image) + : ImageBase(image), + fFormat(image.fFormat), + fType(image.fType), + fTextureId(0), + setupCalled(false) {} + +OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) + : ImageBase(rawData, width, height), + fFormat(format), + fType(type), + fTextureId(0), + setupCalled(false) {} + +OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const GLenum format, const GLenum type) + : ImageBase(rawData, size), + fFormat(format), + fType(type), + fTextureId(0), + setupCalled(false) {} + +OpenGLImage::~OpenGLImage() +{ + if (setupCalled) { + // FIXME test if this is still necessary with new pugl +#ifndef DISTRHO_OS_MAC + if (fTextureId != 0) + cleanup(); +#endif + DISTRHO_SAFE_ASSERT(fTextureId == 0); + } +} + +void OpenGLImage::loadFromMemory(const char* const rawData, + const uint width, + const uint height, + const GLenum format, + const GLenum type) noexcept +{ + loadFromMemory(rawData, Size(width, height), format, type); +} + +void OpenGLImage::loadFromMemory(const char* const rdata, + const Size& s, + const GLenum format, + const GLenum type) noexcept +{ + rawData = rdata; + size = s; + fFormat = format; + fType = type; + setupCalled = false; +} + +void OpenGLImage::setup() +{ + setupCalled = true; + DISTRHO_SAFE_ASSERT_RETURN(fTextureId == 0,); + DISTRHO_SAFE_ASSERT_RETURN(isValid(),); + + glGenTextures(1, &fTextureId); + DISTRHO_SAFE_ASSERT_RETURN(fTextureId != 0,); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fTextureId); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + static_cast(size.getWidth()), static_cast(size.getHeight()), 0, + fFormat, fType, rawData); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +void OpenGLImage::cleanup() +{ + DISTRHO_SAFE_ASSERT_RETURN(fTextureId != 0,); + glDeleteTextures(1, &fTextureId); + fTextureId = 0; +} + +GLenum OpenGLImage::getFormat() const noexcept +{ + return fFormat; +} + +GLenum OpenGLImage::getType() const noexcept +{ + return fType; +} + +void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) +{ + drawAt(pos); +} + +OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept +{ + rawData = image.rawData; + size = image.size; + fFormat = image.fFormat; + fType = image.fType; + setupCalled = false; + return *this; +} + +void OpenGLImage::draw() +{ + drawAt(Point(0, 0)); +} + +void OpenGLImage::drawAt(const int x, const int y) +{ + drawAt(Point(x, y)); +} + +void OpenGLImage::drawAt(const Point& pos) +{ + if (isInvalid()) + return; + + if (! setupCalled) + { + // TODO check if this is valid, give warning about needing to call setup/cleanup manually + setup(); + } + + if (fTextureId == 0) + return; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fTextureId); + + glBegin(GL_QUADS); + + { + const int x = pos.getX(); + const int y = pos.getY(); + const int w = static_cast(size.getWidth()); + const int h = static_cast(size.getHeight()); + + glTexCoord2f(0.0f, 0.0f); + glVertex2d(x, y); + + glTexCoord2f(1.0f, 0.0f); + glVertex2d(x+w, y); + + glTexCoord2f(1.0f, 1.0f); + glVertex2d(x+w, y+h); + + glTexCoord2f(0.0f, 1.0f); + glVertex2d(x, y+h); + } + + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +// ----------------------------------------------------------------------- + #if 0 void Widget::PrivateData::display(const uint width, const uint height, diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 99062ed2..edf2289c 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -25,6 +25,7 @@ #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" #include "dgl/src/Geometry.cpp" +#include "dgl/src/ImageBase.cpp" #include "dgl/src/OpenGL.cpp" #include "dgl/src/SubWidget.cpp" #include "dgl/src/SubWidgetPrivateData.cpp" @@ -37,9 +38,13 @@ #include "dgl/StandaloneWindow.hpp" #include "widgets/ExampleColorWidget.hpp" +#include "widgets/ExampleImagesWidget.hpp" #include "widgets/ExampleRectanglesWidget.hpp" #include "widgets/ExampleShapesWidget.hpp" +#include "demo_res/DemoArtwork.cpp" +#include "images_res/CatPics.cpp" + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- @@ -66,6 +71,7 @@ public: #if 0 // for text font = nvg.createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); +#endif using namespace DemoArtwork; img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); @@ -73,7 +79,6 @@ public: img3.loadFromMemory(ico3Data, ico3Width, ico2Height, GL_BGR); img4.loadFromMemory(ico4Data, ico4Width, ico4Height, GL_BGR); img5.loadFromMemory(ico5Data, ico5Width, ico5Height, GL_BGR); -#endif } protected: @@ -110,7 +115,6 @@ protected: // reset color glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -#if 0 const int pad = iconSize/2 - DemoArtwork::ico1Width/2; img1.drawAt(pad, pad); @@ -119,6 +123,7 @@ protected: img4.drawAt(pad, pad + 9 + iconSize*3); img5.drawAt(pad, pad + 12 + iconSize*4); +#if 0 // draw some text nvg.beginFrame(this); @@ -215,9 +220,9 @@ private: int curPage, curHover; Rectangle bgIcon; Line lineSep; -#if 0 - Image img1, img2, img3, img4, img5; + OpenGLImage img1, img2, img3, img4, img5; +#if 0 // for text NanoVG nvg;D NanoVG::FontId font; @@ -238,20 +243,21 @@ public: DemoWindow(Application& app) : StandaloneWindow(app), wColor(this), + wImages(this), wRects(this), wShapes(this), wLeft(this, this), curWidget(nullptr) { wColor.hide(); -// wImages.hide(); + wImages.hide(); wRects.hide(); wShapes.hide(); // wText.hide(); // //wPerf.hide(); wColor.setAbsoluteX(kSidebarWidth); -// wImages.setAbsoluteX(kSidebarWidth); + wImages.setAbsoluteX(kSidebarWidth); wRects.setAbsoluteX(kSidebarWidth); wShapes.setAbsoluteX(kSidebarWidth); // wText.setAbsoluteX(kSidebarWidth); @@ -275,9 +281,9 @@ protected: case 0: curWidget = &wColor; break; -// case 1: -// curWidget = &wImages; -// break; + case 1: + curWidget = &wImages; + break; case 2: curWidget = &wRects; break; @@ -309,7 +315,7 @@ protected: Size size(width-kSidebarWidth, height); wColor.setSize(size); -// wImages.setSize(size); + wImages.setSize(size); wRects.setSize(size); wShapes.setSize(size); // wText.setSize(size); @@ -323,7 +329,7 @@ protected: private: ExampleColorSubWidget wColor; -// ExampleImagesWidget wImages; + ExampleImagesSubWidget wImages; ExampleRectanglesSubWidget wRects; ExampleShapesSubWidget wShapes; // ExampleTextWidget wText; @@ -364,6 +370,8 @@ int main(int argc, char* argv[]) /**/ if (std::strcmp(argv[1], "color") == 0) createAndShowExampleWidgetStandaloneWindow(app); + else if (std::strcmp(argv[1], "images") == 0) + createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "rectangles") == 0) createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "shapes") == 0) diff --git a/tests/widgets/ExampleImagesWidget.hpp b/tests/widgets/ExampleImagesWidget.hpp index 8c0a134a..9f33e801 100644 --- a/tests/widgets/ExampleImagesWidget.hpp +++ b/tests/widgets/ExampleImagesWidget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2015 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,22 +20,25 @@ // ------------------------------------------------------ // DGL Stuff -#include "Image.hpp" -#include "Widget.hpp" -#include "Window.hpp" +#include "../../dgl/Image.hpp" +#include "../../dgl/SubWidget.hpp" +#include "../../dgl/TopLevelWidget.hpp" + // ------------------------------------------------------ // Images #include "../images_res/CatPics.hpp" +START_NAMESPACE_DGL + // ------------------------------------------------------ // our widget -class ExampleImagesWidget : public Widget, +template +class ExampleImagesWidget : public BaseWidget, public IdleCallback { -public: static const int kImg1y = 0; static const int kImg2y = 500/2-CatPics::cat2Height/2; static const int kImg3x = 400/3-CatPics::cat3Width/3; @@ -44,160 +47,207 @@ public: static const int kImg2max = 500-CatPics::cat2Width; static const int kImg3max = 400-CatPics::cat3Height; - ExampleImagesWidget(Window& parent, const bool setParentSize = false) - : Widget(parent), - fImgTop1st(1), - fImgTop2nd(2), - fImgTop3rd(3), - fImg1x(0), - fImg2x(kImg2max), - fImg3y(kImg3max), - fImg1rev(false), - fImg2rev(true), - fImg3rev(true), - fImg1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), - fImg2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), - fImg3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + int imgTop1st, imgTop2nd, imgTop3rd; + int img1x, img2x, img3y; + bool img1rev, img2rev, img3rev; + Image img1, img2, img3; + +public: + static constexpr const char* kExampleWidgetName = "Images"; + + // SubWidget + ExampleImagesWidget(Widget* const parent) + : BaseWidget(parent), + imgTop1st(1), + imgTop2nd(2), + imgTop3rd(3), + img1x(0), + img2x(kImg2max), + img3y(kImg3max), + img1rev(false), + img2rev(true), + img3rev(true), + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) { - setSize(500, 400); + BaseWidget::setSize(500, 400); - parent.addIdleCallback(this); + parent->getApp().addIdleCallback(this); + } - if (setParentSize) - { - parent.setSize(500, 400); - parent.setResizable(false); - } + // TopLevelWidget + ExampleImagesWidget(Window& windowToMapTo) + : BaseWidget(windowToMapTo), + imgTop1st(1), + imgTop2nd(2), + imgTop3rd(3), + img1x(0), + img2x(kImg2max), + img3y(kImg3max), + img1rev(false), + img2rev(true), + img3rev(true), + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + { + BaseWidget::setSize(500, 400); + + windowToMapTo.getApp().addIdleCallback(this); } -private: + // StandaloneWindow + ExampleImagesWidget(Application& app) + : BaseWidget(app), + imgTop1st(1), + imgTop2nd(2), + imgTop3rd(3), + img1x(0), + img2x(kImg2max), + img3y(kImg3max), + img1rev(false), + img2rev(true), + img3rev(true), + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + { + BaseWidget::setSize(500, 400); + + app.addIdleCallback(this); + } + +protected: void idleCallback() noexcept override { - if (fImg1rev) + if (img1rev) { - fImg1x -= 2; - if (fImg1x <= -50) + img1x -= 2; + if (img1x <= -50) { - fImg1rev = false; + img1rev = false; setNewTopImg(1); } } else { - fImg1x += 2; - if (fImg1x >= kImg1max+50) + img1x += 2; + if (img1x >= kImg1max+50) { - fImg1rev = true; + img1rev = true; setNewTopImg(1); } } - if (fImg2rev) + if (img2rev) { - fImg2x -= 1; - if (fImg2x <= -50) + img2x -= 1; + if (img2x <= -50) { - fImg2rev = false; + img2rev = false; setNewTopImg(2); } } else { - fImg2x += 4; - if (fImg2x >= kImg2max+50) + img2x += 4; + if (img2x >= kImg2max+50) { - fImg2rev = true; + img2rev = true; setNewTopImg(2); } } - if (fImg3rev) + if (img3rev) { - fImg3y -= 3; - if (fImg3y <= -50) + img3y -= 3; + if (img3y <= -50) { - fImg3rev = false; + img3rev = false; setNewTopImg(3); } } else { - fImg3y += 3; - if (fImg3y >= kImg3max+50) + img3y += 3; + if (img3y >= kImg3max+50) { - fImg3rev = true; + img3rev = true; setNewTopImg(3); } } - repaint(); + BaseWidget::repaint(); } void onDisplay() override { - switch (fImgTop3rd) + switch (imgTop3rd) { case 1: - fImg1.drawAt(fImg1x, kImg1y); + img1.drawAt(img1x, kImg1y); break; case 2: - fImg2.drawAt(fImg2x, kImg2y); + img2.drawAt(img2x, kImg2y); break; case 3: - fImg3.drawAt(kImg3x, fImg3y); + img3.drawAt(kImg3x, img3y); break; }; - switch (fImgTop2nd) + switch (imgTop2nd) { case 1: - fImg1.drawAt(fImg1x, kImg1y); + img1.drawAt(img1x, kImg1y); break; case 2: - fImg2.drawAt(fImg2x, kImg2y); + img2.drawAt(img2x, kImg2y); break; case 3: - fImg3.drawAt(kImg3x, fImg3y); + img3.drawAt(kImg3x, img3y); break; }; - switch (fImgTop1st) + switch (imgTop1st) { case 1: - fImg1.drawAt(fImg1x, kImg1y); + img1.drawAt(img1x, kImg1y); break; case 2: - fImg2.drawAt(fImg2x, kImg2y); + img2.drawAt(img2x, kImg2y); break; case 3: - fImg3.drawAt(kImg3x, fImg3y); + img3.drawAt(kImg3x, img3y); break; }; } +private: void setNewTopImg(const int imgId) noexcept { - if (fImgTop1st == imgId) + if (imgTop1st == imgId) return; - if (fImgTop2nd == imgId) + if (imgTop2nd == imgId) { - fImgTop2nd = fImgTop1st; - fImgTop1st = imgId; + imgTop2nd = imgTop1st; + imgTop1st = imgId; return; } - fImgTop3rd = fImgTop2nd; - fImgTop2nd = fImgTop1st; - fImgTop1st = imgId; + imgTop3rd = imgTop2nd; + imgTop2nd = imgTop1st; + imgTop1st = imgId; } - - int fImgTop1st, fImgTop2nd, fImgTop3rd; - int fImg1x, fImg2x, fImg3y; - bool fImg1rev, fImg2rev, fImg3rev; - Image fImg1, fImg2, fImg3; }; +typedef ExampleImagesWidget ExampleImagesSubWidget; +typedef ExampleImagesWidget ExampleImagesTopLevelWidget; +typedef ExampleImagesWidget ExampleImagesStandaloneWindow; + // ------------------------------------------------------ +END_NAMESPACE_DGL + #endif // EXAMPLE_IMAGES_WIDGET_HPP_INCLUDED From 5b10613f6e2191a7c38cd67ec62d9517837cb039 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 May 2021 17:13:39 +0100 Subject: [PATCH 049/159] Start working on nanovg and text, mostly works Signed-off-by: falkTX --- dgl/Color.hpp | 4 +- dgl/NanoVG.hpp | 29 +++++++---- dgl/src/NanoVG.cpp | 76 ++++++++++++++++++++--------- dgl/src/WindowPrivateData.cpp | 21 ++++++-- tests/Demo.cpp | 33 +++++++------ tests/widgets/ExampleTextWidget.hpp | 68 ++++++++++++++++---------- 6 files changed, 152 insertions(+), 79 deletions(-) diff --git a/dgl/Color.hpp b/dgl/Color.hpp index 1b611805..6276b0d9 100644 --- a/dgl/Color.hpp +++ b/dgl/Color.hpp @@ -19,10 +19,10 @@ #include "Base.hpp" -START_NAMESPACE_DGL - struct NVGcolor; +START_NAMESPACE_DGL + // -------------------------------------------------------------------------------------------------------------------- /** diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 27a38d45..618b5fdb 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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,7 +19,9 @@ #include "Color.hpp" #include "OpenGL.hpp" -#include "Widget.hpp" +#include "SubWidget.hpp" +#include "TopLevelWidget.hpp" +#include "StandaloneWindow.hpp" #ifndef DGL_NO_SHARED_RESOURCES # define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" @@ -853,7 +855,7 @@ public: /** Load DPF's internal shared resources for this NanoVG class. */ - virtual void loadSharedResources(); + virtual bool loadSharedResources(); #endif private: @@ -875,25 +877,28 @@ private: The drawing function onDisplay() is implemented internally but a new onNanoDisplay() needs to be overridden instead. */ -class NanoWidget : public Widget, +template +class NanoWidget : public BaseWidget, public NanoVG { public: /** - Constructor. + Constructor for a NanoSubWidget. @see CreateFlags */ - explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS); + explicit NanoWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS); /** - Constructor for a subwidget. + Constructor for a NanoTopLevelWidget. + @see CreateFlags */ - explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS); + explicit NanoWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); /** - Constructor for a subwidget, reusing a NanoVG context. + Constructor for a NanoStandaloneWindow. + @see CreateFlags */ - explicit NanoWidget(NanoWidget* groupWidget); + explicit NanoWidget(Application& app, int flags = CREATE_ANTIALIAS); /** Destructor. @@ -927,6 +932,10 @@ private: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget) }; +typedef NanoWidget NanoSubWidget; +typedef NanoWidget NanoTopLevelWidget; +typedef NanoWidget NanoStandaloneWindow; + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 5e2a93c3..0f50dbe3 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -257,25 +257,25 @@ NanoVG::~NanoVG() void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor) { - fInFrame = true; if (fContext == nullptr) return; DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); + fInFrame = true; nvgBeginFrame(fContext, static_cast(width), static_cast(height), scaleFactor); } void NanoVG::beginFrame(Widget* const widget) { - fInFrame = true; DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); + fInFrame = true; if (fContext == nullptr) return; - Window& window(widget->getParentWindow()); - nvgBeginFrame(fContext, static_cast(window.getWidth()), static_cast(window.getHeight()), 1.0f); + if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) + nvgBeginFrame(fContext, static_cast(tlw->getWidth()), static_cast(tlw->getHeight()), 1.0f); } void NanoVG::cancelFrame() @@ -771,26 +771,26 @@ void NanoVG::stroke() NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename) { - if (fContext == nullptr) return -1; DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); return nvgCreateFont(fContext, name, filename); } NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData) { - if (fContext == nullptr) return -1; DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); return nvgCreateFontMem(fContext, name, const_cast(data), static_cast(dataSize), freeData); } NanoVG::FontId NanoVG::findFont(const char* name) { - if (fContext == nullptr) return -1; DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1); + DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1); return nvgFindFont(fContext, name); } @@ -912,35 +912,62 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi } #ifndef DGL_NO_SHARED_RESOURCES -void NanoVG::loadSharedResources() +bool NanoVG::loadSharedResources() { - if (fContext == nullptr) return; + if (fContext == nullptr) return false; if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0) - return; + return true; using namespace dpf_resources; - nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0); + return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, + (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; } #endif // ----------------------------------------------------------------------- -struct NanoWidget::PrivateData { - NanoWidget* const self; - std::vector subWidgets; +template +struct NanoWidget::PrivateData { + NanoWidget* const self; - PrivateData(NanoWidget* const s) - : self(s), - subWidgets() {} + PrivateData(NanoWidget* const s) + : self(s) {} ~PrivateData() { - subWidgets.clear(); } }; +// SubWidget +template +NanoWidget::NanoWidget(Widget* const parent, int flags) + : BaseWidget(parent), + NanoVG(flags), + nData(new PrivateData(this)) +{ +} + +// TopLevelWidget +template +NanoWidget::NanoWidget(Window& windowToMapTo, int flags) + : BaseWidget(windowToMapTo), + NanoVG(flags), + nData(new PrivateData(this)) +{ +} + +// StandaloneWindow +template +NanoWidget::NanoWidget(Application& app, int flags) + : BaseWidget(app), + NanoVG(flags), + nData(new PrivateData(this)) +{ +} + +/* NanoWidget::NanoWidget(Window& parent, int flags) : Widget(parent), NanoVG(flags), @@ -957,7 +984,6 @@ NanoWidget::NanoWidget(Widget* groupWidget, int flags) pData->needsScaling = true; } -/* NanoWidget::NanoWidget(NanoWidget* groupWidget) : Widget(groupWidget, false), NanoVG(groupWidget), @@ -969,21 +995,25 @@ NanoWidget::NanoWidget(NanoWidget* groupWidget) } */ -NanoWidget::~NanoWidget() +template +NanoWidget::~NanoWidget() { delete nData; } -void NanoWidget::onDisplay() +template +void NanoWidget::onDisplay() { - NanoVG::beginFrame(getWidth(), getHeight()); + NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); onNanoDisplay(); + /* for (std::vector::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it) { NanoWidget* const widget(*it); widget->onNanoDisplay(); } + */ NanoVG::endFrame(); } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 560d4c81..9082710a 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -90,13 +90,17 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(1.0), pendingVisibility(kPendingVisibilityNone) { + if (isEmbed) + { + puglSetDefaultSize(view, width, height); + puglSetParentWindow(view, parentWindowHandle); + } + init(width, height, resizable); if (isEmbed) { appData->oneWindowShown(); - puglSetDefaultSize(view, width, height); - puglSetParentWindow(view, parentWindowHandle); puglShow(view); } } @@ -137,7 +141,7 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); - puglSetViewHint(view, PUGL_DEPTH_BITS, 8); + puglSetViewHint(view, PUGL_DEPTH_BITS, 16); puglSetViewHint(view, PUGL_STENCIL_BITS, 8); // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); @@ -149,6 +153,10 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r rect.width = width; rect.height = height; puglSetFrame(view, rect); + + // FIXME this is bad + puglRealize(view); + puglX11GlEnter(view, NULL); } // ----------------------------------------------------------------------- @@ -182,9 +190,16 @@ void Window::PrivateData::show() isClosed = false; appData->oneWindowShown(); +#ifdef DISTRHO_OS_WINDOWS + puglWin32ShowWindowCentered(view); +#else + puglShow(view); +#endif + /* pendingVisibility = kPendingVisibilityShow; const PuglStatus status = puglRealize(view); DISTRHO_SAFE_ASSERT_INT_RETURN(status == PUGL_SUCCESS, status, close()); + */ } else { diff --git a/tests/Demo.cpp b/tests/Demo.cpp index edf2289c..89d484f5 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -21,12 +21,16 @@ #include "tests.hpp" // #define DPF_TEST_POINT_CPP +#include "dgl/OpenGL.hpp" #include "dgl/src/pugl.cpp" #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" +#include "dgl/src/Color.cpp" #include "dgl/src/Geometry.cpp" #include "dgl/src/ImageBase.cpp" +#include "dgl/src/NanoVG.cpp" #include "dgl/src/OpenGL.cpp" +#include "dgl/src/Resources.cpp" #include "dgl/src/SubWidget.cpp" #include "dgl/src/SubWidgetPrivateData.cpp" #include "dgl/src/TopLevelWidget.cpp" @@ -40,6 +44,7 @@ #include "widgets/ExampleColorWidget.hpp" #include "widgets/ExampleImagesWidget.hpp" #include "widgets/ExampleRectanglesWidget.hpp" +#include "widgets/ExampleTextWidget.hpp" #include "widgets/ExampleShapesWidget.hpp" #include "demo_res/DemoArtwork.cpp" @@ -68,10 +73,8 @@ public: curPage(0), curHover(-1) { -#if 0 // for text - font = nvg.createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf"); -#endif + nvg.loadSharedResources(); using namespace DemoArtwork; img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); @@ -123,7 +126,6 @@ protected: img4.drawAt(pad, pad + 9 + iconSize*3); img5.drawAt(pad, pad + 12 + iconSize*4); -#if 0 // draw some text nvg.beginFrame(this); @@ -136,7 +138,6 @@ protected: nvg.textBox(15, 440, iconSize, "Look!", nullptr); nvg.endFrame(); -#endif } bool onMouse(const MouseEvent& ev) override @@ -222,11 +223,8 @@ private: Line lineSep; OpenGLImage img1, img2, img3, img4, img5; -#if 0 // for text - NanoVG nvg;D - NanoVG::FontId font; -#endif + NanoVG nvg; }; // -------------------------------------------------------------------------------------------------------------------- @@ -246,6 +244,7 @@ public: wImages(this), wRects(this), wShapes(this), + wText(this), wLeft(this, this), curWidget(nullptr) { @@ -253,14 +252,14 @@ public: wImages.hide(); wRects.hide(); wShapes.hide(); -// wText.hide(); + wText.hide(); // //wPerf.hide(); wColor.setAbsoluteX(kSidebarWidth); wImages.setAbsoluteX(kSidebarWidth); wRects.setAbsoluteX(kSidebarWidth); wShapes.setAbsoluteX(kSidebarWidth); -// wText.setAbsoluteX(kSidebarWidth); + wText.setAbsoluteX(kSidebarWidth); wLeft.setAbsolutePos(2, 2); // wPerf.setAbsoluteY(5); @@ -290,9 +289,9 @@ protected: case 3: curWidget = &wShapes; break; -// case 4: -// curWidget = &wText; -// break; + case 4: + curWidget = &wText; + break; default: curWidget = nullptr; break; @@ -318,7 +317,7 @@ protected: wImages.setSize(size); wRects.setSize(size); wShapes.setSize(size); -// wText.setSize(size); + wText.setSize(size); wLeft.setSize(kSidebarWidth-4, height-4); //wRezHandle.setAbsoluteX(width-wRezHandle.getWidth()); @@ -332,7 +331,7 @@ private: ExampleImagesSubWidget wImages; ExampleRectanglesSubWidget wRects; ExampleShapesSubWidget wShapes; -// ExampleTextWidget wText; + ExampleTextSubWidget wText; LeftSideWidget wLeft; //ResizeHandle wRezHandle; // NanoPerfWidget wPerf; @@ -376,6 +375,8 @@ int main(int argc, char* argv[]) createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "shapes") == 0) createAndShowExampleWidgetStandaloneWindow(app); + else if (std::strcmp(argv[1], "text") == 0) + createAndShowExampleWidgetStandaloneWindow(app); else d_stderr2("Invalid demo mode, must be one of: color, rectangles, shapes"); } diff --git a/tests/widgets/ExampleTextWidget.hpp b/tests/widgets/ExampleTextWidget.hpp index 3d3bbca1..c8d4d8e3 100644 --- a/tests/widgets/ExampleTextWidget.hpp +++ b/tests/widgets/ExampleTextWidget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2015 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -20,51 +20,69 @@ // ------------------------------------------------------ // DGL Stuff -#include "NanoVG.hpp" +#include "../../dgl/NanoVG.hpp" + +START_NAMESPACE_DGL // ------------------------------------------------------ // our widget -class ExampleTextWidget : public NanoWidget +template +class ExampleTextWidget : public BaseWidget { public: - ExampleTextWidget(Window& parent) - : NanoWidget(parent), - fFont(createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf")) + static constexpr const char* kExampleWidgetName = "Text"; + + // SubWidget + explicit ExampleTextWidget(Widget* const parent) + : BaseWidget(parent) + { + NanoVG::loadSharedResources(); + BaseWidget::setSize(500, 300); + } + + // TopLevelWidget + explicit ExampleTextWidget(Window& windowToMapTo) + : BaseWidget(windowToMapTo) { - setSize(500, 300); + NanoVG::loadSharedResources(); + BaseWidget::setSize(500, 300); } - ExampleTextWidget(Widget* groupWidget) - : NanoWidget(groupWidget), - fFont(createFontFromFile("sans", "./nanovg_res/Roboto-Regular.ttf")) + // StandaloneWindow + explicit ExampleTextWidget(Application& app) + : BaseWidget(app) { - setSize(500, 300); + NanoVG::loadSharedResources(); + BaseWidget::setSize(500, 300); } protected: void onNanoDisplay() override { - const int width = getWidth(); - const int height = getHeight(); + const int width = BaseWidget::getWidth(); + const int height = BaseWidget::getHeight(); - fontSize(40.0f); - textAlign(Align(ALIGN_CENTER|ALIGN_MIDDLE)); - textLineHeight(20.0f); + NanoVG::fontSize(40.0f); + NanoVG::textAlign(NanoVG::Align(NanoVG::ALIGN_CENTER|NanoVG::ALIGN_MIDDLE)); + NanoVG::textLineHeight(20.0f); - beginPath(); - fillColor(220,220,220,255); - roundedRect(10, height/4+10, width-20, height/2-20, 3); - fill(); + NanoVG::beginPath(); + NanoVG::fillColor(220,220,220,255); + NanoVG::roundedRect(10, height/4+10, width-20, height/2-20, 3); + NanoVG::fill(); - fillColor(0,200,0,220); - textBox(10, height/2, width-20, "Hello World!", nullptr); + NanoVG::fillColor(0,150,0,220); + NanoVG::textBox(10, height/2, width-20, "Hello World!", nullptr); } - -private: - FontId fFont; }; +typedef ExampleTextWidget ExampleTextSubWidget; +typedef ExampleTextWidget ExampleTextTopLevelWidget; +typedef ExampleTextWidget ExampleTextStandaloneWindow; + // ------------------------------------------------------ +END_NAMESPACE_DGL + #endif // EXAMPLE_TEXT_WIDGET_HPP_INCLUDED From febdf28e055512e929aab07982a3d445fc3ef401 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 May 2021 17:30:49 +0100 Subject: [PATCH 050/159] Fix build, add puglBackendEnter Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 2 +- dgl/src/pugl.cpp | 8 ++++++++ dgl/src/pugl.hpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 9082710a..56bf8e57 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -156,7 +156,7 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r // FIXME this is bad puglRealize(view); - puglX11GlEnter(view, NULL); + puglBackendEnter(view); } // ----------------------------------------------------------------------- diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 29f02eda..507c0411 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -93,6 +93,14 @@ const char* puglGetWindowTitle(const PuglView* view) return view->title; } +// -------------------------------------------------------------------------------------------------------------------- +// expose backend enter + +void puglBackendEnter(PuglView* view) +{ + view->backend->enter(view, NULL); +} + // -------------------------------------------------------------------------------------------------------------------- // set window size without changing frame x/y position diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 2248fd73..959ce560 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -37,6 +37,10 @@ PUGL_BEGIN_DECLS PUGL_API const char* puglGetWindowTitle(const PuglView* view); +// expose backend enter +PUGL_API void +puglBackendEnter(PuglView* view); + // set window size without changing frame x/y position PUGL_API PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); From 01410d90fe082f4249fb3c39ccb4ce31e0d04fc7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 12 May 2021 16:32:21 +0100 Subject: [PATCH 051/159] Fixup CI Signed-off-by: falkTX --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d7880803..e315bdd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,4 @@ -os: linux -dist: bionic - cache: directories: - ${HOME}/debs @@ -11,7 +8,7 @@ jobs: - name: "Linux native" os: linux compiler: gcc - dist: focal + dist: bionic env: - TARGET="linux" services: From bc9bf65844c2d776a8e44b0294933838a68739c7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 13 May 2021 19:21:57 +0100 Subject: [PATCH 052/159] Fix nanovg subwidgets, need viewport scaling Signed-off-by: falkTX --- dgl/OpenGL.hpp | 4 ---- dgl/SubWidget.hpp | 1 + dgl/src/NanoVG.cpp | 1 + dgl/src/OpenGL.cpp | 10 ++++------ dgl/src/SubWidgetPrivateData.cpp | 3 ++- dgl/src/SubWidgetPrivateData.hpp | 1 + 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index db1e0729..b0f37252 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -232,10 +232,6 @@ public: // TODO mark as deprecated void drawAt(const Point& pos); -protected: - /** @internal */ -// void _drawAt(const Point& pos) override; - private: GLenum fFormat; GLenum fType; diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 6cbe49af..8969ea8e 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -124,6 +124,7 @@ private: struct PrivateData; PrivateData* const pData; friend class Widget; + template friend class NanoWidget; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) }; diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 5af16da8..3e1aa459 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -963,6 +963,7 @@ NanoWidget::NanoWidget(Widget* const parent, int flags) NanoVG(flags), nData(new PrivateData(this)) { + BaseWidget::pData->viewportNeedsScaling = true; } // TopLevelWidget diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index dd5b39c7..757b7c72 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -365,12 +365,11 @@ void SubWidget::PrivateData::display(uint width, uint height, double autoScaling { // full viewport size glViewport(0, - -(height * autoScaling - height), - width * autoScaling, - height * autoScaling); + -(height * autoScaling - height), + width * autoScaling, + height * autoScaling); } - /* - else if (needsScaling) + else if (viewportNeedsScaling) { // limit viewport to widget bounds glViewport(absolutePos.getX(), @@ -378,7 +377,6 @@ void SubWidget::PrivateData::display(uint width, uint height, double autoScaling self->getWidth(), self->getHeight()); } - */ else { // only set viewport pos diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp index 98133fa9..4d7a3874 100644 --- a/dgl/src/SubWidgetPrivateData.cpp +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -24,7 +24,8 @@ START_NAMESPACE_DGL SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) : self(s), parentWidget(pw), - absolutePos() + absolutePos(), + viewportNeedsScaling(false) { parentWidget->pData->subWidgets.push_back(self); } diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index 699bcac0..e7e7382b 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -27,6 +27,7 @@ struct SubWidget::PrivateData { SubWidget* const self; Widget* const parentWidget; Point absolutePos; + bool viewportNeedsScaling; // needed for NanoVG explicit PrivateData(SubWidget* const s, Widget* const pw); ~PrivateData(); From 327bf352ef032c12f8b62c9ca52effe51a3c9822 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 13 May 2021 20:16:24 +0100 Subject: [PATCH 053/159] Get a few more things to build Signed-off-by: falkTX --- dgl/Makefile | 19 ++++++++++++------- dgl/src/Cairo.cpp | 14 +++++++------- dgl/src/NanoVG.cpp | 2 +- dgl/src/OpenGL.cpp | 24 ++++++++++++++++++++++-- dgl/src/SubWidgetPrivateData.cpp | 2 +- dgl/src/SubWidgetPrivateData.hpp | 2 +- dgl/src/TopLevelWidgetPrivateData.cpp | 17 ----------------- dgl/src/WidgetPrivateData.cpp | 2 +- 8 files changed, 45 insertions(+), 37 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 8497ed50..9adc3f0c 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -28,13 +28,18 @@ OBJS_common = \ ../build/dgl/ApplicationPrivateData.cpp.o \ ../build/dgl/Color.cpp.o \ ../build/dgl/Geometry.cpp.o \ + ../build/dgl/ImageBase.cpp.o \ + ../build/dgl/Resources.cpp.o \ + ../build/dgl/SubWidget.cpp.o \ + ../build/dgl/SubWidgetPrivateData.cpp.o \ ../build/dgl/TopLevelWidget.cpp.o \ + ../build/dgl/TopLevelWidgetPrivateData.cpp.o \ + ../build/dgl/Widget.cpp.o \ + ../build/dgl/WidgetPrivateData.cpp.o \ ../build/dgl/Window.cpp.o \ ../build/dgl/WindowPrivateData.cpp.o -# ../build/dgl/ImageBase.cpp.o \ -# ../build/dgl/Resources.cpp.o\ + # ../build/dgl/StandaloneWindow.cpp.o \ -# ../build/dgl/Widget.cpp.o \ # ../build/dgl/WindowFileBrowser.cpp.o # TODO: ImageWidgets.cpp @@ -42,9 +47,8 @@ OBJS_common = \ # --------------------------------------------------------------------------------------------------------------------- OBJS_cairo = $(OBJS_common) \ + ../build/dgl/Cairo.cpp.cairo.o \ ../build/dgl/pugl.cpp.cairo.o -# ../build/dgl/Cairo.cpp.cairo.o \ -# ../build/dgl/WidgetPrivateData.cpp.cairo.o # ifeq ($(MACOS),true) # OBJS_cairo += ../build/dgl/Window.mm.cairo.o @@ -55,11 +59,12 @@ OBJS_cairo = $(OBJS_common) \ # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ + ../build/dgl/OpenGL.cpp.opengl.o \ + ../build/dgl/NanoVG.cpp.opengl.o \ ../build/dgl/pugl.cpp.opengl.o -# ../build/dgl/OpenGL.cpp.opengl.o \ + # ../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/WindowPrivateData.cpp.opengl.o diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index fdb171cc..303244e9 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -15,7 +15,8 @@ */ #include "../Cairo.hpp" -#include "WidgetPrivateData.hpp" +#include "SubWidgetPrivateData.hpp" +#include "WindowPrivateData.hpp" START_NAMESPACE_DGL @@ -64,15 +65,14 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- -void Widget::PrivateData::display(const uint width, - const uint height, - const double autoScaling, - const bool renderingSubWidget) +void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaling) { + /* if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) return; + */ - cairo_t* cr = static_cast(parent.getGraphicsContext()).cairo; + cairo_t* cr = static_cast(self->getGraphicsContext()).handle; cairo_matrix_t matrix; cairo_get_matrix(cr, &matrix); cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); @@ -83,7 +83,7 @@ void Widget::PrivateData::display(const uint width, cairo_set_matrix(cr, &matrix); - displaySubWidgets(width, height, autoScaling); +// displaySubWidgets(width, height, autoScaling); } // ----------------------------------------------------------------------- diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 3e1aa459..95d15648 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -963,7 +963,7 @@ NanoWidget::NanoWidget(Widget* const parent, int flags) NanoVG(flags), nData(new PrivateData(this)) { - BaseWidget::pData->viewportNeedsScaling = true; + BaseWidget::pData->needsViewportScaling = true; } // TopLevelWidget diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 757b7c72..399fd640 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -16,6 +16,7 @@ #include "../OpenGL.hpp" #include "SubWidgetPrivateData.hpp" +#include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" @@ -357,7 +358,7 @@ void Widget::PrivateData::display(const uint width, } #endif -void SubWidget::PrivateData::display(uint width, uint height, double autoScaling) +void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaling) { bool needsDisableScissor = false; @@ -369,7 +370,7 @@ void SubWidget::PrivateData::display(uint width, uint height, double autoScaling width * autoScaling, height * autoScaling); } - else if (viewportNeedsScaling) + else if (needsViewportScaling) { // limit viewport to widget bounds glViewport(absolutePos.getX(), @@ -409,6 +410,25 @@ void SubWidget::PrivateData::display(uint width, uint height, double autoScaling // ----------------------------------------------------------------------- +void TopLevelWidget::PrivateData::display() +{ + const Size size(window.getSize()); + const uint width = size.getWidth(); + const uint height = size.getHeight(); + const double autoScaling = window.pData->autoScaling; + + // full viewport size + glViewport(0, -(height * autoScaling - height), width * autoScaling, height * autoScaling); + + // main widget drawing + self->onDisplay(); + + // now draw subwidgets if there are any + selfw->pData->displaySubWidgets(width, height, autoScaling); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp index 4d7a3874..039aa9b8 100644 --- a/dgl/src/SubWidgetPrivateData.cpp +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -25,7 +25,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) : self(s), parentWidget(pw), absolutePos(), - viewportNeedsScaling(false) + needsViewportScaling(false) { parentWidget->pData->subWidgets.push_back(self); } diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index e7e7382b..5e0e5cf5 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -27,7 +27,7 @@ struct SubWidget::PrivateData { SubWidget* const self; Widget* const parentWidget; Point absolutePos; - bool viewportNeedsScaling; // needed for NanoVG + bool needsViewportScaling; // needed for NanoVG explicit PrivateData(SubWidget* const s, Widget* const pw); ~PrivateData(); diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index c8fa5f28..c0ea814a 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -37,23 +37,6 @@ TopLevelWidget::PrivateData::~PrivateData() window.pData->topLevelWidget = nullptr; } -void TopLevelWidget::PrivateData::display() -{ - const Size size(window.getSize()); - const uint width = size.getWidth(); - const uint height = size.getHeight(); - const double autoScaling = window.pData->autoScaling; - - // full viewport size - glViewport(0, -(height * autoScaling - height), width * autoScaling, height * autoScaling); - - // main widget drawing - self->onDisplay(); - - // now draw subwidgets if there are any - selfw->pData->displaySubWidgets(width, height, autoScaling); -} - void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) { Events::MouseEvent rev = ev; diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index f163d47a..a5e7b5fb 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -15,7 +15,7 @@ */ #include "WidgetPrivateData.hpp" -#include "../SubWidget.hpp" +#include "SubWidgetPrivateData.hpp" #include "../TopLevelWidget.hpp" START_NAMESPACE_DGL From cd141a4367915915445a6f1e1161019b82bf4cff Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 13 May 2021 21:13:29 +0100 Subject: [PATCH 054/159] Add some quick things to get plugins to build again Signed-off-by: falkTX --- Makefile | 3 +- dgl/TopLevelWidget.hpp | 18 ++++++++ dgl/Widget.hpp | 12 ++++- dgl/Window.hpp | 12 ++++- dgl/src/NanoVG.cpp | 66 ++++++++++++--------------- dgl/src/OpenGL.cpp | 31 +++++++++++++ dgl/src/TopLevelWidget.cpp | 5 ++ dgl/src/TopLevelWidgetPrivateData.cpp | 5 ++ dgl/src/TopLevelWidgetPrivateData.hpp | 1 + dgl/src/Widget.cpp | 5 ++ dgl/src/Window.cpp | 6 +++ dgl/src/WindowPrivateData.cpp | 30 ++++++++++++ dgl/src/WindowPrivateData.hpp | 3 ++ distrho/DistrhoUI.hpp | 8 ++-- distrho/src/DistrhoUI.cpp | 55 ++++++++++++---------- distrho/src/DistrhoUIInternal.hpp | 32 +++++++++---- 16 files changed, 214 insertions(+), 78 deletions(-) diff --git a/Makefile b/Makefile index 4819c9dc..f57cc1e9 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,7 @@ include Makefile.base.mk -all: dgl -# examples gen +all: dgl examples gen # -------------------------------------------------------------- diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index f3c02a10..51e25ca4 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -19,6 +19,12 @@ #include "Widget.hpp" +#ifdef DISTRHO_DEFINES_H_INCLUDED +START_NAMESPACE_DISTRHO +class UI; +END_NAMESPACE_DISTRHO +#endif + START_NAMESPACE_DGL class Window; @@ -55,13 +61,25 @@ public: */ Application& getApp() const noexcept; + /** + Get the window associated with this top-level widget. + */ + Window& getWindow() const noexcept; + void repaint() noexcept; void repaint(const Rectangle& rect) noexcept; + // TODO deprecated + Application& getParentApp() const noexcept { return getApp(); } + Window& getParentWindow() const noexcept { return getWindow(); } + private: struct PrivateData; PrivateData* const pData; friend class Window; +#ifdef DISTRHO_DEFINES_H_INCLUDED + friend class DISTRHO_NAMESPACE::UI; +#endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) }; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 3f268edd..93f12701 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -32,7 +32,7 @@ START_NAMESPACE_DGL class Application; // class NanoWidget; -// class Window; +class Window; // class StandaloneWindow; class SubWidget; class TopLevelWidget; @@ -154,6 +154,12 @@ public: */ Application& getApp() const noexcept; + /** + Get the window associated with this widget. + This is the same as calling `getTopLevelWidget()->getWindow()`. + */ + Window& getWindow() const noexcept; + /** Get the graphics context associated with this widget. GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, @@ -174,6 +180,10 @@ public: */ virtual void repaint() noexcept; + // TODO deprecated + Application& getParentApp() const noexcept { return getApp(); } + Window& getParentWindow() const noexcept { return getWindow(); } + protected: /** A function called to draw the widget contents. diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 852c1658..9f24dea6 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -56,7 +56,17 @@ public: explicit Window(Application& app); /** - Constructor for an embed Window, typically used in modules or plugins that run inside another host. + Constructor for an embed Window without known size, + typically used in modules or plugins that run inside another host. + */ + explicit Window(Application& app, + uintptr_t parentWindowHandle, + double scaling, + bool resizable); + + /** + Constructor for an embed Window with known size, + typically used in modules or plugins that run inside another host. */ explicit Window(Application& app, uintptr_t parentWindowHandle, diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 95d15648..37cb9d35 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -15,7 +15,7 @@ */ #include "../NanoVG.hpp" -#include "WidgetPrivateData.hpp" +#include "SubWidgetPrivateData.hpp" #ifndef DGL_NO_SHARED_RESOURCES # include "Resources.hpp" @@ -956,68 +956,60 @@ struct NanoWidget::PrivateData { } }; +// ----------------------------------------------------------------------- // SubWidget -template -NanoWidget::NanoWidget(Widget* const parent, int flags) - : BaseWidget(parent), - NanoVG(flags), - nData(new PrivateData(this)) -{ - BaseWidget::pData->needsViewportScaling = true; -} -// TopLevelWidget -template -NanoWidget::NanoWidget(Window& windowToMapTo, int flags) - : BaseWidget(windowToMapTo), +template <> +NanoWidget::NanoWidget(Widget* const parent, int flags) + : SubWidget(parent), NanoVG(flags), nData(new PrivateData(this)) { + pData->needsViewportScaling = true; } -// StandaloneWindow -template -NanoWidget::NanoWidget(Application& app, int flags) - : BaseWidget(app), - NanoVG(flags), - nData(new PrivateData(this)) +template <> +NanoWidget::~NanoWidget() { + delete nData; } -/* -NanoWidget::NanoWidget(Window& parent, int flags) - : Widget(parent), +// ----------------------------------------------------------------------- +// TopLevelWidget + +template <> +NanoWidget::NanoWidget(Window& windowToMapTo, int flags) + : TopLevelWidget(windowToMapTo), NanoVG(flags), nData(new PrivateData(this)) { - pData->needsScaling = true; } -NanoWidget::NanoWidget(Widget* groupWidget, int flags) - : Widget(groupWidget, true), - NanoVG(flags), - nData(new PrivateData(this)) +template <> +NanoWidget::~NanoWidget() { - pData->needsScaling = true; + delete nData; } -NanoWidget::NanoWidget(NanoWidget* groupWidget) - : Widget(groupWidget, false), - NanoVG(groupWidget), +// ----------------------------------------------------------------------- +// StandaloneWindow + +template <> +NanoWidget::NanoWidget(Application& app, int flags) + : StandaloneWindow(app), + NanoVG(flags), nData(new PrivateData(this)) { - pData->needsScaling = true; - pData->skipDisplay = true; - groupWidget->nData->subWidgets.push_back(this); } -*/ -template -NanoWidget::~NanoWidget() +template <> +NanoWidget::~NanoWidget() { delete nData; } +// ----------------------------------------------------------------------- + template void NanoWidget::onDisplay() { diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 399fd640..8a5151c2 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -110,6 +110,37 @@ void Rectangle::_draw(const bool outline) glEnd(); } +// ----------------------------------------------------------------------- +// Possible template data types + +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + // ----------------------------------------------------------------------- OpenGLImage::OpenGLImage() diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 2c9f1c02..6b9bb825 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -35,6 +35,11 @@ Application& TopLevelWidget::getApp() const noexcept return pData->window.getApp(); } +Window& TopLevelWidget::getWindow() const noexcept +{ + return pData->window; +} + void TopLevelWidget::repaint() noexcept { pData->window.repaint(); diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index c0ea814a..9f19cd1c 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -57,6 +57,11 @@ void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) selfw->pData->giveMouseEventForSubWidgets(rev); } +void TopLevelWidget::PrivateData::fallbackOnResize() +{ + puglFallbackOnResize(window.pData->view); +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index 4e9f597e..bc4338eb 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -34,6 +34,7 @@ struct TopLevelWidget::PrivateData { ~PrivateData(); void display(); void mouseEvent(const Events::MouseEvent& ev); + void fallbackOnResize(); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 707a31c4..5b7f16e7 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -127,6 +127,11 @@ Application& Widget::getApp() const noexcept return pData->topLevelWidget->getApp(); } +Window& Widget::getWindow() const noexcept +{ + return pData->topLevelWidget->getWindow(); +} + TopLevelWidget* Widget::getTopLevelWidget() const noexcept { return pData->topLevelWidget; diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index b45b2c1a..9750c773 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -29,6 +29,12 @@ START_NAMESPACE_DGL Window::Window(Application& app) : pData(new PrivateData(app, this)) {} +Window::Window(Application& app, + const uintptr_t parentWindowHandle, + const double scaling, + const bool resizable) + : pData(new PrivateData(app, this, parentWindowHandle, scaling, resizable)) {} + Window::Window(Application& app, const uintptr_t parentWindowHandle, const uint width, diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 56bf8e57..c7491347 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -74,6 +74,36 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); } +Window::PrivateData::PrivateData(Application& a, Window* const s, + const uintptr_t parentWindowHandle, + const double scale, const bool resizable) + : app(a), + appData(a.pData), + self(s), + view(puglNewView(appData->world)), + topLevelWidget(nullptr), + isClosed(parentWindowHandle == 0), + isVisible(parentWindowHandle != 0), + isEmbed(parentWindowHandle != 0), + scaling(scale), + autoScaling(1.0), + pendingVisibility(kPendingVisibilityNone) +{ + if (isEmbed) + { + // puglSetDefaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, height); + puglSetParentWindow(view, parentWindowHandle); + } + + init(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); + + if (isEmbed) + { + appData->oneWindowShown(); + puglShow(view); + } +} + Window::PrivateData::PrivateData(Application& a, Window* const s, const uintptr_t parentWindowHandle, const uint width, const uint height, diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index b87a2718..01ce4048 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -77,6 +77,9 @@ struct Window::PrivateData : IdleCallback { /** Constructor for a regular, standalone window with a transient parent. */ explicit PrivateData(Application& app, Window* self, Window& transientWindow); + /** Constructor for an embed Window, with a few extra hints from the host side. */ + explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); + /** Constructor for an embed Window, with a few extra hints from the host side. */ explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, uint width, uint height, double scaling, bool resizable); diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 69dafdec..c5db90c3 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -26,10 +26,10 @@ typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; #elif DISTRHO_UI_USE_NANOVG # include "../dgl/NanoVG.hpp" -typedef DGL_NAMESPACE::NanoWidget UIWidget; +typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; #else -# include "../dgl/Widget.hpp" -typedef DGL_NAMESPACE::Widget UIWidget; +# include "../dgl/TopLevelWidget.hpp" +typedef DGL_NAMESPACE::TopLevelWidget UIWidget; #endif #ifdef DGL_CAIRO @@ -259,7 +259,7 @@ protected: private: struct PrivateData; - PrivateData* const pData; + PrivateData* const uiData; friend class UIExporter; friend class UIExporterWindow; diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 211cad44..474d8832 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -17,9 +17,11 @@ #include "DistrhoUIPrivateData.hpp" #include "src/WindowPrivateData.hpp" #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# include "src/WidgetPrivateData.hpp" +# include "src/TopLevelWidgetPrivateData.hpp" #endif +#include "NanoVG.hpp" + START_NAMESPACE_DISTRHO /* ------------------------------------------------------------------------------------------------------------ @@ -69,14 +71,12 @@ UI* createUiWrapper(void* const dspPtr, Window* const window) #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI UI::UI(uint width, uint height) : UIWidget(width, height), - pData(new PrivateData()) {} + uiData(new PrivateData()) {} #else UI::UI(uint width, uint height) : UIWidget(*d_lastUiWindow), - pData(new PrivateData()) + uiData(new PrivateData()) { - ((UIWidget*)this)->pData->needsFullViewport = false; - if (width > 0 && height > 0) setSize(width, height); } @@ -84,7 +84,7 @@ UI::UI(uint width, uint height) UI::~UI() { - delete pData; + delete uiData; } #if DISTRHO_UI_USER_RESIZABLE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -93,10 +93,11 @@ void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRa DISTRHO_SAFE_ASSERT_RETURN(minWidth > 0,); DISTRHO_SAFE_ASSERT_RETURN(minHeight > 0,); - pData->automaticallyScale = automaticallyScale; - pData->minWidth = minWidth; - pData->minHeight = minHeight; + uiData->automaticallyScale = automaticallyScale; + uiData->minWidth = minWidth; + uiData->minHeight = minHeight; +#if 0 /* TODO */ Window& window(getParentWindow()); const double uiScaleFactor = window.getScaling(); @@ -104,6 +105,7 @@ void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRa if (d_isNotZero(uiScaleFactor - 1.0)) setSize(getWidth() * uiScaleFactor, getHeight() * uiScaleFactor); +#endif } #endif @@ -112,47 +114,47 @@ void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRa uint UI::getBackgroundColor() const noexcept { - return pData->bgColor; + return uiData->bgColor; } uint UI::getForegroundColor() const noexcept { - return pData->fgColor; + return uiData->fgColor; } double UI::getSampleRate() const noexcept { - return pData->sampleRate; + return uiData->sampleRate; } void UI::editParameter(uint32_t index, bool started) { - pData->editParamCallback(index + pData->parameterOffset, started); + uiData->editParamCallback(index + uiData->parameterOffset, started); } void UI::setParameterValue(uint32_t index, float value) { - pData->setParamCallback(index + pData->parameterOffset, value); + uiData->setParamCallback(index + uiData->parameterOffset, value); } #if DISTRHO_PLUGIN_WANT_STATE void UI::setState(const char* key, const char* value) { - pData->setStateCallback(key, value); + uiData->setStateCallback(key, value); } #endif #if DISTRHO_PLUGIN_WANT_STATEFILES bool UI::requestStateFile(const char* key) { - return pData->fileRequestCallback(key); + return uiData->fileRequestCallback(key); } #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) { - pData->sendNoteCallback(channel, note, velocity); + uiData->sendNoteCallback(channel, note, velocity); } #endif @@ -162,7 +164,7 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) void* UI::getPluginInstancePointer() const noexcept { - return pData->dspPtr; + return uiData->dspPtr; } #endif @@ -203,9 +205,9 @@ void UI::uiFileBrowserSelected(const char*) } # endif -void UI::uiReshape(uint width, uint height) +void UI::uiReshape(uint, uint) { - Window::PrivateData::Fallback::onReshape(width, height); + pData->fallbackOnResize(); } /* ------------------------------------------------------------------------------------------------------------ @@ -213,13 +215,20 @@ void UI::uiReshape(uint width, uint height) void UI::onResize(const ResizeEvent& ev) { - if (pData->resizeInProgress) + if (uiData->resizeInProgress) return; - pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); + uiData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Possible template data types + +template class NanoWidget; +template class NanoWidget; +template class NanoWidget; diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 819ddb68..a9073268 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -66,7 +66,7 @@ public: fIsReady(false) { DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(fUI->pData != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fUI->uiData != nullptr,); setSize(fUI->getWidth(), fUI->getHeight()); } @@ -92,24 +92,27 @@ protected: { DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); - UI::PrivateData* const pData = fUI->pData; - DISTRHO_SAFE_ASSERT_RETURN(pData != nullptr,); + UI::PrivateData* const uiData = fUI->uiData; + DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,); - if (pData->automaticallyScale) +#if 0 /* TODO */ + if (uiData->automaticallyScale) { - const double scaleHorizontal = static_cast(width) / static_cast(pData->minWidth); - const double scaleVertical = static_cast(height) / static_cast(pData->minHeight); + const double scaleHorizontal = static_cast(width) / static_cast(uiData->minWidth); + const double scaleVertical = static_cast(height) / static_cast(uiData->minHeight); _setAutoScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical); } +#endif - pData->resizeInProgress = true; + uiData->resizeInProgress = true; fUI->setSize(width, height); - pData->resizeInProgress = false; + uiData->resizeInProgress = false; fUI->uiReshape(width, height); fIsReady = true; } +#if 0 /* TODO */ # ifndef DGL_FILE_BROWSER_DISABLED // custom file-browser selected void fileBrowserSelected(const char* filename) override @@ -119,6 +122,7 @@ protected: fUI->uiFileBrowserSelected(filename); } # endif +#endif private: UI* const fUI; @@ -153,7 +157,7 @@ public: fChangingSize(false), fUI(glWindow.getUI()), #endif - fData((fUI != nullptr) ? fUI->pData : nullptr) + fData((fUI != nullptr) ? fUI->uiData : nullptr) { DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); @@ -325,8 +329,8 @@ public: DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); - glWindow.addIdleCallback(cb); glWindow.setVisible(true); + glApp.addIdleCallback(cb); glApp.exec(); } @@ -338,7 +342,9 @@ public: void focus() { +#if 0 /* TODO */ glWindow.focus(); +#endif } bool idle() @@ -415,7 +421,9 @@ public: void setWindowTransientWinId(const uintptr_t winId) { +#if 0 /* TODO */ glWindow.setTransientWinId(winId); +#endif } bool setWindowVisible(const bool yesNo) @@ -427,12 +435,16 @@ public: bool handlePluginKeyboard(const bool press, const uint key) { +#if 0 /* TODO */ return glWindow.handlePluginKeyboard(press, key); +#endif } bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key) { +#if 0 /* TODO */ return glWindow.handlePluginSpecial(press, key); +#endif } #endif From af4f463f5ca0f3170067e8d094d34bc027ce918c Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 13 May 2021 21:19:16 +0100 Subject: [PATCH 055/159] Fix building tests Signed-off-by: falkTX --- dgl/src/OpenGL.cpp | 2 ++ tests/Demo.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 8a5151c2..0beb3f45 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -113,6 +113,7 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- // Possible template data types +#ifndef DPF_TEST_DEMO template class Line; template class Line; template class Line; @@ -140,6 +141,7 @@ template class Rectangle; template class Rectangle; template class Rectangle; template class Rectangle; +#endif // ----------------------------------------------------------------------- diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 89d484f5..db2564a3 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -20,7 +20,7 @@ #include "tests.hpp" -// #define DPF_TEST_POINT_CPP +#define DPF_TEST_DEMO #include "dgl/OpenGL.hpp" #include "dgl/src/pugl.cpp" #include "dgl/src/Application.cpp" From 70078d30a0c977bd2877a9599cf98781a7807cd5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 09:50:14 +0100 Subject: [PATCH 056/159] Allow *.S files; Cleanup Signed-off-by: falkTX --- Makefile.plugins.mk | 5 +++++ distrho/DistrhoUI.hpp | 10 +--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index e58176fc..5e3b9986 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -131,6 +131,11 @@ all: # --------------------------------------------------------------------------------------------------------------------- # Common +$(BUILD_DIR)/%.S.o: %.S + -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + @echo "Compiling $<" + @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ + $(BUILD_DIR)/%.c.o: %.c -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index c5db90c3..009f0f82 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -263,14 +263,6 @@ private: friend class UIExporter; friend class UIExporterWindow; -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - // these should not be used - void setAbsoluteX(int) const noexcept {} - void setAbsoluteY(int) const noexcept {} - void setAbsolutePos(int, int) const noexcept {} - void setAbsolutePos(const DGL_NAMESPACE::Point&) const noexcept {} -#endif - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) }; From 3447af0d08b56066fdecc2d01475038b71e94732 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 10:18:31 +0100 Subject: [PATCH 057/159] cleanup Signed-off-by: falkTX --- dgl/Widget.hpp | 45 +++++++---------------------------- dgl/src/Widget.cpp | 40 ++++++------------------------- dgl/src/WidgetPrivateData.cpp | 10 ++++---- 3 files changed, 20 insertions(+), 75 deletions(-) diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 93f12701..5cb69b6a 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -19,27 +19,19 @@ #include "Events.hpp" -// ----------------------------------------------------------------------- -// Forward class names - -// #ifdef DISTRHO_DEFINES_H_INCLUDED -// START_NAMESPACE_DISTRHO -// class UI; -// END_NAMESPACE_DISTRHO -// #endif - START_NAMESPACE_DGL +// -------------------------------------------------------------------------------------------------------------------- +// Forward class names + class Application; -// class NanoWidget; -class Window; -// class StandaloneWindow; class SubWidget; class TopLevelWidget; +class Window; using namespace Events; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- /** Base DGL Widget class. @@ -85,9 +77,9 @@ public: bool isVisible() const noexcept; /** - Set widget visible (or not) according to @a yesNo. + Set widget visible (or not) according to @a visible. */ - void setVisible(bool yesNo); + void setVisible(bool visible); /** Show widget. @@ -234,34 +226,13 @@ protected: private: struct PrivateData; PrivateData* const pData; - -// friend class NanoWidget; -// friend class Window; -// friend class StandaloneWindow; friend class SubWidget; friend class TopLevelWidget; -// #ifdef DISTRHO_DEFINES_H_INCLUDED -// friend class DISTRHO_NAMESPACE::UI; -// #endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) }; -#if 0 - // TODO: should we remove this? - /** - Get this widget's window application. - Same as calling getParentWindow().getApp(). - */ - Application& getParentApp() const noexcept; - - /** - Get parent window, as passed in the constructor. - */ - Window& getParentWindow() const noexcept; -#endif - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 5b7f16e7..c00e3a97 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -19,7 +19,7 @@ START_NAMESPACE_DGL -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Widget Widget::Widget(TopLevelWidget* const topLevelWidget) @@ -38,12 +38,12 @@ bool Widget::isVisible() const noexcept return pData->visible; } -void Widget::setVisible(bool yesNo) +void Widget::setVisible(bool visible) { - if (pData->visible == yesNo) + if (pData->visible == visible) return; - pData->visible = yesNo; + pData->visible = visible; repaint(); } @@ -124,11 +124,13 @@ void Widget::setSize(const Size& size) noexcept Application& Widget::getApp() const noexcept { + DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); return pData->topLevelWidget->getApp(); } Window& Widget::getWindow() const noexcept { + DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); return pData->topLevelWidget->getWindow(); } @@ -185,34 +187,6 @@ void Widget::onResize(const ResizeEvent&) { } -// ----------------------------------------------------------------------- - -// ----------------------------------------------------------------------- - -// ----------------------------------------------------------------------- - -#if 0 -Widget::Widget(Widget* groupWidget) - : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true)) -{ -} - -Widget::Widget(Widget* groupWidget, bool addToSubWidgets) - : pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets)) -{ -} - -Window& Widget::getParentWindow() const noexcept -{ - return pData->parent; -} - -void Widget::setNeedsFullViewport() -{ - pData->needsFullViewport = true; -} -#endif - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index a5e7b5fb..300f8112 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -94,12 +94,12 @@ void Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) // ----------------------------------------------------------------------- -TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const w) +TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw) { - if (w->pData->topLevelWidget != nullptr) - return w->pData->topLevelWidget; - if (w->pData->parentWidget != nullptr) - return findTopLevelWidget(w->pData->parentWidget); + if (pw->pData->topLevelWidget != nullptr) + return pw->pData->topLevelWidget; + if (pw->pData->parentWidget != nullptr) + return findTopLevelWidget(pw->pData->parentWidget); return nullptr; } From f8a71fe521c068938093d3aaed1a7dbd04a03dcd Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 12:18:00 +0100 Subject: [PATCH 058/159] Add back setGeometryConstraints and some resizing Signed-off-by: falkTX --- dgl/TopLevelWidget.hpp | 5 + dgl/Window.hpp | 81 +++++++++++++---- dgl/src/Cairo.cpp | 4 +- dgl/src/OpenGL.cpp | 66 +++++++------- dgl/src/SubWidgetPrivateData.hpp | 2 +- dgl/src/TopLevelWidget.cpp | 8 ++ dgl/src/TopLevelWidgetPrivateData.cpp | 10 +- dgl/src/WidgetPrivateData.cpp | 4 +- dgl/src/WidgetPrivateData.hpp | 2 +- dgl/src/Window.cpp | 126 ++++++++++++++------------ dgl/src/WindowPrivateData.cpp | 49 ++++++++-- dgl/src/WindowPrivateData.hpp | 12 ++- dgl/src/pugl.cpp | 87 ++++++++++++++---- dgl/src/pugl.hpp | 20 ++-- distrho/DistrhoUI.hpp | 9 -- distrho/src/DistrhoUI.cpp | 22 ----- distrho/src/DistrhoUIInternal.hpp | 11 --- 17 files changed, 322 insertions(+), 196 deletions(-) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 51e25ca4..deca78bf 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -66,8 +66,13 @@ public: */ Window& getWindow() const noexcept; + // TODO group stuff after here, convenience functions present in Window class void repaint() noexcept; void repaint(const Rectangle& rect) noexcept; + void setGeometryConstraints(uint minimumWidth, + uint minimumHeight, + bool keepAspectRatio = false, + bool automaticallyScale = false); // TODO deprecated Application& getParentApp() const noexcept { return getApp(); } diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 9f24dea6..7bfe1c90 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -124,6 +124,9 @@ public: */ void close(); + bool isResizable() const noexcept; + void setResizable(bool resizable); + /** Get width. */ @@ -159,9 +162,28 @@ public: */ void setSize(const Size& size); + /** + Get the title of the window previously set with setTitle(). + */ const char* getTitle() const noexcept; + + /** + Set the title of the window, typically displayed in the title bar or in window switchers. + + This only makes sense for non-embedded windows. + */ void setTitle(const char* title); + /** + Check if key repeat events are ignored. + */ + bool isIgnoringKeyRepeat() const noexcept; + + /** + Set to ignore (or not) key repeat events according to @a ignore. + */ + void setIgnoringKeyRepeat(bool ignore) noexcept; + /** Get the application associated with this window. */ @@ -177,9 +199,47 @@ public: */ uintptr_t getNativeWindowHandle() const noexcept; + /** + Get the scale factor requested for this window. + This is purely informational, and up to developers to choose what to do with it. + + If you do not want to deal with this yourself, + consider using setGeometryConstraints() where you can specify to automatically scale the window contents. + @see setGeometryConstraints + */ + double getScaleFactor() const noexcept; + + /** + Grab the keyboard input focus. + */ + void focus(); + + /** + Request repaint of this window, for the entire area. + */ void repaint() noexcept; + + /** + Request partial repaint of this window, with bounds according to @a rect. + */ void repaint(const Rectangle& rect) noexcept; + /** + Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. + */ + void setGeometryConstraints(uint minimumWidth, + uint minimumHeight, + bool keepAspectRatio = false, + bool automaticallyScale = false); + + /* + void setTransientWinId(uintptr_t winId); + */ + + // TODO deprecated + inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } + inline double getScaling() const noexcept { return getScaling(); } + protected: /** A function called when the window is resized. @@ -266,28 +326,15 @@ END_NAMESPACE_DGL void exec(bool lockWait = false); - void focus(); - -#ifndef DGL_FILE_BROWSER_DISABLED - bool openFileBrowser(const FileBrowserOptions& options); -#endif - - bool isResizable() const noexcept; - void setResizable(bool resizable); - - bool getIgnoringKeyRepeat() const noexcept; - void setIgnoringKeyRepeat(bool ignore) noexcept; - - void setGeometryConstraints(uint width, uint height, bool aspect); - void setTransientWinId(uintptr_t winId); - - double getScaling() const noexcept; - const GraphicsContext& getGraphicsContext() const noexcept; void addIdleCallback(IdleCallback* const callback); void removeIdleCallback(IdleCallback* const callback); +#ifndef DGL_FILE_BROWSER_DISABLED + bool openFileBrowser(const FileBrowserOptions& options); +#endif + protected: #ifndef DGL_FILE_BROWSER_DISABLED diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 303244e9..1f423c0d 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -65,7 +65,7 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- -void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaling) +void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) { /* if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) @@ -83,7 +83,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const cairo_set_matrix(cr, &matrix); -// displaySubWidgets(width, height, autoScaling); +// displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 0beb3f45..13021068 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -327,7 +327,7 @@ void OpenGLImage::drawAt(const Point& pos) #if 0 void Widget::PrivateData::display(const uint width, const uint height, - const double autoScaling, + const double autoScaleFactor, const bool renderingSubWidget) { printf("Widget::PrivateData::display INIT\n"); @@ -346,9 +346,9 @@ void Widget::PrivateData::display(const uint width, { // full viewport size glViewport(0, - -(height * autoScaling - height), - width * autoScaling, - height * autoScaling); + -(height * autoScaleFactor - height), + width * autoScaleFactor, + height * autoScaleFactor); } #if 0 else if (needsScaling) @@ -362,16 +362,16 @@ void Widget::PrivateData::display(const uint width, else { // only set viewport pos - glViewport(absolutePos.getX() * autoScaling, - -std::round((height * autoScaling - height) + (absolutePos.getY() * autoScaling)), - std::round(width * autoScaling), - std::round(height * autoScaling)); + glViewport(absolutePos.getX() * autoScaleFactor, + -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), + std::round(width * autoScaleFactor), + std::round(height * autoScaleFactor)); // then cut the outer bounds - glScissor(absolutePos.getX() * autoScaling, - height - std::round((self->getHeight() + absolutePos.getY()) * autoScaling), - std::round(self->getWidth() * autoScaling), - std::round(self->getHeight() * autoScaling)); + glScissor(absolutePos.getX() * autoScaleFactor, + height - std::round((self->getHeight() + absolutePos.getY()) * autoScaleFactor), + std::round(self->getWidth() * autoScaleFactor), + std::round(self->getHeight() * autoScaleFactor)); glEnable(GL_SCISSOR_TEST); needsDisableScissor = true; @@ -387,11 +387,11 @@ void Widget::PrivateData::display(const uint width, needsDisableScissor = false; } - displaySubWidgets(width, height, autoScaling); + displaySubWidgets(width, height, autoScaleFactor); } #endif -void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaling) +void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) { bool needsDisableScissor = false; @@ -399,9 +399,9 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const { // full viewport size glViewport(0, - -(height * autoScaling - height), - width * autoScaling, - height * autoScaling); + -(height * autoScaleFactor - height), + width * autoScaleFactor, + height * autoScaleFactor); } else if (needsViewportScaling) { @@ -414,16 +414,16 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const else { // only set viewport pos - glViewport(absolutePos.getX() * autoScaling, - -std::round((height * autoScaling - height) + (absolutePos.getY() * autoScaling)), - std::round(width * autoScaling), - std::round(height * autoScaling)); + glViewport(absolutePos.getX() * autoScaleFactor, + -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), + std::round(width * autoScaleFactor), + std::round(height * autoScaleFactor)); // then cut the outer bounds - glScissor(absolutePos.getX() * autoScaling, - height - std::round((self->getHeight() + absolutePos.getY()) * autoScaling), - std::round(self->getWidth() * autoScaling), - std::round(self->getHeight() * autoScaling)); + glScissor(absolutePos.getX() * autoScaleFactor, + height - std::round((self->getHeight() + absolutePos.getY()) * autoScaleFactor), + std::round(self->getWidth() * autoScaleFactor), + std::round(self->getHeight() * autoScaleFactor)); glEnable(GL_SCISSOR_TEST); needsDisableScissor = true; @@ -438,7 +438,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const needsDisableScissor = false; } -// displaySubWidgets(width, height, autoScaling); +// displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- @@ -446,18 +446,22 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const void TopLevelWidget::PrivateData::display() { const Size size(window.getSize()); - const uint width = size.getWidth(); - const uint height = size.getHeight(); - const double autoScaling = window.pData->autoScaling; + const uint width = size.getWidth(); + const uint height = size.getHeight(); + + const double autoScaleFactor = window.pData->autoScaleFactor; // full viewport size - glViewport(0, -(height * autoScaling - height), width * autoScaling, height * autoScaling); + if (window.pData->autoScaling) + glViewport(0, -height, width, height); + else + glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); // main widget drawing self->onDisplay(); // now draw subwidgets if there are any - selfw->pData->displaySubWidgets(width, height, autoScaling); + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index 5e0e5cf5..8a219f52 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -33,7 +33,7 @@ struct SubWidget::PrivateData { ~PrivateData(); // NOTE display function is different depending on build type, must call displaySubWidgets at the end - void display(uint width, uint height, double autoScaling); + void display(uint width, uint height, double autoScaleFactor); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 6b9bb825..85612ef5 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -50,6 +50,14 @@ void TopLevelWidget::repaint(const Rectangle& rect) noexcept pData->window.repaint(rect); } +void TopLevelWidget::setGeometryConstraints(const uint minimumWidth, + const uint minimumHeight, + const bool keepAspectRatio, + const bool automaticallyScale) +{ + pData->window.setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); +} + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 9f19cd1c..9edc6646 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -41,12 +41,12 @@ void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) { Events::MouseEvent rev = ev; - const double autoScaling = window.pData->autoScaling; - - if (autoScaling != 1.0) + if (window.pData->autoScaling) { - rev.pos.setX(ev.pos.getX() / autoScaling); - rev.pos.setY(ev.pos.getY() / autoScaling); + const double autoScaleFactor = window.pData->autoScaleFactor; + + rev.pos.setX(ev.pos.getX() / autoScaleFactor); + rev.pos.setY(ev.pos.getY() / autoScaleFactor); } // give top-level widget chance to catch this event first diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 300f8112..8ee47b22 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -53,7 +53,7 @@ Widget::PrivateData::~PrivateData() subWidgets.clear(); } -void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double scaling) +void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) { if (subWidgets.size() == 0) return; @@ -63,7 +63,7 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, SubWidget* const subwidget(*it); if (subwidget->isVisible()) - subwidget->pData->display(width, height, scaling); + subwidget->pData->display(width, height, autoScaleFactor); } } diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index fa2e6f87..2fcf64e7 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -41,7 +41,7 @@ struct Widget::PrivateData { explicit PrivateData(Widget* const s, Widget* const pw); ~PrivateData(); - void displaySubWidgets(uint width, uint height, double autoScaling); + void displaySubWidgets(uint width, uint height, double autoScaleFactor); void giveMouseEventForSubWidgets(Events::MouseEvent& ev); static TopLevelWidget* findTopLevelWidget(Widget* const w); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 9750c773..afa7e187 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -81,6 +81,16 @@ void Window::close() pData->close(); } +bool Window::isResizable() const noexcept +{ + return puglGetViewHint(pData->view, PUGL_RESIZABLE) == PUGL_TRUE; +} + +void Window::setResizable(const bool resizable) +{ + pData->setResizable(resizable); +} + uint Window::getWidth() const noexcept { return puglGetFrame(pData->view).width; @@ -129,6 +139,16 @@ void Window::setTitle(const char* const title) puglSetWindowTitle(pData->view, title); } +bool Window::isIgnoringKeyRepeat() const noexcept +{ + return puglGetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT) == PUGL_TRUE; +} + +void Window::setIgnoringKeyRepeat(const bool ignore) noexcept +{ + puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); +} + Application& Window::getApp() const noexcept { return pData->app; @@ -139,6 +159,19 @@ uintptr_t Window::getNativeWindowHandle() const noexcept return puglGetNativeWindow(pData->view); } +double Window::getScaleFactor() const noexcept +{ + return pData->scaleFactor; +} + +void Window::focus() +{ + if (! pData->isEmbed) + puglRaiseWindow(pData->view); + + puglGrabFocus(pData->view); +} + void Window::repaint() noexcept { puglPostRedisplay(pData->view); @@ -155,82 +188,64 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } -void Window::onReshape(uint, uint) +void Window::setGeometryConstraints(const uint minimumWidth, + const uint minimumHeight, + const bool keepAspectRatio, + const bool automaticallyScale) { - puglFallbackOnResize(pData->view); -} + DISTRHO_SAFE_ASSERT_RETURN(minimumWidth > 0,); + DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,); -bool Window::onClose() -{ - return true; -} - -#if 0 -#if 0 -void Window::exec(bool lockWait) -{ - pData->exec(lockWait); -} -#endif + if (pData->isEmbed) { + // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? + DISTRHO_SAFE_ASSERT_RETURN(isResizable(),); + } else if (! isResizable()) { + setResizable(true); + } -void Window::focus() -{ - if (! pData->fUsingEmbed) - puglRaiseWindow(pData->fView); + pData->minWidth = minimumWidth; + pData->minHeight = minimumHeight; + pData->autoScaling = automaticallyScale; - puglGrabFocus(pData->fView); -} + const double scaleFactor = pData->scaleFactor; -bool Window::isResizable() const noexcept -{ - return puglGetViewHint(pData->fView, PUGL_RESIZABLE) == PUGL_TRUE; -} + puglSetGeometryConstraints(pData->view, + minimumWidth * scaleFactor, + minimumHeight * scaleFactor, + keepAspectRatio); -void Window::setResizable(const bool resizable) -{ - DISTRHO_SAFE_ASSERT_RETURN(pData->fUsingEmbed,); - if (pData->fUsingEmbed) + if (scaleFactor != 1.0) { - DGL_DBG("Window setResizable cannot be called when embedded\n"); - return; - } + const Size size(getSize()); - DGL_DBG("Window setResizable called\n"); - - puglSetViewHint(pData->fView, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); -#ifdef DISTRHO_OS_WINDOWS - puglWin32SetWindowResizable(pData->fView, resizable); -#endif + setSize(size.getWidth() * scaleFactor, + size.getHeight() * scaleFactor); + } } -bool Window::getIgnoringKeyRepeat() const noexcept +void Window::onReshape(uint, uint) { - return puglGetViewHint(pData->fView, PUGL_IGNORE_KEY_REPEAT) == PUGL_TRUE; + puglFallbackOnResize(pData->view); } -void Window::setIgnoringKeyRepeat(const bool ignore) noexcept +bool Window::onClose() { - puglSetViewHint(pData->fView, PUGL_IGNORE_KEY_REPEAT, ignore); + return true; } -void Window::setGeometryConstraints(const uint width, const uint height, bool aspect) +#if 0 +#if 0 +void Window::exec(bool lockWait) { - // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? - DISTRHO_SAFE_ASSERT_RETURN(isResizable(),); - - puglUpdateGeometryConstraints(pData->fView, width, height, aspect); + pData->exec(lockWait); } +#endif void Window::setTransientWinId(const uintptr_t winId) { puglSetTransientFor(pData->fView, winId); } -double Window::getScaling() const noexcept -{ - return pData->fScaling; -} - #if 0 Application& Window::getApp() const noexcept { @@ -238,13 +253,6 @@ Application& Window::getApp() const noexcept } #endif -void Window::_setAutoScaling(double scaling) noexcept -{ - DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); - - pData->fAutoScaling = scaling; -} - void Window::_addWidget(Widget* const widget) { pData->addWidget(widget); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index c7491347..fc92e4ea 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -49,8 +49,11 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) isClosed(true), isVisible(false), isEmbed(false), - scaling(1.0), - autoScaling(1.0), + scaleFactor(1.0), + autoScaling(false), + autoScaleFactor(1.0), + minWidth(0), + minHeight(0), pendingVisibility(kPendingVisibilityNone) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -65,8 +68,11 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi isClosed(true), isVisible(false), isEmbed(false), - scaling(1.0), - autoScaling(1.0), + scaleFactor(1.0), + autoScaling(false), + autoScaleFactor(1.0), + minWidth(0), + minHeight(0), pendingVisibility(kPendingVisibilityNone) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -85,8 +91,11 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), - scaling(scale), - autoScaling(1.0), + scaleFactor(scale), + autoScaling(false), + autoScaleFactor(1.0), + minWidth(0), + minHeight(0), pendingVisibility(kPendingVisibilityNone) { if (isEmbed) @@ -116,8 +125,11 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), - scaling(scale), - autoScaling(1.0), + scaleFactor(scale), + autoScaling(false), + autoScaleFactor(1.0), + minWidth(0), + minHeight(0), pendingVisibility(kPendingVisibilityNone) { if (isEmbed) @@ -293,6 +305,20 @@ void Window::PrivateData::close() // ----------------------------------------------------------------------- +void Window::PrivateData::setResizable(const bool resizable) +{ + DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,); + + DGL_DBG("Window setResizable called\n"); + + puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); +#ifdef DISTRHO_OS_WINDOWS + puglWin32SetWindowResizable(view, resizable); +#endif +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::idleCallback() { // #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) @@ -329,6 +355,13 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); + if (autoScaling) + { + const double scaleHorizontal = static_cast(width) / static_cast(minWidth); + const double scaleVertical = static_cast(height) / static_cast(minHeight); + autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical; + } + self->onReshape(width, height); #ifndef DPF_TEST_WINDOW_CPP diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 01ce4048..78c6f50c 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -58,11 +58,15 @@ struct Window::PrivateData : IdleCallback { /** Whether this Window is embed into another (usually not DGL-controlled) Window. */ const bool isEmbed; - /** Scaling to report to widgets on request, purely informational. */ - double scaling; + /** Scale factor to report to widgets on request, purely informational. */ + double scaleFactor; /** Automatic scaling to apply on widgets, implemented internally. */ - double autoScaling; + bool autoScaling; + double autoScaleFactor; + + /** Pugl minWidth, minHeight access. */ + uint minWidth, minHeight; /** Pending state of visility, used for the action to be triggered during Pugl create events. */ enum PendingVisibility { @@ -102,6 +106,8 @@ struct Window::PrivateData : IdleCallback { */ void close(); + void setResizable(bool resizable); + const GraphicsContext& getGraphicsContext() const noexcept; void idleCallback() override; diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 507c0411..49bb29ca 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -85,6 +85,14 @@ START_NAMESPACE_DGL #include "pugl-upstream/src/implementation.c" +// -------------------------------------------------------------------------------------------------------------------- +// expose backend enter + +void puglBackendEnter(PuglView* view) +{ + view->backend->enter(view, NULL); +} + // -------------------------------------------------------------------------------------------------------------------- // missing in pugl, directly returns title char* pointer @@ -94,11 +102,68 @@ const char* puglGetWindowTitle(const PuglView* view) } // -------------------------------------------------------------------------------------------------------------------- -// expose backend enter +// bring view window into the foreground, aka "raise" window -void puglBackendEnter(PuglView* view) +void puglRaiseWindow(PuglView* view) { - view->backend->enter(view, NULL); +#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) + // nothing here yet +#elif defined(DISTRHO_OS_WINDOWS) + SetForegroundWindow(view->impl->hwnd); + SetActiveWindow(view->impl->hwnd); +#else + XRaiseWindow(view->impl->display, view->impl->win); +#endif +} + +// -------------------------------------------------------------------------------------------------------------------- +// set backend that matches current build + +void puglSetMatchingBackendForCurrentBuild(PuglView* view) +{ +#ifdef DGL_CAIRO + puglSetBackend(view, puglCairoBackend()); +#endif +#ifdef DGL_OPENGL + puglSetBackend(view, puglGlBackend()); +#endif +#ifdef DGL_Vulkan + puglSetBackend(view, puglVulkanBackend()); +#endif +} + +// -------------------------------------------------------------------------------------------------------------------- +// Combine puglSetMinSize and puglSetAspectRatio + +PuglStatus puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) +{ + view->minWidth = width; + view->minHeight = height; + + if (aspect) { + view->minAspectX = width; + view->minAspectY = height; + view->maxAspectX = width; + view->maxAspectY = height; + } + +#if defined(DISTRHO_OS_HAIKU) + // nothing? +#elif defined(DISTRHO_OS_MAC) + if (view->impl->window) + { + [view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; + + if (aspect) + [view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)]; + } +#elif defined(DISTRHO_OS_WINDOWS) + // nothing +#else + return updateSizeHints(view); +#endif + + return PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- @@ -161,22 +226,6 @@ PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int he return PUGL_SUCCESS; } -// -------------------------------------------------------------------------------------------------------------------- -// set backend that matches current build - -void puglSetMatchingBackendForCurrentBuild(PuglView* view) -{ -#ifdef DGL_CAIRO - puglSetBackend(view, puglCairoBackend()); -#endif -#ifdef DGL_OPENGL - puglSetBackend(view, puglGlBackend()); -#endif -#ifdef DGL_Vulkan - puglSetBackend(view, puglVulkanBackend()); -#endif -} - // -------------------------------------------------------------------------------------------------------------------- // DGL specific, build-specific drawing prepare diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 959ce560..1bc9a632 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -33,22 +33,30 @@ START_NAMESPACE_DGL PUGL_BEGIN_DECLS +// expose backend enter +PUGL_API void +puglBackendEnter(PuglView* view); + // missing in pugl, directly returns title char* pointer PUGL_API const char* puglGetWindowTitle(const PuglView* view); -// expose backend enter +// bring view window into the foreground, aka "raise" window PUGL_API void -puglBackendEnter(PuglView* view); - -// set window size without changing frame x/y position -PUGL_API PuglStatus -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); +puglRaiseWindow(PuglView* view); // DGL specific, assigns backend that matches current DGL build PUGL_API void puglSetMatchingBackendForCurrentBuild(PuglView* view); +// Combine puglSetMinSize and puglSetAspectRatio +PUGL_API PuglStatus +puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); + +// set window size without changing frame x/y position +PUGL_API PuglStatus +puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); + // DGL specific, build-specific drawing prepare PUGL_API void puglOnDisplayPrepare(PuglView* view); diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 009f0f82..9db5cd41 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -69,15 +69,6 @@ public: */ virtual ~UI(); -#if DISTRHO_UI_USER_RESIZABLE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - /** - Set geometry constraints for the UI when resized by the user, and optionally scale UI automatically. - @see Window::setGeometryConstraints(uint,uint,bool) - @see Window::setScaling(double) - */ - void setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale = false); -#endif - /* -------------------------------------------------------------------------------------------------------- * Host state */ diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 474d8832..c6a07a31 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -87,28 +87,6 @@ UI::~UI() delete uiData; } -#if DISTRHO_UI_USER_RESIZABLE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale) -{ - DISTRHO_SAFE_ASSERT_RETURN(minWidth > 0,); - DISTRHO_SAFE_ASSERT_RETURN(minHeight > 0,); - - uiData->automaticallyScale = automaticallyScale; - uiData->minWidth = minWidth; - uiData->minHeight = minHeight; - -#if 0 /* TODO */ - Window& window(getParentWindow()); - - const double uiScaleFactor = window.getScaling(); - window.setGeometryConstraints(minWidth * uiScaleFactor, minHeight * uiScaleFactor, keepAspectRatio); - - if (d_isNotZero(uiScaleFactor - 1.0)) - setSize(getWidth() * uiScaleFactor, getHeight() * uiScaleFactor); -#endif -} -#endif - /* ------------------------------------------------------------------------------------------------------------ * Host state */ diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index a9073268..73b923ad 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -95,15 +95,6 @@ protected: UI::PrivateData* const uiData = fUI->uiData; DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,); -#if 0 /* TODO */ - if (uiData->automaticallyScale) - { - const double scaleHorizontal = static_cast(width) / static_cast(uiData->minWidth); - const double scaleVertical = static_cast(height) / static_cast(uiData->minHeight); - _setAutoScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical); - } -#endif - uiData->resizeInProgress = true; fUI->setSize(width, height); uiData->resizeInProgress = false; @@ -342,9 +333,7 @@ public: void focus() { -#if 0 /* TODO */ glWindow.focus(); -#endif } bool idle() From 6bf22d0fc148bfbcbd133b0a81e0ce6d007c3d69 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 20:57:55 +0100 Subject: [PATCH 059/159] Make ImageWidgets build again Signed-off-by: falkTX --- dgl/ImageWidgets.hpp | 50 +++++--------- dgl/Makefile | 1 + dgl/SubWidget.hpp | 5 ++ dgl/src/Common.hpp | 10 +-- dgl/src/ImageWidgets.cpp | 141 +++++++-------------------------------- dgl/src/SubWidget.cpp | 16 +++++ 6 files changed, 68 insertions(+), 155 deletions(-) diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index a88bc3ac..ec465320 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -18,36 +18,29 @@ #define DGL_IMAGE_WIDGETS_HPP_INCLUDED #include "Image.hpp" -#include "Widget.hpp" -#include "Window.hpp" +#include "StandaloneWindow.hpp" +#include "SubWidget.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -#ifndef DISTRHO_OS_HAIKU -class ImageAboutWindow : public Window, - public Widget -#else -// crash when creating or opening 2nd window -class ImageAboutWindow -#endif +class ImageAboutWindow : public StandaloneWindow { public: - explicit ImageAboutWindow(Window& parent, const Image& image = Image()); - explicit ImageAboutWindow(Widget* widget, const Image& image = Image()); + explicit ImageAboutWindow(Window& parentWindow, const Image& image = Image()); + explicit ImageAboutWindow(TopLevelWidget* parentTopLevelWidget, const Image& image = Image()); void setImage(const Image& image); -#ifndef DISTRHO_OS_HAIKU + // TODO + void exec() {} + protected: void onDisplay() override; bool onKeyboard(const KeyboardEvent&) override; bool onMouse(const MouseEvent&) override; void onReshape(uint width, uint height) override; -#else - void exec() {} -#endif private: Image fImgBackground; @@ -57,7 +50,7 @@ private: // ----------------------------------------------------------------------- -class ImageButton : public Widget +class ImageButton : public SubWidget { public: class Callback @@ -67,13 +60,9 @@ public: virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; }; - explicit ImageButton(Window& parent, const Image& image); - explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown); - explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown); - - explicit ImageButton(Widget* widget, const Image& image); - explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown); - explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); + explicit ImageButton(Widget* parentWidget, const Image& image); + explicit ImageButton(Widget* parentWidget, const Image& imageNormal, const Image& imageDown); + explicit ImageButton(Widget* parentWidget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); ~ImageButton() override; @@ -93,7 +82,7 @@ private: // ----------------------------------------------------------------------- -class ImageKnob : public Widget +class ImageKnob : public SubWidget { public: enum Orientation { @@ -110,8 +99,7 @@ public: virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; }; - explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept; - explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept; + explicit ImageKnob(Widget* parentWidget, const Image& image, Orientation orientation = Vertical) noexcept; explicit ImageKnob(const ImageKnob& imageKnob); ImageKnob& operator=(const ImageKnob& imageKnob); ~ImageKnob() override; @@ -172,7 +160,7 @@ private: // note set range and step before setting the value -class ImageSlider : public Widget +class ImageSlider : public SubWidget { public: class Callback @@ -184,8 +172,7 @@ public: virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; }; - explicit ImageSlider(Window& parent, const Image& image) noexcept; - explicit ImageSlider(Widget* widget, const Image& image) noexcept; + explicit ImageSlider(Widget* parentWidget, const Image& image) noexcept; float getValue() const noexcept; void setValue(float value, bool sendCallback = false) noexcept; @@ -242,7 +229,7 @@ private: // ----------------------------------------------------------------------- -class ImageSwitch : public Widget +class ImageSwitch : public SubWidget { public: class Callback @@ -252,8 +239,7 @@ public: virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0; }; - explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept; - explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept; + explicit ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept; explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept; ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept; diff --git a/dgl/Makefile b/dgl/Makefile index 9adc3f0c..b8a3db4f 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -59,6 +59,7 @@ OBJS_cairo = $(OBJS_common) \ # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ + ../build/dgl/ImageWidgets.cpp.o \ ../build/dgl/OpenGL.cpp.opengl.o \ ../build/dgl/NanoVG.cpp.opengl.o \ ../build/dgl/pugl.cpp.opengl.o diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 8969ea8e..83118d32 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -109,6 +109,11 @@ public: */ void setAbsolutePos(const Point& pos) noexcept; + /** + Get parent Widget, as passed in the constructor. + */ + Widget* getParentWidget() const noexcept; + /** Request repaint of this subwidget's area to the window this widget belongs to. */ diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index fd1070a0..e4e4d552 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -32,17 +32,17 @@ struct ButtonImpl { int button; int state; - Widget* self; + SubWidget* self; ImageButton::Callback* callback_img; - ButtonImpl(Widget* const s) noexcept + ButtonImpl(SubWidget* const s) noexcept : button(-1), state(kStateNormal), self(s), callback_img(nullptr) {} - bool onMouse(const Widget::MouseEvent& ev) + bool onMouse(const Events::MouseEvent& ev) { // button was released, handle it now if (button != -1 && ! ev.press) @@ -83,7 +83,7 @@ struct ButtonImpl { return false; } - bool onMotion(const Widget::MotionEvent& ev) + bool onMotion(const Events::MotionEvent& ev) { // keep pressed if (button != -1) diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index b7c2f0db..f19c1894 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -25,30 +25,25 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -#ifndef DISTRHO_OS_HAIKU -ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image) - : Window(parent), - Widget((Window&)*this), +ImageAboutWindow::ImageAboutWindow(Window& parentWindow, const Image& image) + : StandaloneWindow(parentWindow.getApp()), fImgBackground(image) { + // TODO set transient Window::setResizable(false); Window::setSize(image.getSize()); Window::setTitle("About"); } -ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) - : Window(widget->getParentWindow()), - Widget((Window&)*this), +ImageAboutWindow::ImageAboutWindow(TopLevelWidget* const parentTopLevelWidget, const Image& image) + : StandaloneWindow(parentTopLevelWidget->getApp()), fImgBackground(image) { + // TODO set transient Window::setResizable(false); Window::setSize(image.getSize()); Window::setTitle("About"); } -#else -ImageAboutWindow::ImageAboutWindow(Window& parent, const Image& image) : fImgBackground(image) {} -ImageAboutWindow::ImageAboutWindow(Widget* widget, const Image& image) : fImgBackground(image) {} -#endif void ImageAboutWindow::setImage(const Image& image) { @@ -56,12 +51,9 @@ void ImageAboutWindow::setImage(const Image& image) return; fImgBackground = image; -#ifndef DISTRHO_OS_HAIKU Window::setSize(image.getSize()); -#endif } -#ifndef DISTRHO_OS_HAIKU void ImageAboutWindow::onDisplay() { fImgBackground.draw(); @@ -94,7 +86,6 @@ void ImageAboutWindow::onReshape(uint width, uint height) Widget::setSize(width, height); Window::onReshape(width, height); } -#endif // ----------------------------------------------------------------------- @@ -104,7 +95,7 @@ struct ImageButton::PrivateData { Image imageHover; Image imageDown; - PrivateData(Widget* const s, const Image& normal, const Image& hover, const Image& down) + PrivateData(SubWidget* const s, const Image& normal, const Image& hover, const Image& down) : impl(s), imageNormal(normal), imageHover(hover), @@ -115,40 +106,15 @@ struct ImageButton::PrivateData { // ----------------------------------------------------------------------- -ImageButton::ImageButton(Window& parent, const Image& image) - : Widget(parent), - pData(new PrivateData(this, image, image, image)) -{ - setSize(image.getSize()); -} - -ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown) - : Widget(parent), - pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) -{ - DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); - - setSize(imageNormal.getSize()); -} - -ImageButton::ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown) - : Widget(parent), - pData(new PrivateData(this, imageNormal, imageHover, imageDown)) -{ - DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); - - setSize(imageNormal.getSize()); -} - -ImageButton::ImageButton(Widget* widget, const Image& image) - : Widget(widget->getParentWindow()), +ImageButton::ImageButton(Widget* const parentWidget, const Image& image) + : SubWidget(parentWidget), pData(new PrivateData(this, image, image, image)) { setSize(image.getSize()); } -ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown) - : Widget(widget->getParentWindow()), +ImageButton::ImageButton(Widget* const parentWidget, const Image& imageNormal, const Image& imageDown) + : SubWidget(parentWidget), pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); @@ -156,8 +122,8 @@ ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& setSize(imageNormal.getSize()); } -ImageButton::ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) - : Widget(widget->getParentWindow()), +ImageButton::ImageButton(Widget* const parentWidget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) + : SubWidget(parentWidget), pData(new PrivateData(this, imageNormal, imageHover, imageDown)) { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); @@ -203,36 +169,8 @@ bool ImageButton::onMotion(const MotionEvent& ev) // ----------------------------------------------------------------------- -ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation) noexcept - : Widget(parent), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fUsingLog(false), - fOrientation(orientation), - fRotationAngle(0), - fDragging(false), - fLastX(0), - fLastY(0), - fCallback(nullptr), - fIsImgVertical(image.getHeight() > image.getWidth()), - fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), - fImgLayerHeight(fImgLayerWidth), - fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), - fIsReady(false), - fTextureId(0) -{ - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); -} - -ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation) noexcept - : Widget(widget->getParentWindow()), +ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept + : SubWidget(parentWidget), fImage(image), fMinimum(0.0f), fMaximum(1.0f), @@ -260,7 +198,7 @@ ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation } ImageKnob::ImageKnob(const ImageKnob& imageKnob) - : Widget(imageKnob.getParentWindow()), + : SubWidget(imageKnob.getParentWidget()), fImage(imageKnob.fImage), fMinimum(imageKnob.fMinimum), fMaximum(imageKnob.fMaximum), @@ -655,31 +593,8 @@ float ImageKnob::_invlogscale(float value) const // ----------------------------------------------------------------------- -ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept - : Widget(parent), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fDragging(false), - fInverted(false), - fValueIsSet(false), - fStartedX(0), - fStartedY(0), - fCallback(nullptr), - fStartPos(), - fEndPos(), - fSliderArea() -{ - setNeedsFullViewport(); -} - -ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept - : Widget(widget->getParentWindow()), +ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept + : SubWidget(parentWidget), fImage(image), fMinimum(0.0f), fMaximum(1.0f), @@ -698,7 +613,9 @@ ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept fEndPos(), fSliderArea() { + /* TODO setNeedsFullViewport(); + */ } float ImageSlider::getValue() const noexcept @@ -1008,20 +925,8 @@ void ImageSlider::_recheckArea() noexcept // ----------------------------------------------------------------------- -ImageSwitch::ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept - : Widget(parent), - fImageNormal(imageNormal), - fImageDown(imageDown), - fIsDown(false), - fCallback(nullptr) -{ - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); -} - -ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept - : Widget(widget->getParentWindow()), +ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept + : SubWidget(parentWidget), fImageNormal(imageNormal), fImageDown(imageDown), fIsDown(false), @@ -1033,7 +938,7 @@ ImageSwitch::ImageSwitch(Widget* widget, const Image& imageNormal, const Image& } ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept - : Widget(imageSwitch.getParentWindow()), + : SubWidget(imageSwitch.getParentWidget()), fImageNormal(imageSwitch.fImageNormal), fImageDown(imageSwitch.fImageDown), fIsDown(imageSwitch.fIsDown), diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index b694dd33..6cd8e6d9 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -100,6 +100,11 @@ void SubWidget::setAbsolutePos(const Point& pos) noexcept pData->parentWidget->repaint(); } +Widget* SubWidget::getParentWidget() const noexcept +{ + return pData->parentWidget; +} + void SubWidget::repaint() noexcept { if (! isVisible()) @@ -113,6 +118,17 @@ void SubWidget::onPositionChanged(const PositionChangedEvent&) { } +// -------------------------------------------------------------------------------------------------------------------- +// Possible template data types + +template<> +bool SubWidget::contains(const Point& pos) const noexcept +{ + return contains(pos.getX(), pos.getY()); +} + +// float, int, uint, short, ushort + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL From 38aa7b12a3e0036c443a1e066ed8c23693888e9a Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 21:04:06 +0100 Subject: [PATCH 060/159] Backwards-compatible HAVE_DGL Signed-off-by: falkTX --- Makefile.base.mk | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index 042e6c1c..5eaea285 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -313,6 +313,19 @@ LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo) LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo) endif +# --------------------------------------------------------------------------------------------------------------------- +# Backwards-compatible HAVE_DGL + +ifeq ($(MACOS_OR_WINDOWS),true) +HAVE_DGL = true +else ifeq ($(HAVE_OPENGL),true) +ifeq ($(HAIKU),true) +HAVE_DGL = true +else +HAVE_DGL = $(HAVE_X11) +endif +endif + # --------------------------------------------------------------------------------------------------------------------- # Set app extension From 18b5b5e50b3913b7ea09c011c85f717a0e95dadb Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 21:06:18 +0100 Subject: [PATCH 061/159] Cleanup Signed-off-by: falkTX --- dgl/src/Window.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index afa7e187..8f479306 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -246,13 +246,6 @@ void Window::setTransientWinId(const uintptr_t winId) puglSetTransientFor(pData->fView, winId); } -#if 0 -Application& Window::getApp() const noexcept -{ - return pData->fApp; -} -#endif - void Window::_addWidget(Widget* const widget) { pData->addWidget(widget); From 67f04d3d6aed251ae3af46f7be6e50c33263666d Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 21:12:42 +0100 Subject: [PATCH 062/159] Fix example plugins build with latest changes Signed-off-by: falkTX --- Makefile.plugins.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 5e3b9986..41b05ce9 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -71,6 +71,7 @@ vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) # Handle UI stuff, disable UI support automatically ifeq ($(FILES_UI),) +HAVE_DGL = false UI_TYPE = none endif From aa897a365a4d1796f6f456ee88593b972f683017 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 22:11:39 +0100 Subject: [PATCH 063/159] Fix an assertion and invalid window sizes Signed-off-by: falkTX --- dgl/src/ImageWidgets.cpp | 8 ++++++-- dgl/src/Window.cpp | 4 ++++ distrho/src/DistrhoUI.cpp | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index f19c1894..80221b23 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -31,8 +31,10 @@ ImageAboutWindow::ImageAboutWindow(Window& parentWindow, const Image& image) { // TODO set transient Window::setResizable(false); - Window::setSize(image.getSize()); Window::setTitle("About"); + + if (image.isValid()) + Window::setSize(image.getSize()); } ImageAboutWindow::ImageAboutWindow(TopLevelWidget* const parentTopLevelWidget, const Image& image) @@ -41,8 +43,10 @@ ImageAboutWindow::ImageAboutWindow(TopLevelWidget* const parentTopLevelWidget, c { // TODO set transient Window::setResizable(false); - Window::setSize(image.getSize()); Window::setTitle("About"); + + if (image.isValid()) + Window::setSize(image.getSize()); } void ImageAboutWindow::setImage(const Image& image) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 8f479306..d8014434 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -121,6 +121,10 @@ void Window::setSize(const uint width, const uint height) { DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,); + // FIXME add default and min props for this + if (pData->minWidth == 0 && pData->minHeight == 0) + puglSetDefaultSize(pData->view, width, height); + puglSetWindowSize(pData->view, width, height); } diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index c6a07a31..cfb922e1 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -196,7 +196,11 @@ void UI::onResize(const ResizeEvent& ev) if (uiData->resizeInProgress) return; - uiData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); + const uint width = ev.size.getWidth(); + const uint height = ev.size.getHeight(); + + pData->window.setSize(width, height); + uiData->setSizeCallback(width, height); } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI From 04032b02e3aef7f176bba86b729ef93b3e99f9c0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 14 May 2021 22:48:35 +0100 Subject: [PATCH 064/159] Allow DPF_SCALE_FACTOR env var for quickly testing scale factors Signed-off-by: falkTX --- dgl/TopLevelWidget.hpp | 1 + dgl/Window.hpp | 4 ++-- dgl/src/TopLevelWidget.cpp | 5 +++++ dgl/src/Window.cpp | 8 ++++---- dgl/src/WindowPrivateData.cpp | 18 ++++++++++++++---- distrho/src/DistrhoPluginJack.cpp | 12 +----------- distrho/src/DistrhoUIInternal.hpp | 2 +- 7 files changed, 28 insertions(+), 22 deletions(-) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index deca78bf..fe859766 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -67,6 +67,7 @@ public: Window& getWindow() const noexcept; // TODO group stuff after here, convenience functions present in Window class + double getScaleFactor() const noexcept; void repaint() noexcept; void repaint(const Rectangle& rect) noexcept; void setGeometryConstraints(uint minimumWidth, diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 7bfe1c90..9c5e18eb 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -61,7 +61,7 @@ public: */ explicit Window(Application& app, uintptr_t parentWindowHandle, - double scaling, + double scaleFactor, bool resizable); /** @@ -72,7 +72,7 @@ public: uintptr_t parentWindowHandle, uint width, uint height, - double scaling, + double scaleFactor, bool resizable); /** diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 85612ef5..9d553604 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -40,6 +40,11 @@ Window& TopLevelWidget::getWindow() const noexcept return pData->window; } +double TopLevelWidget::getScaleFactor() const noexcept +{ + return pData->window.getScaleFactor(); +} + void TopLevelWidget::repaint() noexcept { pData->window.repaint(); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index d8014434..1b77915f 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -31,17 +31,17 @@ Window::Window(Application& app) Window::Window(Application& app, const uintptr_t parentWindowHandle, - const double scaling, + const double scaleFactor, const bool resizable) - : pData(new PrivateData(app, this, parentWindowHandle, scaling, resizable)) {} + : pData(new PrivateData(app, this, parentWindowHandle, scaleFactor, resizable)) {} Window::Window(Application& app, const uintptr_t parentWindowHandle, const uint width, const uint height, - const double scaling, + const double scaleFactor, const bool resizable) - : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaling, resizable)) {} + : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable)) {} Window::~Window() { diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index fc92e4ea..8f163667 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -40,6 +40,16 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- +static double getDesktopScaleFactor() +{ + if (const char* const scale = getenv("DPF_SCALE_FACTOR")) + return std::max(1.0, std::atof(scale)); + + return 1.0; +} + +// ----------------------------------------------------------------------- + Window::PrivateData::PrivateData(Application& a, Window* const s) : app(a), appData(a.pData), @@ -49,7 +59,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) isClosed(true), isVisible(false), isEmbed(false), - scaleFactor(1.0), + scaleFactor(getDesktopScaleFactor()), autoScaling(false), autoScaleFactor(1.0), minWidth(0), @@ -68,7 +78,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi isClosed(true), isVisible(false), isEmbed(false), - scaleFactor(1.0), + scaleFactor(getDesktopScaleFactor()), autoScaling(false), autoScaleFactor(1.0), minWidth(0), @@ -91,7 +101,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), - scaleFactor(scale), + scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()), autoScaling(false), autoScaleFactor(1.0), minWidth(0), @@ -125,7 +135,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), - scaleFactor(scale), + scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor()), autoScaling(false), autoScaleFactor(1.0), minWidth(0), diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 59393472..5e4f3d98 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -82,16 +82,6 @@ static void initSignalHandler() // ----------------------------------------------------------------------- -#if DISTRHO_PLUGIN_HAS_UI -// TODO -static double getDesktopScaleFactor() noexcept -{ - return 1.0; -} -#endif - -// ----------------------------------------------------------------------- - #if DISTRHO_PLUGIN_HAS_UI class PluginJack : public IdleCallback #else @@ -111,7 +101,7 @@ public: nullptr, // file request nullptr, // bundle fPlugin.getInstancePointer(), - getDesktopScaleFactor()), + 0.0), #endif fClient(client) { diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 73b923ad..786bca7c 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -137,7 +137,7 @@ public: const fileRequestFunc fileRequestCall, const char* const bundlePath = nullptr, void* const dspPtr = nullptr, - const float scaleFactor = 1.0f, + const double scaleFactor = 1.0, const uint32_t bgColor = 0, const uint32_t fgColor = 0xffffffff) #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI From b71a32c9a86aaa0207dad0f8460833dd2cf4327f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 14:47:53 +0100 Subject: [PATCH 065/159] Experiment with custom UI type Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 21 +++++++++++++++++++++ distrho/DistrhoUI.hpp | 3 +++ 2 files changed, 24 insertions(+) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 41177907..e7d9ed7c 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -562,6 +562,27 @@ START_NAMESPACE_DISTRHO @see Plugin::getTimePosition() */ #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 +/** + Wherever the %UI uses a custom toolkit implementation based on OpenGL.@n + When enabled, the macros @ref DISTRHO_UI_CUSTOM_INCLUDE_PATH and @ref DISTRHO_UI_CUSTOM_WIDGET_TYPE are required. + */ +#define DISTRHO_UI_USE_CUSTOM 1 + +/** + The include path to the header file used by the custom toolkit implementation. + This path must be relative to dpf/distrho/DistrhoUI.hpp + @see DISTRHO_UI_USE_CUSTOM + */ +#define DISTRHO_UI_CUSTOM_INCLUDE_PATH + +/** + The top-level-widget typedef to use for the custom toolkit. + This widget class MUST be a subclass of DGL TopLevelWindow class. + It is recommended that you keep this widget class inside the DGL namespace, + and define widget type as e.g. DGL_NAMESPACE::MyCustomTopLevelWidget. + @see DISTRHO_UI_USE_CUSTOM + */ +#define DISTRHO_UI_CUSTOM_WIDGET_TYPE /** Wherever the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 9db5cd41..2925d7dd 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -24,6 +24,9 @@ # include "../dgl/Base.hpp" # include "extra/ExternalWindow.hpp" typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; +#elif DISTRHO_UI_USE_CUSTOM +# include DISTRHO_UI_CUSTOM_INCLUDE_PATH +typedef DISTRHO_UI_CUSTOM_WIDGET_TYPE UIWidget; #elif DISTRHO_UI_USE_NANOVG # include "../dgl/NanoVG.hpp" typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; From a06479325ea510f4df80ffe3c0981d4bf8bf3feb Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 16:32:45 +0100 Subject: [PATCH 066/159] Fix missing UIWidget::onResize handler Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 1 + distrho/src/DistrhoUI.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index e7d9ed7c..c06a7a16 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -562,6 +562,7 @@ START_NAMESPACE_DISTRHO @see Plugin::getTimePosition() */ #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 + /** Wherever the %UI uses a custom toolkit implementation based on OpenGL.@n When enabled, the macros @ref DISTRHO_UI_CUSTOM_INCLUDE_PATH and @ref DISTRHO_UI_CUSTOM_WIDGET_TYPE are required. diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index cfb922e1..023a9752 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -196,6 +196,8 @@ void UI::onResize(const ResizeEvent& ev) if (uiData->resizeInProgress) return; + UIWidget::onResize(ev); + const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); From 1682712841083a7591352dad59c316b8b6ac528b Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 16:33:27 +0100 Subject: [PATCH 067/159] Add ImguiSimpleGain example, WIP Signed-off-by: falkTX --- examples/ImguiSimpleGain/CParamSmooth.hpp | 41 +++ examples/ImguiSimpleGain/DistrhoPluginInfo.h | 145 ++++++++ examples/ImguiSimpleGain/ImGuiSrc.cpp | 35 ++ examples/ImguiSimpleGain/ImGuiUI.cpp | 316 ++++++++++++++++++ examples/ImguiSimpleGain/ImGuiUI.hpp | 56 ++++ examples/ImguiSimpleGain/Makefile | 51 +++ examples/ImguiSimpleGain/PluginSimpleGain.cpp | 161 +++++++++ examples/ImguiSimpleGain/PluginSimpleGain.hpp | 161 +++++++++ examples/ImguiSimpleGain/UISimpleGain.cpp | 130 +++++++ examples/ImguiSimpleGain/UISimpleGain.hpp | 55 +++ 10 files changed, 1151 insertions(+) create mode 100644 examples/ImguiSimpleGain/CParamSmooth.hpp create mode 100644 examples/ImguiSimpleGain/DistrhoPluginInfo.h create mode 100644 examples/ImguiSimpleGain/ImGuiSrc.cpp create mode 100644 examples/ImguiSimpleGain/ImGuiUI.cpp create mode 100644 examples/ImguiSimpleGain/ImGuiUI.hpp create mode 100644 examples/ImguiSimpleGain/Makefile create mode 100644 examples/ImguiSimpleGain/PluginSimpleGain.cpp create mode 100644 examples/ImguiSimpleGain/PluginSimpleGain.hpp create mode 100644 examples/ImguiSimpleGain/UISimpleGain.cpp create mode 100644 examples/ImguiSimpleGain/UISimpleGain.hpp diff --git a/examples/ImguiSimpleGain/CParamSmooth.hpp b/examples/ImguiSimpleGain/CParamSmooth.hpp new file mode 100644 index 00000000..b2b96ce0 --- /dev/null +++ b/examples/ImguiSimpleGain/CParamSmooth.hpp @@ -0,0 +1,41 @@ +/** + * One-pole LPF for smooth parameter changes + * + * https://www.musicdsp.org/en/latest/Filters/257-1-pole-lpf-for-smooth-parameter-changes.html + */ + +#ifndef C_PARAM_SMOOTH_H +#define C_PARAM_SMOOTH_H + +#include + +#define TWO_PI 6.283185307179586476925286766559f + +class CParamSmooth { +public: + CParamSmooth(float smoothingTimeMs, float samplingRate) + : t(smoothingTimeMs) + { + setSampleRate(samplingRate); + } + + ~CParamSmooth() { } + + void setSampleRate(float samplingRate) { + if (samplingRate != fs) { + fs = samplingRate; + a = exp(-TWO_PI / (t * 0.001f * samplingRate)); + b = 1.0f - a; + z = 0.0f; + } + } + + inline float process(float in) { + return z = (in * b) + (z * a); + } +private: + float a, b, t, z; + double fs = 0.0; +}; + +#endif // #ifndef C_PARAM_SMOOTH_H diff --git a/examples/ImguiSimpleGain/DistrhoPluginInfo.h b/examples/ImguiSimpleGain/DistrhoPluginInfo.h new file mode 100644 index 00000000..0deddd7b --- /dev/null +++ b/examples/ImguiSimpleGain/DistrhoPluginInfo.h @@ -0,0 +1,145 @@ +/* + * Simple Gain audio effect for DISTRHO Plugin Framework (DPF) + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2021 Jean Pierre Cimalando + * Copyright (C) 2021 Filipe Coelho + * + * 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. + */ + +/** + The plugin name.@n + This is used to identify your plugin before a Plugin instance can be created. + @note This macro is required. + */ +#define DISTRHO_PLUGIN_NAME "ImguiSimpleGain" + +/** + Number of audio inputs the plugin has. + @note This macro is required. + */ +#define DISTRHO_PLUGIN_NUM_INPUTS 2 + +/** + Number of audio outputs the plugin has. + @note This macro is required. + */ +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 + +/** + The plugin URI when exporting in LV2 format. + @note This macro is required. + */ +#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/imguisimplegain" + +/** + Wherever the plugin has a custom %UI. + @see DISTRHO_UI_USE_NANOVG + @see UI + */ +#define DISTRHO_PLUGIN_HAS_UI 1 + +/** + Wherever the plugin processing is realtime-safe.@n + TODO - list rtsafe requirements + */ +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 + +/** + Wherever the plugin is a synth.@n + @ref DISTRHO_PLUGIN_WANT_MIDI_INPUT is automatically enabled when this is too. + @see DISTRHO_PLUGIN_WANT_MIDI_INPUT + */ +#define DISTRHO_PLUGIN_IS_SYNTH 0 + +/** + Enable direct access between the %UI and plugin code. + @see UI::getPluginInstancePointer() + @note DO NOT USE THIS UNLESS STRICTLY NECESSARY!! + Try to avoid it at all costs! + */ +#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 + +/** + Wherever the plugin introduces latency during audio or midi processing. + @see Plugin::setLatency(uint32_t) + */ +#define DISTRHO_PLUGIN_WANT_LATENCY 0 + +/** + Wherever the plugin wants MIDI input.@n + This is automatically enabled if @ref DISTRHO_PLUGIN_IS_SYNTH is true. + */ +#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 + +/** + Wherever the plugin wants MIDI output. + @see Plugin::writeMidiEvent(const MidiEvent&) + */ +#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 + +/** + Wherever the plugin provides its own internal programs. + @see Plugin::initProgramName(uint32_t, String&) + @see Plugin::loadProgram(uint32_t) + */ +#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 + +/** + Wherever the plugin uses internal non-parameter data. + @see Plugin::initState(uint32_t, String&, String&) + @see Plugin::setState(const char*, const char*) + */ +#define DISTRHO_PLUGIN_WANT_STATE 0 + +/** + Wherever the plugin wants time position information from the host. + @see Plugin::getTimePosition() + */ +#define DISTRHO_PLUGIN_WANT_TIMEPOS 0 + +/** + Wherever the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n + When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget. + */ +#define DISTRHO_UI_USE_NANOVG 0 + +/** + The %UI URI when exporting in LV2 format.@n + By default this is set to @ref DISTRHO_PLUGIN_URI with "#UI" as suffix. + */ +#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" + +/** + Wherever the %UI uses a custom toolkit implementation based on OpenGL.@n + When enabled, the macros @ref DISTRHO_UI_CUSTOM_INCLUDE_PATH and @ref DISTRHO_UI_CUSTOM_WIDGET_TYPE are required. + */ +#define DISTRHO_UI_USE_CUSTOM 1 + +/** + The include path to the header file used by the custom toolkit implementation. + This path must be relative to dpf/distrho/DistrhoUI.hpp + @see DISTRHO_UI_USE_CUSTOM + */ +#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "ImGuiUI.hpp" + +/** + The top-level-widget typedef to use for the custom toolkit. + This widget class MUST be a subclass of DGL TopLevelWindow class. + It is recommended that you keep this widget class inside the DGL namespace, + and define widget type as e.g. DGL_NAMESPACE::MyCustomTopLevelWidget. + @see DISTRHO_UI_USE_CUSTOM + */ +#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiUI + +#define DISTRHO_UI_USER_RESIZABLE 1 diff --git a/examples/ImguiSimpleGain/ImGuiSrc.cpp b/examples/ImguiSimpleGain/ImGuiSrc.cpp new file mode 100644 index 00000000..ad4c106c --- /dev/null +++ b/examples/ImguiSimpleGain/ImGuiSrc.cpp @@ -0,0 +1,35 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2021 Jean Pierre Cimalando + * + * 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 +#if !defined(IMGUI_GL2) && !defined(IMGUI_GL3) +# define IMGUI_GL2 1 +#endif +#if defined(IMGUI_GL2) +# include +#elif defined(IMGUI_GL3) +# include +#endif + +#include +#include +#include +#include +#if defined(IMGUI_GL2) +#include +#elif defined(IMGUI_GL3) +#include +#endif diff --git a/examples/ImguiSimpleGain/ImGuiUI.cpp b/examples/ImguiSimpleGain/ImGuiUI.cpp new file mode 100644 index 00000000..629ba259 --- /dev/null +++ b/examples/ImguiSimpleGain/ImGuiUI.cpp @@ -0,0 +1,316 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2021 Jean Pierre Cimalando + * + * 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 "Application.hpp" + +#include + +#include "ImGuiUI.hpp" +#include +#if !defined(IMGUI_GL2) && !defined(IMGUI_GL3) +# define IMGUI_GL2 1 +#endif +#if defined(IMGUI_GL2) +# include +#elif defined(IMGUI_GL3) +# include +#endif +#include +#include + +START_NAMESPACE_DGL + +struct ImGuiUI::Impl +{ + explicit Impl(ImGuiUI* self); + ~Impl(); + + void setupGL(); + void cleanupGL(); + + // perhaps DPF will implement this in the future + float getScaleFactor() const { return 1.0f; } + + static int mouseButtonToImGui(int button); + + ImGuiUI* fSelf = nullptr; + ImGuiContext* fContext = nullptr; + Color fBackgroundColor{0.25f, 0.25f, 0.25f}; + int fRepaintIntervalMs = 15; + + using Clock = std::chrono::steady_clock; + Clock::time_point fLastRepainted; + bool fWasEverPainted = false; +}; + +ImGuiUI::ImGuiUI(Window& windowToMapTo) + : TopLevelWidget(windowToMapTo), + fImpl(new ImGuiUI::Impl(this)) +{ + getApp().addIdleCallback(this); +} + +ImGuiUI::~ImGuiUI() +{ + delete fImpl; +} + +void ImGuiUI::setBackgroundColor(Color color) +{ + fImpl->fBackgroundColor = color; +} + +void ImGuiUI::setRepaintInterval(int intervalMs) +{ + fImpl->fRepaintIntervalMs = intervalMs; +} + +void ImGuiUI::onDisplay() +{ + ImGui::SetCurrentContext(fImpl->fContext); + +#if defined(IMGUI_GL2) + ImGui_ImplOpenGL2_NewFrame(); +#elif defined(IMGUI_GL3) + ImGui_ImplOpenGL3_NewFrame(); +#endif + + ImGui::NewFrame(); + onImGuiDisplay(); + ImGui::Render(); + + ImGuiIO &io = ImGui::GetIO(); + + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + + Color backgroundColor = fImpl->fBackgroundColor; + glClearColor( + backgroundColor.red, backgroundColor.green, + backgroundColor.blue, backgroundColor.alpha); + glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + +#if defined(IMGUI_GL2) + ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); +#elif defined(IMGUI_GL3) + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +#endif + + fImpl->fLastRepainted = Impl::Clock::now(); + fImpl->fWasEverPainted = true; +} + +bool ImGuiUI::onKeyboard(const KeyboardEvent& event) +{ + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + if (event.press) + io.AddInputCharacter(event.key); + + int imGuiKey = event.key; + if (imGuiKey >= 0 && imGuiKey < 128) + { + if (imGuiKey >= 'a' && imGuiKey <= 'z') + imGuiKey = imGuiKey - 'a' + 'A'; + io.KeysDown[imGuiKey] = event.press; + } + + return io.WantCaptureKeyboard; +} + +bool ImGuiUI::onSpecial(const SpecialEvent& event) +{ + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + int imGuiKey = IM_ARRAYSIZE(io.KeysDown) - event.key; + io.KeysDown[imGuiKey] = event.press; + + switch (event.key) + { + case kKeyShift: + io.KeyShift = event.press; + break; + case kKeyControl: + io.KeyCtrl = event.press; + break; + case kKeyAlt: + io.KeyAlt = event.press; + break; + case kKeySuper: + io.KeySuper = event.press; + break; + default: + break; + } + + return io.WantCaptureKeyboard; +} + +bool ImGuiUI::onMouse(const MouseEvent& event) +{ + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + int imGuiButton = Impl::mouseButtonToImGui(event.button); + if (imGuiButton != -1) + io.MouseDown[imGuiButton] = event.press; + + return io.WantCaptureMouse; +} + +bool ImGuiUI::onMotion(const MotionEvent& event) +{ + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + const float scaleFactor = fImpl->getScaleFactor(); + io.MousePos.x = std::round(scaleFactor * event.pos.getX()); + io.MousePos.y = std::round(scaleFactor * event.pos.getY()); + + return false; +} + +bool ImGuiUI::onScroll(const ScrollEvent& event) +{ + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + io.MouseWheel += event.delta.getY(); + io.MouseWheelH += event.delta.getX(); + + return io.WantCaptureMouse; +} + +void ImGuiUI::onResize(const ResizeEvent& event) +{ + TopLevelWidget::onResize(event); + + const uint width = event.size.getWidth(); + const uint height = event.size.getHeight(); + + ImGui::SetCurrentContext(fImpl->fContext); + ImGuiIO &io = ImGui::GetIO(); + + const float scaleFactor = fImpl->getScaleFactor(); + io.DisplaySize.x = std::round(scaleFactor * width); + io.DisplaySize.y = std::round(scaleFactor * height); +} + +void ImGuiUI::idleCallback() +{ + bool shouldRepaint; + + if (fImpl->fWasEverPainted) + { + Impl::Clock::duration elapsed = + Impl::Clock::now() - fImpl->fLastRepainted; + std::chrono::milliseconds elapsedMs = + std::chrono::duration_cast(elapsed); + shouldRepaint = elapsedMs.count() > fImpl->fRepaintIntervalMs; + } + else + { + shouldRepaint = true; + } + + if (shouldRepaint) + repaint(); +} + +ImGuiUI::Impl::Impl(ImGuiUI* self) + : fSelf(self) +{ + setupGL(); +} + +ImGuiUI::Impl::~Impl() +{ + cleanupGL(); +} + +void ImGuiUI::Impl::setupGL() +{ + DISTRHO_SAFE_ASSERT_RETURN(glewInit() == 0,); + + IMGUI_CHECKVERSION(); + fContext = ImGui::CreateContext(); + ImGui::SetCurrentContext(fContext); + + ImGuiIO &io = ImGui::GetIO(); + const float scaleFactor = getScaleFactor(); + io.DisplaySize.x = std::round(scaleFactor * fSelf->getWidth()); + io.DisplaySize.y = std::round(scaleFactor * fSelf->getHeight()); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.IniFilename = nullptr; + + io.KeyMap[ImGuiKey_Tab] = '\t'; + io.KeyMap[ImGuiKey_LeftArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyLeft; + io.KeyMap[ImGuiKey_RightArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyRight; + io.KeyMap[ImGuiKey_UpArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyUp; + io.KeyMap[ImGuiKey_DownArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyDown; + io.KeyMap[ImGuiKey_PageUp] = IM_ARRAYSIZE(io.KeysDown) - kKeyPageUp; + io.KeyMap[ImGuiKey_PageDown] = IM_ARRAYSIZE(io.KeysDown) - kKeyPageDown; + io.KeyMap[ImGuiKey_Home] = IM_ARRAYSIZE(io.KeysDown) - kKeyHome; + io.KeyMap[ImGuiKey_End] = IM_ARRAYSIZE(io.KeysDown) - kKeyEnd; + io.KeyMap[ImGuiKey_Insert] = IM_ARRAYSIZE(io.KeysDown) - kKeyInsert; + io.KeyMap[ImGuiKey_Delete] = 127; + io.KeyMap[ImGuiKey_Backspace] = '\b'; + io.KeyMap[ImGuiKey_Space] = ' '; + io.KeyMap[ImGuiKey_Enter] = '\r'; + io.KeyMap[ImGuiKey_Escape] = 27; + io.KeyMap[ImGuiKey_A] = 'A'; + io.KeyMap[ImGuiKey_C] = 'C'; + io.KeyMap[ImGuiKey_V] = 'V'; + io.KeyMap[ImGuiKey_X] = 'X'; + io.KeyMap[ImGuiKey_Y] = 'Y'; + io.KeyMap[ImGuiKey_Z] = 'Z'; + +#if defined(IMGUI_GL2) + ImGui_ImplOpenGL2_Init(); +#elif defined(IMGUI_GL3) + ImGui_ImplOpenGL3_Init(); +#endif +} + +void ImGuiUI::Impl::cleanupGL() +{ + ImGui::SetCurrentContext(fContext); +#if defined(IMGUI_GL2) + ImGui_ImplOpenGL2_Shutdown(); +#elif defined(IMGUI_GL3) + ImGui_ImplOpenGL3_Shutdown(); +#endif + ImGui::DestroyContext(fContext); +} + +int ImGuiUI::Impl::mouseButtonToImGui(int button) +{ + switch (button) + { + default: + return -1; + case 1: + return 0; + case 2: + return 2; + case 3: + return 1; + } +} + +END_NAMESPACE_DGL diff --git a/examples/ImguiSimpleGain/ImGuiUI.hpp b/examples/ImguiSimpleGain/ImGuiUI.hpp new file mode 100644 index 00000000..589d3bef --- /dev/null +++ b/examples/ImguiSimpleGain/ImGuiUI.hpp @@ -0,0 +1,56 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2021 Jean Pierre Cimalando + * + * 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. + */ + +#pragma once + +#include "TopLevelWidget.hpp" +#include "Color.hpp" + +#ifndef DGL_OPENGL +# error ImGUI is only available in OpenGL mode +#endif + +START_NAMESPACE_DGL + +/** + ImGui user interface class. +*/ +class ImGuiUI : public TopLevelWidget, + public IdleCallback { +public: + ImGuiUI(Window& windowToMapTo); + ~ImGuiUI() override; + void setBackgroundColor(Color color); + void setRepaintInterval(int intervalMs); + +protected: + virtual void onImGuiDisplay() = 0; + + virtual void onDisplay() override; + virtual bool onKeyboard(const KeyboardEvent& event) override; + virtual bool onSpecial(const SpecialEvent& event) override; + virtual bool onMouse(const MouseEvent& event) override; + virtual bool onMotion(const MotionEvent& event) override; + virtual bool onScroll(const ScrollEvent& event) override; + virtual void onResize(const ResizeEvent& event) override; + virtual void idleCallback() override; + +private: + struct Impl; + Impl* fImpl; +}; + +END_NAMESPACE_DGL diff --git a/examples/ImguiSimpleGain/Makefile b/examples/ImguiSimpleGain/Makefile new file mode 100644 index 00000000..0e07c251 --- /dev/null +++ b/examples/ImguiSimpleGain/Makefile @@ -0,0 +1,51 @@ +#!/usr/bin/make -f +# Makefile for DISTRHO Plugins # +# ---------------------------- # +# Created by falkTX, Christopher Arndt, and Patrick Desaulniers +# + +# -------------------------------------------------------------- +# Project name, used for binaries + +NAME = d_ImguiSimpleGain + +# -------------------------------------------------------------- +# Files to build + +FILES_DSP = \ + PluginSimpleGain.cpp + +FILES_UI = \ + UISimpleGain.cpp \ + ImGuiUI.cpp \ + ImGuiSrc.cpp + +# -------------------------------------------------------------- +# Do some magic + +include ../../Makefile.plugins.mk + +BUILD_CXX_FLAGS += -I../../../imgui -I../../../imgui/backends +BUILD_CXX_FLAGS += $(shell $(PKG_CONFIG) glew --cflags) +LINK_FLAGS += $(shell $(PKG_CONFIG) glew --libs) + +# -------------------------------------------------------------- +# Enable all selected plugin types + +ifeq ($(HAVE_JACK),true) +ifeq ($(HAVE_OPENGL),true) +TARGETS += jack +endif +endif + +ifeq ($(HAVE_OPENGL),true) +TARGETS += lv2_sep +else +TARGETS += lv2_dsp +endif + +TARGETS += vst + +all: $(TARGETS) + +# -------------------------------------------------------------- diff --git a/examples/ImguiSimpleGain/PluginSimpleGain.cpp b/examples/ImguiSimpleGain/PluginSimpleGain.cpp new file mode 100644 index 00000000..6c38238b --- /dev/null +++ b/examples/ImguiSimpleGain/PluginSimpleGain.cpp @@ -0,0 +1,161 @@ +/* + * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) + * + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2021 Jean Pierre Cimalando + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "PluginSimpleGain.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +PluginSimpleGain::PluginSimpleGain() + : Plugin(paramCount, presetCount, 0) // paramCount param(s), presetCount program(s), 0 states +{ + smooth_gain = new CParamSmooth(20.0f, getSampleRate()); + + for (unsigned p = 0; p < paramCount; ++p) { + Parameter param; + initParameter(p, param); + setParameterValue(p, param.ranges.def); + } +} + +PluginSimpleGain::~PluginSimpleGain() { + delete smooth_gain; +} + +// ----------------------------------------------------------------------- +// Init + +void PluginSimpleGain::initParameter(uint32_t index, Parameter& parameter) { + if (index >= paramCount) + return; + + parameter.ranges.min = -90.0f; + parameter.ranges.max = 30.0f; + parameter.ranges.def = -0.0f; + parameter.unit = "db"; + parameter.hints = kParameterIsAutomable; + + switch (index) { + case paramGain: + parameter.name = "Gain (dB)"; + parameter.shortName = "Gain"; + parameter.symbol = "gain"; + break; + } +} + +/** + Set the name of the program @a index. + This function will be called once, shortly after the plugin is created. +*/ +void PluginSimpleGain::initProgramName(uint32_t index, String& programName) { + if (index < presetCount) { + programName = factoryPresets[index].name; + } +} + +// ----------------------------------------------------------------------- +// Internal data + +/** + Optional callback to inform the plugin about a sample rate change. +*/ +void PluginSimpleGain::sampleRateChanged(double newSampleRate) { + fSampleRate = newSampleRate; + smooth_gain->setSampleRate(newSampleRate); +} + +/** + Get the current value of a parameter. +*/ +float PluginSimpleGain::getParameterValue(uint32_t index) const { + return fParams[index]; +} + +/** + Change a parameter value. +*/ +void PluginSimpleGain::setParameterValue(uint32_t index, float value) { + fParams[index] = value; + + switch (index) { + case paramGain: + gain = DB_CO(CLAMP(fParams[paramGain], -90.0, 30.0)); + break; + } +} + +/** + Load a program. + The host may call this function from any context, + including realtime processing. +*/ +void PluginSimpleGain::loadProgram(uint32_t index) { + if (index < presetCount) { + for (int i=0; i < paramCount; i++) { + setParameterValue(i, factoryPresets[index].params[i]); + } + } +} + +// ----------------------------------------------------------------------- +// Process + +void PluginSimpleGain::activate() { + // plugin is activated +} + + + +void PluginSimpleGain::run(const float** inputs, float** outputs, + uint32_t frames) { + + // get the left and right audio inputs + const float* const inpL = inputs[0]; + const float* const inpR = inputs[1]; + + // get the left and right audio outputs + float* const outL = outputs[0]; + float* const outR = outputs[1]; + + // apply gain against all samples + for (uint32_t i=0; i < frames; ++i) { + float gainval = smooth_gain->process(gain); + outL[i] = inpL[i] * gainval; + outR[i] = inpR[i] * gainval; + } +} + +// ----------------------------------------------------------------------- + +Plugin* createPlugin() { + return new PluginSimpleGain(); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/examples/ImguiSimpleGain/PluginSimpleGain.hpp b/examples/ImguiSimpleGain/PluginSimpleGain.hpp new file mode 100644 index 00000000..69da27d0 --- /dev/null +++ b/examples/ImguiSimpleGain/PluginSimpleGain.hpp @@ -0,0 +1,161 @@ +/* + * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) + * + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2021 Jean Pierre Cimalando + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef PLUGIN_SIMPLEGAIN_H +#define PLUGIN_SIMPLEGAIN_H + +#include "DistrhoPlugin.hpp" +#include "CParamSmooth.hpp" + +START_NAMESPACE_DISTRHO + +#ifndef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#endif + +#ifndef MAX +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#endif + +#ifndef CLAMP +#define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) +#endif + +#ifndef DB_CO +#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) +#endif + +// ----------------------------------------------------------------------- + +class PluginSimpleGain : public Plugin { +public: + enum Parameters { + paramGain = 0, + paramCount + }; + + PluginSimpleGain(); + + ~PluginSimpleGain(); + +protected: + // ------------------------------------------------------------------- + // Information + + const char* getLabel() const noexcept override { + return "SimpleGain"; + } + + const char* getDescription() const override { + return "A simple audio volume gain plugin"; + } + + const char* getMaker() const noexcept override { + return "example.com"; + } + + const char* getHomePage() const override { + return "https://example.com/plugins/simplegain"; + } + + const char* getLicense() const noexcept override { + return "https://spdx.org/licenses/MIT"; + } + + uint32_t getVersion() const noexcept override { + return d_version(0, 1, 0); + } + + // Go to: + // + // http://service.steinberg.de/databases/plugin.nsf/plugIn + // + // Get a proper plugin UID and fill it in here! + int64_t getUniqueId() const noexcept override { + return d_cconst('a', 'b', 'c', 'd'); + } + + // ------------------------------------------------------------------- + // Init + + void initParameter(uint32_t index, Parameter& parameter) override; + void initProgramName(uint32_t index, String& programName) override; + + // ------------------------------------------------------------------- + // Internal data + + float getParameterValue(uint32_t index) const override; + void setParameterValue(uint32_t index, float value) override; + void loadProgram(uint32_t index) override; + + // ------------------------------------------------------------------- + // Optional + + // Optional callback to inform the plugin about a sample rate change. + void sampleRateChanged(double newSampleRate) override; + + // ------------------------------------------------------------------- + // Process + + void activate() override; + + void run(const float**, float** outputs, uint32_t frames) override; + + + // ------------------------------------------------------------------- + +private: + float fParams[paramCount]; + double fSampleRate; + float gain; + CParamSmooth *smooth_gain; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginSimpleGain) +}; + +struct Preset { + const char* name; + float params[PluginSimpleGain::paramCount]; +}; + +const Preset factoryPresets[] = { + { + "Unity Gain", + {0.0f} + } + //,{ + // "Another preset", // preset name + // {-14.0f, ...} // array of presetCount float param values + //} +}; + +const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // #ifndef PLUGIN_SIMPLEGAIN_H diff --git a/examples/ImguiSimpleGain/UISimpleGain.cpp b/examples/ImguiSimpleGain/UISimpleGain.cpp new file mode 100644 index 00000000..70af1d5c --- /dev/null +++ b/examples/ImguiSimpleGain/UISimpleGain.cpp @@ -0,0 +1,130 @@ +/* + * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) + * + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2021 Jean Pierre Cimalando + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "UISimpleGain.hpp" +#include +// #include "Window.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Init / Deinit + +UISimpleGain::UISimpleGain() +: UI(600, 400) +{ + setGeometryConstraints(600, 400, true); +} + +UISimpleGain::~UISimpleGain() { + +} + +// ----------------------------------------------------------------------- +// DSP/Plugin callbacks + +/** + A parameter has changed on the plugin side. + This is called by the host to inform the UI about parameter changes. +*/ +void UISimpleGain::parameterChanged(uint32_t index, float value) { + params[index] = value; + + switch (index) { + case PluginSimpleGain::paramGain: + // do something when Gain param is set, such as update a widget + break; + } + + (void)value; +} + +/** + A program has been loaded on the plugin side. + This is called by the host to inform the UI about program changes. +*/ +void UISimpleGain::programLoaded(uint32_t index) { + if (index < presetCount) { + for (int i=0; i < PluginSimpleGain::paramCount; i++) { + // set values for each parameter and update their widgets + parameterChanged(i, factoryPresets[index].params[i]); + } + } +} + +/** + Optional callback to inform the UI about a sample rate change on the plugin side. +*/ +void UISimpleGain::sampleRateChanged(double newSampleRate) { + (void)newSampleRate; +} + +// ----------------------------------------------------------------------- +// Widget callbacks + + +/** + A function called to draw the view contents. +*/ +void UISimpleGain::onImGuiDisplay() { + float width = getWidth(); + float height = getHeight(); + float margin = 20.0f; + + ImGui::SetNextWindowPos(ImVec2(margin, margin)); + ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); + + if (ImGui::Begin("Simple gain")) { + static char aboutText[256] = + "This is a demo plugin made with ImGui.\n"; + ImGui::InputTextMultiline("About", aboutText, sizeof(aboutText)); + + float& gain = params[PluginSimpleGain::paramGain]; + if (ImGui::SliderFloat("Gain (dB)", &gain, -90.0f, 30.0f)) + { + if (ImGui::IsItemActivated()) + { + editParameter(PluginSimpleGain::paramGain, true); + } + setParameterValue(PluginSimpleGain::paramGain, gain); + } + if (ImGui::IsItemDeactivated()) + { + editParameter(PluginSimpleGain::paramGain, false); + } + } + ImGui::End(); +} + +// ----------------------------------------------------------------------- + +UI* createUI() { + return new UISimpleGain(); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/examples/ImguiSimpleGain/UISimpleGain.hpp b/examples/ImguiSimpleGain/UISimpleGain.hpp new file mode 100644 index 00000000..cf28dd8e --- /dev/null +++ b/examples/ImguiSimpleGain/UISimpleGain.hpp @@ -0,0 +1,55 @@ +/* + * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) + * + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2021 Jean Pierre Cimalando + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UI_SIMPLEGAIN_H +#define UI_SIMPLEGAIN_H + +#include "DistrhoUI.hpp" +#include "PluginSimpleGain.hpp" + +START_NAMESPACE_DISTRHO + +class UISimpleGain : public UI { +public: + UISimpleGain(); + ~UISimpleGain(); + +protected: + void parameterChanged(uint32_t, float value) override; + void programLoaded(uint32_t index) override; + void sampleRateChanged(double newSampleRate) override; + + void onImGuiDisplay() override; + +private: + float params[PluginSimpleGain::paramCount] {}; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UISimpleGain) +}; + +END_NAMESPACE_DISTRHO + +#endif From 3098954a0024021db40f6deb3ebf8876e84fae79 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 19:54:50 +0100 Subject: [PATCH 068/159] Cleanup Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 307 ++++++++++------------------------ dgl/src/WindowPrivateData.hpp | 12 +- 2 files changed, 89 insertions(+), 230 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 8f163667..a6848881 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -63,8 +63,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0), - pendingVisibility(kPendingVisibilityNone) + minHeight(0) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } @@ -82,8 +81,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0), - pendingVisibility(kPendingVisibilityNone) + minHeight(0) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -105,8 +103,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0), - pendingVisibility(kPendingVisibilityNone) + minHeight(0) { if (isEmbed) { @@ -139,8 +136,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0), - pendingVisibility(kPendingVisibilityNone) + minHeight(0) { if (isEmbed) { @@ -247,11 +243,6 @@ void Window::PrivateData::show() #else puglShow(view); #endif - /* - pendingVisibility = kPendingVisibilityShow; - const PuglStatus status = puglRealize(view); - DISTRHO_SAFE_ASSERT_INT_RETURN(status == PUGL_SUCCESS, status, close()); - */ } else { @@ -272,9 +263,6 @@ void Window::PrivateData::hide() DGL_DBG("Window hide cannot be called when embedded\n"); return; } - - pendingVisibility = kPendingVisibilityHide; - if (! isVisible) { DGL_DBG("Window hide matches current visible state, ignoring request\n"); @@ -347,19 +335,7 @@ void Window::PrivateData::idleCallback() // ----------------------------------------------------------------------- -void Window::PrivateData::onPuglDisplay() -{ - DGL_DBGp("PUGL: onPuglDisplay : %p\n", topLevelWidget); - - puglOnDisplayPrepare(view); - -#ifndef DPF_TEST_WINDOW_CPP - if (topLevelWidget != nullptr) - topLevelWidget->pData->display(); -#endif -} - -void Window::PrivateData::onPuglReshape(const int width, const int height) +void Window::PrivateData::onPuglConfigure(const int width, const int height) { DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); @@ -380,18 +356,15 @@ void Window::PrivateData::onPuglReshape(const int width, const int height) #endif } -void Window::PrivateData::onPuglCreate() +void Window::PrivateData::onPuglExpose() { - DGL_DBGp("PUGL: onPuglCreate %i\n", pendingVisibility); + DGL_DBGp("PUGL: onPuglExpose : %p\n", topLevelWidget); - if (pendingVisibility != kPendingVisibilityShow) - return; + puglOnDisplayPrepare(view); - pendingVisibility = kPendingVisibilityNone; -#ifdef DISTRHO_OS_WINDOWS - puglWin32ShowWindowCentered(view); -#else - puglShow(view); +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->display(); #endif } @@ -437,14 +410,38 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_NOTHING: break; + ///< View created, a #PuglEventCreate + case PUGL_CREATE: +#ifdef DGL_PUGL_USING_X11 + if (! pData->isEmbed) + puglExtraSetWindowTypeAndPID(view); +#endif + break; + + ///< View destroyed, a #PuglEventDestroy + case PUGL_DESTROY: + break; + ///< View moved/resized, a #PuglEventConfigure case PUGL_CONFIGURE: - pData->onPuglReshape(event->configure.width, event->configure.height); + pData->onPuglConfigure(event->configure.width, event->configure.height); + break; + + ///< View made visible, a #PuglEventMap + case PUGL_MAP: + break; + + ///< View made invisible, a #PuglEventUnmap + case PUGL_UNMAP: + break; + + ///< View ready to draw, a #PuglEventUpdate + case PUGL_UPDATE: break; ///< View must be drawn, a #PuglEventExpose case PUGL_EXPOSE: - pData->onPuglDisplay(); + pData->onPuglExpose(); break; ///< View will be closed, a #PuglEventClose @@ -452,12 +449,36 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu pData->onPuglClose(); break; - case PUGL_CREATE: - pData->onPuglCreate(); + ///< Keyboard focus entered view, a #PuglEventFocus + case PUGL_FOCUS_IN: break; - case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton - case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton + ///< Keyboard focus left view, a #PuglEventFocus + case PUGL_FOCUS_OUT: + break; + + ///< Key pressed, a #PuglEventKey + case PUGL_KEY_PRESS: + break; + ///< Key released, a #PuglEventKey + case PUGL_KEY_RELEASE: + break; + + ///< Character entered, a #PuglEventText + case PUGL_TEXT: + break; + + ///< Pointer entered view, a #PuglEventCrossing + case PUGL_POINTER_IN: + break; + ///< Pointer left view, a #PuglEventCrossing + case PUGL_POINTER_OUT: + break; + + ///< Mouse button pressed, a #PuglEventButton + case PUGL_BUTTON_PRESS: + ///< Mouse button released, a #PuglEventButton + case PUGL_BUTTON_RELEASE: { Events::MouseEvent ev; ev.mod = event->button.state; @@ -470,8 +491,28 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu break; } - // TODO - default: + ///< Pointer moved, a #PuglEventMotion + case PUGL_MOTION: + break; + + ///< Scrolled, a #PuglEventScroll + case PUGL_SCROLL: + break; + + ///< Custom client message, a #PuglEventClient + case PUGL_CLIENT: + break; + + ///< Timer triggered, a #PuglEventTimer + case PUGL_TIMER: + break; + + ///< Recursive loop entered, a #PuglEventLoopEnter + case PUGL_LOOP_ENTER: + break; + + ///< Recursive loop left, a #PuglEventLoopLeave + case PUGL_LOOP_LEAVE: break; } @@ -608,177 +649,3 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver // ----------------------------------------------------------------------- END_NAMESPACE_DGL - -#if 0 -#ifdef DGL_CAIRO -# define PUGL_CAIRO -# include "../Cairo.hpp" -#endif -#ifdef DGL_OPENGL -# define PUGL_OPENGL -# include "../OpenGL.hpp" -#endif - -#ifndef DPF_TEST_WINDOW_CPP -#include "WidgetPrivateData.hpp" -#include "pugl-upstream/include/pugl/pugl.h" -#include "pugl-extra/extras.h" -#endif - -extern "C" { -#include "pugl-upstream/src/implementation.c" -#include "pugl-extra/extras.c" -} - -#if defined(DISTRHO_OS_HAIKU) -# define DGL_DEBUG_EVENTS -# include "pugl-upstream/src/haiku.cpp" -#elif defined(DISTRHO_OS_MAC) -# include "pugl-upstream/src/mac.m" -#elif defined(DISTRHO_OS_WINDOWS) -# include "ppugl-upstream/src/win.c" -# undef max -# undef min -#else -# define DGL_PUGL_USING_X11 -extern "C" { -# include "pugl-upstream/src/x11.c" -// # ifdef DGL_CAIRO -// # include "pugl-upstream/src/x11_cairo.c" -// # endif -# ifdef DGL_OPENGL -# include "pugl-upstream/src/x11_gl.c" -# endif -# define PUGL_DETAIL_X11_H_INCLUDED -# include "pugl-extra/x11.c" -} -#endif - -#include -#include -#include -#include -#include -#include - -START_NAMESPACE_DGL - -// ----------------------------------------------------------------------- - -void Window::PrivateData::addWidget(Widget* const widget) -{ - fWidgets.push_back(widget); -} - -void Window::PrivateData::removeWidget(Widget* const widget) -{ - fWidgets.remove(widget); -} - -// ----------------------------------------------------------------------- - -void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) -{ - DGL_DBGp("PUGL: onMouse : %i %i %i %i\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); - -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); - - Widget::MouseEvent rev = ev; - double x = ev.pos.getX() / fAutoScaling; - double y = ev.pos.getY() / fAutoScaling; - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - rev.pos = Point(x - widget->getAbsoluteX(), - y - widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onMouse(rev)) - break; - } -} - -PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) -{ - Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); - - switch (event->type) - { - ///< No event - case PUGL_NOTHING: - break; - - ///< View created, a #PuglEventCreate - case PUGL_CREATE: -#ifdef DGL_PUGL_USING_X11 - if (! pData->fUsingEmbed) - puglExtraSetWindowTypeAndPID(view); -#endif - break; - - ///< View destroyed, a #PuglEventDestroy - case PUGL_DESTROY: - break; - - ///< View moved/resized, a #PuglEventConfigure - case PUGL_CONFIGURE: - pData->onPuglReshape(event->configure.width, event->configure.height); - break; - - case PUGL_MAP: ///< View made visible, a #PuglEventMap - case PUGL_UNMAP: ///< View made invisible, a #PuglEventUnmap - break; - - ///< View ready to draw, a #PuglEventUpdate - case PUGL_UPDATE: - break; - - ///< View must be drawn, a #PuglEventExpose - case PUGL_EXPOSE: - pData->onPuglDisplay(); - break; - - ///< View will be closed, a #PuglEventClose - case PUGL_CLOSE: - pData->onPuglClose(); - break; - - case PUGL_BUTTON_PRESS: ///< Mouse button pressed, a #PuglEventButton - case PUGL_BUTTON_RELEASE: ///< Mouse button released, a #PuglEventButton - { - Widget::MouseEvent ev; - ev.mod = event->button.state; - ev.flags = event->button.flags; - ev.time = static_cast(event->button.time * 1000.0 + 0.5); - ev.button = event->button.button; - ev.press = event->type == PUGL_BUTTON_PRESS; - ev.pos = Point(event->button.x, event->button.y); - pData->onPuglMouse(ev); - break; - } - - case PUGL_FOCUS_IN: ///< Keyboard focus entered view, a #PuglEventFocus - case PUGL_FOCUS_OUT: ///< Keyboard focus left view, a #PuglEventFocus - case PUGL_KEY_PRESS: ///< Key pressed, a #PuglEventKey - case PUGL_KEY_RELEASE: ///< Key released, a #PuglEventKey - case PUGL_TEXT: ///< Character entered, a #PuglEventText - case PUGL_POINTER_IN: ///< Pointer entered view, a #PuglEventCrossing - case PUGL_POINTER_OUT: ///< Pointer left view, a #PuglEventCrossing - case PUGL_MOTION: ///< Pointer moved, a #PuglEventMotion - case PUGL_SCROLL: ///< Scrolled, a #PuglEventScroll - case PUGL_CLIENT: ///< Custom client message, a #PuglEventClient - case PUGL_TIMER: ///< Timer triggered, a #PuglEventTimer - case PUGL_LOOP_ENTER: ///< Recursive loop entered, a #PuglEventLoopEnter - case PUGL_LOOP_LEAVE: ///< Recursive loop left, a #PuglEventLoopLeave - break; - } - - return PUGL_SUCCESS; -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL -#endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 78c6f50c..da6066fa 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -68,13 +68,6 @@ struct Window::PrivateData : IdleCallback { /** Pugl minWidth, minHeight access. */ uint minWidth, minHeight; - /** Pending state of visility, used for the action to be triggered during Pugl create events. */ - enum PendingVisibility { - kPendingVisibilityNone, - kPendingVisibilityShow, - kPendingVisibilityHide - } pendingVisibility; - /** Constructor for a regular, standalone window. */ explicit PrivateData(Application& app, Window* self); @@ -113,9 +106,8 @@ struct Window::PrivateData : IdleCallback { void idleCallback() override; // pugl events - void onPuglDisplay(); - void onPuglReshape(int width, int height); - void onPuglCreate(); + void onPuglConfigure(int width, int height); + void onPuglExpose(); void onPuglClose(); void onPuglMouse(const Events::MouseEvent& ev); From feeb29de55351d08335d501e6605c1940aa8ed81 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 20:56:39 +0100 Subject: [PATCH 069/159] Plug-in all pugl/dpf events except SpecialEvent Signed-off-by: falkTX --- dgl/Base.hpp | 15 +++ dgl/Events.hpp | 9 +- dgl/src/TopLevelWidgetPrivateData.cpp | 78 ++++++++++- dgl/src/TopLevelWidgetPrivateData.hpp | 7 +- dgl/src/WidgetPrivateData.cpp | 120 ++++++++++++++++- dgl/src/WidgetPrivateData.hpp | 8 +- dgl/src/WindowPrivateData.cpp | 116 +++++++++++++++- dgl/src/WindowPrivateData.hpp | 185 +------------------------- 8 files changed, 344 insertions(+), 194 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index 0229512c..e64a28cf 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -130,6 +130,21 @@ enum CrossingMode { kCrossingUngrab ///< Crossing due to a grab release }; +/** + Scroll direction. + + Describes the direction of a scroll event along with whether the scroll is a "smooth" scroll. + The discrete directions are for devices like mouse wheels with constrained axes, + while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads. +*/ +enum ScrollDirection { + kScrollUp, ///< Scroll up + kScrollDown, ///< Scroll down + kScrollLeft, ///< Scroll left + kScrollRight, ///< Scroll right + kScrollSmooth ///< Smooth scroll in any direction +}; + // -------------------------------------------------------------------------------------------------------------------- // Base DGL classes diff --git a/dgl/Events.hpp b/dgl/Events.hpp index b34b5807..dcb468c8 100644 --- a/dgl/Events.hpp +++ b/dgl/Events.hpp @@ -170,19 +170,22 @@ namespace Events Some systems and devices support finer resolution and/or higher values for fast scrolls, so programs should handle any value gracefully. - @a pos The widget-relative coordinates of the pointer. - @a delta The scroll distance. + @a pos The widget-relative coordinates of the pointer. + @a delta The scroll distance. + @a direction The direction of the scroll or "smooth". @see onScroll */ struct ScrollEvent : BaseEvent { Point pos; Point delta; + ScrollDirection direction; /** Constuctor */ ScrollEvent() noexcept : BaseEvent(), pos(0.0, 0.0), - delta(0.0, 0.0) {} + delta(0.0, 0.0), + direction(kScrollSmooth) {} }; /** diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 9edc6646..433dcaa9 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -37,7 +37,37 @@ TopLevelWidget::PrivateData::~PrivateData() window.pData->topLevelWidget = nullptr; } -void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) +bool TopLevelWidget::PrivateData::keyboardEvent(const Events::KeyboardEvent& ev) +{ + // give top-level widget chance to catch this event first + if (self->onKeyboard(ev)) + return true; + + // propagate event to all subwidgets recursively + return selfw->pData->giveKeyboardEventForSubWidgets(ev); +} + +bool TopLevelWidget::PrivateData::specialEvent(const Events::SpecialEvent& ev) +{ + // give top-level widget chance to catch this event first + if (self->onSpecial(ev)) + return true; + + // propagate event to all subwidgets recursively + return selfw->pData->giveSpecialEventForSubWidgets(ev); +} + +bool TopLevelWidget::PrivateData::characterInputEvent(const Events::CharacterInputEvent& ev) +{ + // give top-level widget chance to catch this event first + if (self->onCharacterInput(ev)) + return true; + + // propagate event to all subwidgets recursively + return selfw->pData->giveCharacterInputEventForSubWidgets(ev); +} + +bool TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) { Events::MouseEvent rev = ev; @@ -51,10 +81,52 @@ void TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) // give top-level widget chance to catch this event first if (self->onMouse(ev)) - return; + return true; + + // propagate event to all subwidgets recursively + return selfw->pData->giveMouseEventForSubWidgets(rev); +} + +bool TopLevelWidget::PrivateData::motionEvent(const Events::MotionEvent& ev) +{ + Events::MotionEvent rev = ev; + + if (window.pData->autoScaling) + { + const double autoScaleFactor = window.pData->autoScaleFactor; + + rev.pos.setX(ev.pos.getX() / autoScaleFactor); + rev.pos.setY(ev.pos.getY() / autoScaleFactor); + } + + // give top-level widget chance to catch this event first + if (self->onMotion(ev)) + return true; + + // propagate event to all subwidgets recursively + return selfw->pData->giveMotionEventForSubWidgets(rev); +} + +bool TopLevelWidget::PrivateData::scrollEvent(const Events::ScrollEvent& ev) +{ + Events::ScrollEvent rev = ev; + + if (window.pData->autoScaling) + { + const double autoScaleFactor = window.pData->autoScaleFactor; + + rev.pos.setX(ev.pos.getX() / autoScaleFactor); + rev.pos.setY(ev.pos.getY() / autoScaleFactor); + rev.delta.setX(ev.delta.getX() / autoScaleFactor); + rev.delta.setY(ev.delta.getY() / autoScaleFactor); + } + + // give top-level widget chance to catch this event first + if (self->onScroll(ev)) + return true; // propagate event to all subwidgets recursively - selfw->pData->giveMouseEventForSubWidgets(rev); + return selfw->pData->giveScrollEventForSubWidgets(rev); } void TopLevelWidget::PrivateData::fallbackOnResize() diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index bc4338eb..b3225c32 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -33,7 +33,12 @@ struct TopLevelWidget::PrivateData { explicit PrivateData(TopLevelWidget* const s, Window& w); ~PrivateData(); void display(); - void mouseEvent(const Events::MouseEvent& ev); + bool keyboardEvent(const Events::KeyboardEvent& ev); + bool specialEvent(const Events::SpecialEvent& ev); + bool characterInputEvent(const Events::CharacterInputEvent& ev); + bool mouseEvent(const Events::MouseEvent& ev); + bool motionEvent(const Events::MotionEvent& ev); + bool scrollEvent(const Events::ScrollEvent& ev); void fallbackOnResize(); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 8ee47b22..b641dca9 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -67,12 +67,68 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, } } -void Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) +// ----------------------------------------------------------------------- + +bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const Events::KeyboardEvent& ev) { if (! visible) - return; + return false; if (subWidgets.size() == 0) - return; + return false; + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (widget->isVisible() && widget->onKeyboard(ev)) + return true; + } + + return false; +} + +bool Widget::PrivateData::giveSpecialEventForSubWidgets(const Events::SpecialEvent& ev) +{ + if (! visible) + return false; + if (subWidgets.size() == 0) + return false; + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (widget->isVisible() && widget->onSpecial(ev)) + return true; + } + + return false; +} + +bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const Events::CharacterInputEvent& ev) +{ + if (! visible) + return false; + if (subWidgets.size() == 0) + return false; + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (widget->isVisible() && widget->onCharacterInput(ev)) + return true; + } + + return false; +} + +bool Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) +{ + if (! visible) + return false; + if (subWidgets.size() == 0) + return false; const double x = ev.pos.getX(); const double y = ev.pos.getY(); @@ -88,8 +144,64 @@ void Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) y - widget->getAbsoluteY()); if (widget->onMouse(ev)) - return; + return true; } + + return false; +} + +bool Widget::PrivateData::giveMotionEventForSubWidgets(Events::MotionEvent& ev) +{ + if (! visible) + return false; + if (subWidgets.size() == 0) + return false; + + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (! widget->isVisible()) + continue; + + ev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->onMotion(ev)) + return true; + } + + return false; +} + +bool Widget::PrivateData::giveScrollEventForSubWidgets(Events::ScrollEvent& ev) +{ + if (! visible) + return false; + if (subWidgets.size() == 0) + return false; + + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); + + FOR_EACH_SUBWIDGET_INV(rit) + { + SubWidget* const widget(*rit); + + if (! widget->isVisible()) + continue; + + ev.pos = Point(x - widget->getAbsoluteX(), + y - widget->getAbsoluteY()); + + if (widget->onScroll(ev)) + return true; + } + + return false; } // ----------------------------------------------------------------------- diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 2fcf64e7..81471f47 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -42,7 +42,13 @@ struct Widget::PrivateData { ~PrivateData(); void displaySubWidgets(uint width, uint height, double autoScaleFactor); - void giveMouseEventForSubWidgets(Events::MouseEvent& ev); + + bool giveKeyboardEventForSubWidgets(const Events::KeyboardEvent& ev); + bool giveSpecialEventForSubWidgets(const Events::SpecialEvent& ev); + bool giveCharacterInputEventForSubWidgets(const Events::CharacterInputEvent& ev); + bool giveMouseEventForSubWidgets(Events::MouseEvent& ev); + bool giveMotionEventForSubWidgets(Events::MotionEvent& ev); + bool giveScrollEventForSubWidgets(Events::ScrollEvent& ev); static TopLevelWidget* findTopLevelWidget(Widget* const w); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index a6848881..e8f3cad0 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -384,9 +384,48 @@ void Window::PrivateData::onPuglClose() close(); } +void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) +{ + DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->keyboardEvent(ev); +#endif +} + +void Window::PrivateData::onPuglSpecial(const Events::SpecialEvent& ev) +{ + DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->specialEvent(ev); +#endif +} + +void Window::PrivateData::onPuglText(const Events::CharacterInputEvent& ev) +{ + DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->characterInputEvent(ev); +#endif +} + void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) { - DGL_DBGp("PUGL: onMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); + DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); // if (fModal.childFocus != nullptr) // return fModal.childFocus->focus(); @@ -397,6 +436,32 @@ void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) #endif } +void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) +{ + DGL_DBGp("onPuglMotion : %f %f\n", ev.button, ev.pos.getX(), ev.pos.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->motionEvent(ev); +#endif +} + +void Window::PrivateData::onPuglScroll(const Events::ScrollEvent& ev) +{ + DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + if (topLevelWidget != nullptr) + topLevelWidget->pData->scrollEvent(ev); +#endif +} + static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) @@ -424,6 +489,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View moved/resized, a #PuglEventConfigure case PUGL_CONFIGURE: + // unused x, y (double) pData->onPuglConfigure(event->configure.width, event->configure.height); break; @@ -441,6 +507,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View must be drawn, a #PuglEventExpose case PUGL_EXPOSE: + // unused x, y, width, height (double) pData->onPuglExpose(); break; @@ -459,14 +526,38 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Key pressed, a #PuglEventKey case PUGL_KEY_PRESS: - break; ///< Key released, a #PuglEventKey case PUGL_KEY_RELEASE: + { + // unused x, y, xRoot, yRoot (double) + // TODO special keys? + Events::KeyboardEvent ev; + ev.mod = event->key.state; + ev.flags = event->key.flags; + ev.time = static_cast(event->key.time * 1000.0 + 0.5); + ev.press = event->type == PUGL_KEY_PRESS; + ev.key = event->key.key; + ev.keycode = event->key.keycode; + if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z') + ev.key -= 'a' - 'A'; // a-z -> A-Z + pData->onPuglKey(ev); break; + } ///< Character entered, a #PuglEventText case PUGL_TEXT: + { + // unused x, y, xRoot, yRoot (double) + Events::CharacterInputEvent ev; + ev.mod = event->text.state; + ev.flags = event->text.flags; + ev.time = static_cast(event->text.time * 1000.0 + 0.5); + ev.keycode = event->text.keycode; + ev.character = event->text.character; + std::strncpy(ev.string, event->text.string, sizeof(ev.string)); + pData->onPuglText(ev); break; + } ///< Pointer entered view, a #PuglEventCrossing case PUGL_POINTER_IN: @@ -493,11 +584,29 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Pointer moved, a #PuglEventMotion case PUGL_MOTION: + { + Events::MotionEvent ev; + ev.mod = event->motion.state; + ev.flags = event->motion.flags; + ev.time = static_cast(event->motion.time * 1000.0 + 0.5); + ev.pos = Point(event->motion.x, event->motion.y); + pData->onPuglMotion(ev); break; + } ///< Scrolled, a #PuglEventScroll case PUGL_SCROLL: + { + Events::ScrollEvent ev; + ev.mod = event->scroll.state; + ev.flags = event->scroll.flags; + ev.time = static_cast(event->scroll.time * 1000.0 + 0.5); + ev.pos = Point(event->scroll.x, event->scroll.y); + ev.delta = Point(event->scroll.dx, event->scroll.dy); + ev.direction = static_cast(event->scroll.direction); + pData->onPuglScroll(ev); break; + } ///< Custom client message, a #PuglEventClient case PUGL_CLIENT: @@ -646,6 +755,9 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver return 0; } +#undef DGL_DBG +#undef DGL_DBGF + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index da6066fa..4af83ee0 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -109,20 +109,16 @@ struct Window::PrivateData : IdleCallback { void onPuglConfigure(int width, int height); void onPuglExpose(); void onPuglClose(); + void onPuglKey(const Events::KeyboardEvent& ev); + void onPuglSpecial(const Events::SpecialEvent& ev); + void onPuglText(const Events::CharacterInputEvent& ev); void onPuglMouse(const Events::MouseEvent& ev); + void onPuglMotion(const Events::MotionEvent& ev); + void onPuglScroll(const Events::ScrollEvent& ev); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); -#if 0 - // Fallback build-specific Window functions - struct Fallback { - static void onDisplayBefore(const GraphicsContext& context); - static void onDisplayAfter(const GraphicsContext& context); - static void onReshape(const GraphicsContext& context, uint width, uint height); - }; -#endif - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; @@ -185,33 +181,6 @@ END_NAMESPACE_DGL #endif #endif -#if 0 // ndef DPF_TEST_WINDOW_CPP - // ------------------------------------------------------------------- - // stuff that uses pugl internals or build-specific things - - void init(const bool resizable = false); - void setVisible(const bool visible); - void windowSpecificIdle(); - - // ------------------------------------------------------------------- - - // ------------------------------------------------------------------- - - void addWidget(Widget* const widget); - void removeWidget(Widget* const widget); - - // ------------------------------------------------------------------- - - void onPuglClose(); - void onPuglMouse(const Widget::MouseEvent& ev); - - // ------------------------------------------------------------------- -#endif - -// #ifdef DISTRHO_DEFINES_H_INCLUDED -// friend class DISTRHO_NAMESPACE::UI; -// #endif - #if 0 // ----------------------------------------------------------------------- // Window Private @@ -287,147 +256,6 @@ struct Window::PrivateData { // ------------------------------------------------------------------- - // ------------------------------------------------------------------- - - 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(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(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 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(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(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(dx, dy); - ev.mod = static_cast(puglGetModifiers(fView)); - ev.time = puglGetEventTimestamp(fView); - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - ev.pos = Point(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); - - if (widget->isVisible() && widget->onScroll(ev)) - break; - } - } - - // ------------------------------------------------------------------- - - 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(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); @@ -519,7 +347,4 @@ struct Window::PrivateData { }; #endif -// #undef DGL_DBG -// #undef DGL_DBGF - #endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED From 37b81b503f8c3c3735f9b4114a045f6f50fc97c9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 21:29:25 +0100 Subject: [PATCH 070/159] imgui: use scale factor from DPF Signed-off-by: falkTX --- examples/ImguiSimpleGain/ImGuiUI.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/ImguiSimpleGain/ImGuiUI.cpp b/examples/ImguiSimpleGain/ImGuiUI.cpp index 629ba259..fae28d5f 100644 --- a/examples/ImguiSimpleGain/ImGuiUI.cpp +++ b/examples/ImguiSimpleGain/ImGuiUI.cpp @@ -41,9 +41,6 @@ struct ImGuiUI::Impl void setupGL(); void cleanupGL(); - // perhaps DPF will implement this in the future - float getScaleFactor() const { return 1.0f; } - static int mouseButtonToImGui(int button); ImGuiUI* fSelf = nullptr; @@ -178,7 +175,8 @@ bool ImGuiUI::onMotion(const MotionEvent& event) ImGui::SetCurrentContext(fImpl->fContext); ImGuiIO &io = ImGui::GetIO(); - const float scaleFactor = fImpl->getScaleFactor(); + // FIXME + const double scaleFactor = 1; // getScaleFactor(); io.MousePos.x = std::round(scaleFactor * event.pos.getX()); io.MousePos.y = std::round(scaleFactor * event.pos.getY()); @@ -206,7 +204,7 @@ void ImGuiUI::onResize(const ResizeEvent& event) ImGui::SetCurrentContext(fImpl->fContext); ImGuiIO &io = ImGui::GetIO(); - const float scaleFactor = fImpl->getScaleFactor(); + const double scaleFactor = getScaleFactor(); io.DisplaySize.x = std::round(scaleFactor * width); io.DisplaySize.y = std::round(scaleFactor * height); } @@ -252,7 +250,7 @@ void ImGuiUI::Impl::setupGL() ImGui::SetCurrentContext(fContext); ImGuiIO &io = ImGui::GetIO(); - const float scaleFactor = getScaleFactor(); + const double scaleFactor = fSelf->getScaleFactor(); io.DisplaySize.x = std::round(scaleFactor * fSelf->getWidth()); io.DisplaySize.y = std::round(scaleFactor * fSelf->getHeight()); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; From d7d9d9ea0ddae7175972bdd6f5d6b897870f1c84 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 May 2021 21:30:27 +0100 Subject: [PATCH 071/159] Fix debug build Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index e8f3cad0..7966222b 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -438,7 +438,7 @@ void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) { - DGL_DBGp("onPuglMotion : %f %f\n", ev.button, ev.pos.getX(), ev.pos.getY()); + DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); // if (fModal.childFocus != nullptr) // return fModal.childFocus->focus(); From 74680dedcf658ac9298afea5a7cc9237e46a283e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 12:18:39 +0100 Subject: [PATCH 072/159] Add Window::onFocus event Signed-off-by: falkTX --- dgl/Window.hpp | 18 ++++++++++++------ dgl/src/Window.cpp | 12 ++++++++---- dgl/src/WindowPrivateData.cpp | 15 +++++++++++++-- dgl/src/WindowPrivateData.hpp | 1 + 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 9c5e18eb..1996832d 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -241,12 +241,6 @@ public: inline double getScaling() const noexcept { return getScaling(); } protected: - /** - A function called when the window is resized. - If there is a top-level widget associated with this window, its size will be set right after this function. - */ - virtual void onReshape(uint width, uint height); - /** A function called when the window is attempted to be closed. Returning true closes the window, which is the default behaviour. @@ -254,6 +248,18 @@ protected: */ virtual bool onClose(); + /** + A function called when the window gains or loses the keyboard focus. + The default implementation does nothing. + */ + virtual void onFocus(bool focus); + + /** + A function called when the window is resized. + If there is a top-level widget associated with this window, its size will be set right after this function. + */ + virtual void onReshape(uint width, uint height); + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 1b77915f..09fd0a6b 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -227,14 +227,18 @@ void Window::setGeometryConstraints(const uint minimumWidth, } } -void Window::onReshape(uint, uint) +bool Window::onClose() { - puglFallbackOnResize(pData->view); + return true; } -bool Window::onClose() +void Window::onFocus(bool) { - return true; +} + +void Window::onReshape(uint, uint) +{ + puglFallbackOnResize(pData->view); } #if 0 diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 7966222b..cb4a093c 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -384,6 +384,18 @@ void Window::PrivateData::onPuglClose() close(); } +void Window::PrivateData::onPuglFocus(const bool focus) +{ + DGL_DBGp("onPuglFocus : %i %u %u\n", focus); + +// if (fModal.childFocus != nullptr) +// return fModal.childFocus->focus(); + +#ifndef DPF_TEST_WINDOW_CPP + self->onFocus(focus); +#endif +} + void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) { DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); @@ -518,10 +530,9 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Keyboard focus entered view, a #PuglEventFocus case PUGL_FOCUS_IN: - break; - ///< Keyboard focus left view, a #PuglEventFocus case PUGL_FOCUS_OUT: + pData->onPuglFocus(event->type == PUGL_FOCUS_IN); break; ///< Key pressed, a #PuglEventKey diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 4af83ee0..4a029dc4 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -109,6 +109,7 @@ struct Window::PrivateData : IdleCallback { void onPuglConfigure(int width, int height); void onPuglExpose(); void onPuglClose(); + void onPuglFocus(bool focus); void onPuglKey(const Events::KeyboardEvent& ev); void onPuglSpecial(const Events::SpecialEvent& ev); void onPuglText(const Events::CharacterInputEvent& ev); From bdcdd576a42c8525d2e69dc1683d3f5fe62c671f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 12:24:59 +0100 Subject: [PATCH 073/159] Add CrossingMode for window focus event; Cleanup Signed-off-by: falkTX --- dgl/Window.hpp | 39 +---------------------------------- dgl/src/Window.cpp | 2 +- dgl/src/WindowPrivateData.cpp | 9 ++++---- dgl/src/WindowPrivateData.hpp | 2 +- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 1996832d..9142712a 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -252,7 +252,7 @@ protected: A function called when the window gains or loses the keyboard focus. The default implementation does nothing. */ - virtual void onFocus(bool focus); + virtual void onFocus(bool focus, CrossingMode mode); /** A function called when the window is resized. @@ -278,16 +278,6 @@ END_NAMESPACE_DGL * add eventcrossing/enter-leave event */ -// class StandaloneWindow; -// class Widget; - -// #ifdef DISTRHO_DEFINES_H_INCLUDED -// START_NAMESPACE_DISTRHO -// class UI; -// class UIExporter; -// END_NAMESPACE_DISTRHO -// #endif - #if 0 #ifndef DGL_FILE_BROWSER_DISABLED /** @@ -332,8 +322,6 @@ END_NAMESPACE_DGL void exec(bool lockWait = false); - const GraphicsContext& getGraphicsContext() const noexcept; - void addIdleCallback(IdleCallback* const callback); void removeIdleCallback(IdleCallback* const callback); @@ -341,38 +329,13 @@ END_NAMESPACE_DGL bool openFileBrowser(const FileBrowserOptions& options); #endif - protected: #ifndef DGL_FILE_BROWSER_DISABLED virtual void fileBrowserSelected(const char* filename); #endif - // internal - void _setAutoScaling(double scaling) noexcept; - - virtual void _addWidget(Widget* const widget); - virtual void _removeWidget(Widget* const widget); - void _idle(); - bool handlePluginKeyboard(const bool press, const uint key); bool handlePluginSpecial(const bool press, const Key key); - -// friend class Widget; -// friend class StandaloneWindow; -// #ifdef DISTRHO_DEFINES_H_INCLUDED -// friend class DISTRHO_NAMESPACE::UI; -// friend class DISTRHO_NAMESPACE::UIExporter; -// #endif - - // Prevent copies -// #ifdef DISTRHO_PROPER_CPP11_SUPPORT -// Window& operator=(Window&) = delete; -// Window& operator=(const Window&) = delete; -// #else -// Window& operator=(Window&); -// Window& operator=(const Window&); -// #endif - #endif // ----------------------------------------------------------------------- diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 09fd0a6b..fe3f78cd 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -232,7 +232,7 @@ bool Window::onClose() return true; } -void Window::onFocus(bool) +void Window::onFocus(bool, CrossingMode) { } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index cb4a093c..f3ca1922 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -384,15 +384,15 @@ void Window::PrivateData::onPuglClose() close(); } -void Window::PrivateData::onPuglFocus(const bool focus) +void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) { - DGL_DBGp("onPuglFocus : %i %u %u\n", focus); + DGL_DBGp("onPuglFocus : %i %i\n", focus, mode); // if (fModal.childFocus != nullptr) // return fModal.childFocus->focus(); #ifndef DPF_TEST_WINDOW_CPP - self->onFocus(focus); + self->onFocus(focus, mode); #endif } @@ -532,7 +532,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_FOCUS_IN: ///< Keyboard focus left view, a #PuglEventFocus case PUGL_FOCUS_OUT: - pData->onPuglFocus(event->type == PUGL_FOCUS_IN); + pData->onPuglFocus(event->type == PUGL_FOCUS_IN, + static_cast(event->focus.mode)); break; ///< Key pressed, a #PuglEventKey diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 4a029dc4..f128f3d5 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -109,7 +109,7 @@ struct Window::PrivateData : IdleCallback { void onPuglConfigure(int width, int height); void onPuglExpose(); void onPuglClose(); - void onPuglFocus(bool focus); + void onPuglFocus(bool focus, CrossingMode mode); void onPuglKey(const Events::KeyboardEvent& ev); void onPuglSpecial(const Events::SpecialEvent& ev); void onPuglText(const Events::CharacterInputEvent& ev); From 635e5cede4756bd22fe75dab76758e47778072f2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 14:03:27 +0100 Subject: [PATCH 074/159] Add back modal windows related functionality Signed-off-by: falkTX --- dgl/Window.hpp | 19 +++- dgl/src/Window.cpp | 23 ++--- dgl/src/WindowPrivateData.cpp | 163 +++++++++++++++++++++++++++------- dgl/src/WindowPrivateData.hpp | 138 ++++++++-------------------- 4 files changed, 190 insertions(+), 153 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 9142712a..8351b019 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -55,6 +55,12 @@ public: */ explicit Window(Application& app); + /** + Constructor for a modal window, by having another window as its parent. + The Application instance must be the same between the 2 windows. + */ + explicit Window(Application& app, Window& parent); + /** Constructor for an embed Window without known size, typically used in modules or plugins that run inside another host. @@ -224,6 +230,13 @@ public: */ void repaint(const Rectangle& rect) noexcept; + /** + Run this window as a modal, blocking input events from the parent. + Only valid for windows that have been created with another window as parent (as passed in the constructor). + Can optionally block-wait, but such option is only available if the application is running as standalone. + */ + void runAsModal(bool blockWait = false); + /** Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. */ @@ -239,6 +252,7 @@ public: // TODO deprecated inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } inline double getScaling() const noexcept { return getScaling(); } + inline void exec(bool blockWait = false) { runAsModal(blockWait); } protected: /** @@ -257,6 +271,7 @@ protected: /** A function called when the window is resized. If there is a top-level widget associated with this window, its size will be set right after this function. + TODO this seems wrong, top-level widget should be resized here */ virtual void onReshape(uint width, uint height); @@ -318,10 +333,6 @@ END_NAMESPACE_DGL }; #endif // DGL_FILE_BROWSER_DISABLED - static Window& withTransientParentWindow(Window& transientParentWindow); - - void exec(bool lockWait = false); - void addIdleCallback(IdleCallback* const callback); void removeIdleCallback(IdleCallback* const callback); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index fe3f78cd..b68302ef 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -23,12 +23,12 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Window -// Window::Window(Window& transientParentWindow) -// : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} - Window::Window(Application& app) : pData(new PrivateData(app, this)) {} +Window::Window(Application& app, Window& parent) + : pData(new PrivateData(app, this, parent.pData)) {} + Window::Window(Application& app, const uintptr_t parentWindowHandle, const double scaleFactor, @@ -170,10 +170,7 @@ double Window::getScaleFactor() const noexcept void Window::focus() { - if (! pData->isEmbed) - puglRaiseWindow(pData->view); - - puglGrabFocus(pData->view); + pData->focus(); } void Window::repaint() noexcept @@ -192,6 +189,11 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } +void Window::runAsModal(bool blockWait) +{ + pData->runAsModal(blockWait); +} + void Window::setGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, @@ -242,13 +244,6 @@ void Window::onReshape(uint, uint) } #if 0 -#if 0 -void Window::exec(bool lockWait) -{ - pData->exec(lockWait); -} -#endif - void Window::setTransientWinId(const uintptr_t winId) { puglSetTransientFor(pData->fView, winId); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index f3ca1922..90aa11b7 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -63,12 +63,13 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } -Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transientWindow) +Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData) : app(a), appData(a.pData), self(s), @@ -77,15 +78,16 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi isClosed(true), isVisible(false), isEmbed(false), - scaleFactor(getDesktopScaleFactor()), + scaleFactor(ppData->scaleFactor), autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this, ppData) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); - puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); + puglSetTransientFor(view, puglGetNativeWindow(ppData->view)); } Window::PrivateData::PrivateData(Application& a, Window* const s, @@ -103,7 +105,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { if (isEmbed) { @@ -136,7 +139,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { if (isEmbed) { @@ -279,16 +283,26 @@ void Window::PrivateData::hide() // } #endif - puglHide(view); + if (modal.enabled) + stopModal(); -// if (fModal.enabled) -// exec_fini(); + puglHide(view); isVisible = false; } // ----------------------------------------------------------------------- +void Window::PrivateData::focus() +{ + if (! isEmbed) + puglRaiseWindow(view); + + puglGrabFocus(view); +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::close() { DGL_DBG("Window close\n"); @@ -327,13 +341,91 @@ void Window::PrivateData::idleCallback() // std::free(buffer); // } // #endif -// -// if (fModal.enabled && fModal.parent != nullptr) -// fModal.parent->windowSpecificIdle(); -// self->repaint(); + +// if (modal.enabled && modal.parent != nullptr) +// modal.parent->idleCallback(); } // ----------------------------------------------------------------------- +// modal handling + +void Window::PrivateData::startModal() +{ + DGL_DBG("Window modal loop starting..."); DGL_DBGF; + DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show()); + + // activate modal mode for this window + modal.enabled = true; + + // make parent give focus to us + modal.parent->modal.child = this; + + // make sure both parent and ourselves are visible + modal.parent->show(); + show(); + + DGL_DBG("Ok\n"); +} + +void Window::PrivateData::stopModal() +{ + DGL_DBG("Window modal loop stopping..."); DGL_DBGF; + + // deactivate modal mode + modal.enabled = false; + + // safety checks, make sure we have a parent and we are currently active as the child to give focus to + if (modal.parent == nullptr) + return; + if (modal.parent->modal.child != this) + return; + + // stop parent from giving focus to us, so it behaves like normal + modal.parent->modal.child = nullptr; + + // the mouse position probably changed since the modal appeared, + // so send a mouse motion event to the modal's parent window +#if 0 +#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 +#endif + + DGL_DBG("Ok\n"); +} + +void Window::PrivateData::runAsModal(const bool blockWait) +{ + DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait); + startModal(); + + if (blockWait) + { + DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,); + + while (isVisible && modal.enabled) + appData->idle(10); + + stopModal(); + } + else + { + appData->idle(0); + } +} + +// ----------------------------------------------------------------------- +// pugl events void Window::PrivateData::onPuglConfigure(const int width, const int height) { @@ -372,14 +464,19 @@ void Window::PrivateData::onPuglClose() { DGL_DBG("PUGL: onClose\n"); -// if (fModal.enabled) -// exec_fini(); - if (! self->onClose()) return; -// if (fModal.childFocus != nullptr) -// fModal.childFocus->fSelf->onClose(); + if (modal.enabled) + stopModal(); + + if (modal.child != nullptr) + { + if (modal.child->modal.enabled) + modal.child->stopModal(); + + modal.child->close(); + } close(); } @@ -388,8 +485,8 @@ void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) { DGL_DBGp("onPuglFocus : %i %i\n", focus, mode); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP self->onFocus(focus, mode); @@ -400,8 +497,8 @@ void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) { DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -413,8 +510,8 @@ void Window::PrivateData::onPuglSpecial(const Events::SpecialEvent& ev) { DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -426,8 +523,8 @@ void Window::PrivateData::onPuglText(const Events::CharacterInputEvent& ev) { DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -439,8 +536,8 @@ void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) { DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -452,8 +549,8 @@ void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) { DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -465,8 +562,8 @@ void Window::PrivateData::onPuglScroll(const Events::ScrollEvent& ev) { DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index f128f3d5..859992c9 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -68,11 +68,40 @@ struct Window::PrivateData : IdleCallback { /** Pugl minWidth, minHeight access. */ uint minWidth, minHeight; + /** Modal window setup. */ + struct Modal { +// PrivateData* self; // pointer to PrivateData this Modal class belongs to + PrivateData* parent; // parent of this window (so we can become modal) + PrivateData* child; // child window to give focus to when modal mode is enabled + bool enabled; // wherever modal mode is enabled (only possible if parent != null) + + /** Constructor for a non-modal window. */ + Modal(PrivateData* const s) noexcept + : parent(nullptr), + child(nullptr), + enabled(false) {} + + /** Constructor for a modal window (with a parent). */ + Modal(PrivateData* const s, PrivateData* const p) noexcept + : parent(p), + child(nullptr), + enabled(false) {} + + /** Destructor. */ + ~Modal() noexcept + { + DISTRHO_SAFE_ASSERT(! enabled); + } + } modal; + /** Constructor for a regular, standalone window. */ explicit PrivateData(Application& app, Window* self); + /** Constructor for a modal window. */ + explicit PrivateData(Application& app, Window* self, PrivateData* ppData); + /** Constructor for a regular, standalone window with a transient parent. */ - explicit PrivateData(Application& app, Window* self, Window& transientWindow); +// explicit PrivateData(Application& app, Window* self, Window& transientWindow); /** Constructor for an embed Window, with a few extra hints from the host side. */ explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); @@ -89,6 +118,7 @@ struct Window::PrivateData : IdleCallback { void show(); void hide(); + void focus(); /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). @@ -105,6 +135,11 @@ struct Window::PrivateData : IdleCallback { void idleCallback() override; + // modal handling + void startModal(); + void stopModal(); + void runAsModal(bool blockWait); + // pugl events void onPuglConfigure(int width, int height); void onPuglExpose(); @@ -128,39 +163,6 @@ struct Window::PrivateData : IdleCallback { END_NAMESPACE_DGL #if 0 - // this one depends on build type - // GraphicsContext fContext; - - bool fFirstInit; - bool fVisible; - bool fUsingEmbed; - double fScaling; - double fAutoScaling; - - 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; @@ -189,74 +191,6 @@ END_NAMESPACE_DGL struct Window::PrivateData { // ------------------------------------------------------------------- - 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"); - } - - // ------------------------------------------------------------------- - bool handlePluginSpecial(const bool press, const Key key) { DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); From 84cb9db4fdf88814bc72c231340b35fac952dd90 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 15:27:51 +0100 Subject: [PATCH 075/159] Start splitting some image widgets into new generic file Signed-off-by: falkTX --- dgl/Cairo.hpp | 54 +++++++++++ dgl/ImageBaseWidgets.hpp | 53 +++++++++++ dgl/ImageWidgets.hpp | 29 +----- dgl/Makefile | 1 + dgl/OpenGL.hpp | 10 ++- dgl/StandaloneWindow.hpp | 9 +- dgl/src/Cairo.cpp | 64 +++++++++++++ dgl/src/ImageBaseWidgets.cpp | 91 +++++++++++++++++++ dgl/src/ImageWidgets.cpp | 70 +-------------- dgl/src/OpenGL.cpp | 169 +++++++++++++---------------------- 10 files changed, 347 insertions(+), 203 deletions(-) create mode 100644 dgl/ImageBaseWidgets.hpp create mode 100644 dgl/src/ImageBaseWidgets.cpp diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 9a896ec7..d0f8014e 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -17,6 +17,8 @@ #ifndef DGL_CAIRO_HPP_INCLUDED #define DGL_CAIRO_HPP_INCLUDED +#include "ImageBase.hpp" +#include "ImageBaseWidgets.hpp" #include "SubWidget.hpp" #include "TopLevelWidget.hpp" @@ -34,6 +36,54 @@ struct CairoGraphicsContext : GraphicsContext cairo_t* handle; }; +// -------------------------------------------------------------------------------------------------------------------- + +/** + Cairo Image class. + + TODO ... + */ +class CairoImage : public ImageBase +{ +public: + /** + Constructor for a null Image. + */ + CairoImage(); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + CairoImage(const char* const rawData, + const uint width, + const uint height); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + CairoImage(const char* const rawData, + const Size& size); + + /** + Constructor using another image data. + */ + CairoImage(const CairoImage& image); + + /** + Destructor. + */ + ~CairoImage() override; + + /** + Draw this image at position @a pos using the graphics context @a context. + */ + void drawAt(const GraphicsContext& context, const Point& pos) override; +}; + +// -------------------------------------------------------------------------------------------------------------------- + /** Cairo SubWidget, handy class that takes graphics context during onDisplay and passes it in a new function. */ @@ -78,6 +128,10 @@ protected: // -------------------------------------------------------------------------------------------------------------------- +typedef ImageBaseAboutWindow CairoImageAboutWindow; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp new file mode 100644 index 00000000..3fef7e63 --- /dev/null +++ b/dgl/ImageBaseWidgets.hpp @@ -0,0 +1,53 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_IMAGE_BASE_WIDGETS_HPP_INCLUDED +#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED + +#include "StandaloneWindow.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +template +class ImageBaseAboutWindow : public StandaloneWindow +{ +public: + explicit ImageBaseAboutWindow(Window& parentWindow, const ImageType& image = ImageType()); + explicit ImageBaseAboutWindow(TopLevelWidget* parentTopLevelWidget, const ImageType& image = ImageType()); + + void setImage(const ImageType& image); + +protected: + void onDisplay() override; + bool onKeyboard(const KeyboardEvent&) override; + bool onMouse(const MouseEvent&) override; + + // FIXME needed? + void onReshape(uint width, uint height) override; + +private: + ImageType img; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index ec465320..245daea9 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,38 +18,13 @@ #define DGL_IMAGE_WIDGETS_HPP_INCLUDED #include "Image.hpp" -#include "StandaloneWindow.hpp" +#include "ImageBaseWidgets.hpp" #include "SubWidget.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -class ImageAboutWindow : public StandaloneWindow -{ -public: - explicit ImageAboutWindow(Window& parentWindow, const Image& image = Image()); - explicit ImageAboutWindow(TopLevelWidget* parentTopLevelWidget, const Image& image = Image()); - - void setImage(const Image& image); - - // TODO - void exec() {} - -protected: - void onDisplay() override; - bool onKeyboard(const KeyboardEvent&) override; - bool onMouse(const MouseEvent&) override; - void onReshape(uint width, uint height) override; - -private: - Image fImgBackground; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow) -}; - -// ----------------------------------------------------------------------- - class ImageButton : public SubWidget { public: diff --git a/dgl/Makefile b/dgl/Makefile index b8a3db4f..2e228b68 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -29,6 +29,7 @@ OBJS_common = \ ../build/dgl/Color.cpp.o \ ../build/dgl/Geometry.cpp.o \ ../build/dgl/ImageBase.cpp.o \ + ../build/dgl/ImageBaseWidgets.cpp.o \ ../build/dgl/Resources.cpp.o \ ../build/dgl/SubWidget.cpp.o \ ../build/dgl/SubWidgetPrivateData.cpp.o \ diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index b0f37252..55905bec 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,6 +18,7 @@ #define DGL_OPENGL_HPP_INCLUDED #include "ImageBase.hpp" +#include "ImageBaseWidgets.hpp" // ----------------------------------------------------------------------- // Fix OpenGL includes for Windows, based on glfw code (part 1) @@ -241,6 +242,13 @@ private: // ----------------------------------------------------------------------- +typedef ImageBaseAboutWindow OpenGLImageAboutWindow; + +// TODO deprecated +typedef OpenGLImageAboutWindow ImageAboutWindow; + +// ----------------------------------------------------------------------- + END_NAMESPACE_DGL #endif diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 85bfd4b7..5db33926 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -29,12 +29,19 @@ class StandaloneWindow : public Window, { public: /** - Constructor. + Constructor without parent. */ StandaloneWindow(Application& app) : Window(app), TopLevelWidget((Window&)*this) {} + /** + Constructor with parent window, typically used to run as modal. + */ + StandaloneWindow(Application& app, Window& parent) + : Window(app, parent), + TopLevelWidget((Window&)*this) {} + /** Overloaded functions to ensure they apply to the Window class. */ diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 1f423c0d..79eca970 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -63,6 +63,37 @@ void Rectangle::_draw(const bool outline) notImplemented("Rectangle::draw"); } +// ----------------------------------------------------------------------- +// CairoImage + +CairoImage::CairoImage() + : ImageBase() {} + +CairoImage::CairoImage(const char* const rawData, const uint width, const uint height) + : ImageBase(rawData, width, height) {} + +CairoImage::CairoImage(const char* const rawData, const Size& size) + : ImageBase(rawData, size) {} + +CairoImage::CairoImage(const CairoImage& image) + : ImageBase(image.rawData, image.size) {} + +CairoImage::~CairoImage() +{ +} + +void CairoImage::drawAt(const GraphicsContext&, const Point&) +{ +} + +// ----------------------------------------------------------------------- + +template <> +void ImageBaseAboutWindow::onDisplay() +{ + img.draw(getGraphicsContext()); +} + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) @@ -97,6 +128,39 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept return context; } +// ----------------------------------------------------------------------- +// Possible template data types + +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + +template class ImageBaseAboutWindow; + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp new file mode 100644 index 00000000..e01e2b10 --- /dev/null +++ b/dgl/src/ImageBaseWidgets.cpp @@ -0,0 +1,91 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "../ImageBaseWidgets.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +template +ImageBaseAboutWindow::ImageBaseAboutWindow(Window& parentWindow, const ImageType& image) + : StandaloneWindow(parentWindow.getApp(), parentWindow), + img(image) +{ + setResizable(false); + setTitle("About"); + + if (image.isValid()) + setSize(image.getSize()); +} + +template +ImageBaseAboutWindow::ImageBaseAboutWindow(TopLevelWidget* const parentTopLevelWidget, const ImageType& image) + : StandaloneWindow(parentTopLevelWidget->getApp(), parentTopLevelWidget->getWindow()), + img(image) +{ + setResizable(false); + setTitle("About"); + + if (image.isValid()) + setSize(image.getSize()); +} + +template +void ImageBaseAboutWindow::setImage(const ImageType& image) +{ + if (img == image) + return; + + img = image; + setSize(image.getSize()); +} + +template +bool ImageBaseAboutWindow::onKeyboard(const KeyboardEvent& ev) +{ + if (ev.press && ev.key == kCharEscape) + { + close(); + return true; + } + + return false; +} + +template +bool ImageBaseAboutWindow::onMouse(const MouseEvent& ev) +{ + if (ev.press) + { + close(); + return true; + } + + return false; +} + +template +void ImageBaseAboutWindow::onReshape(uint width, uint height) +{ + // FIXME needed? + TopLevelWidget::setSize(width, height); + StandaloneWindow::onReshape(width, height); +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index 80221b23..a22d8b6e 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -15,6 +15,8 @@ */ #include "../Image.hpp" +#include "../ImageBaseWidgets.hpp" + #include "Common.hpp" #include "WidgetPrivateData.hpp" @@ -25,74 +27,6 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -ImageAboutWindow::ImageAboutWindow(Window& parentWindow, const Image& image) - : StandaloneWindow(parentWindow.getApp()), - fImgBackground(image) -{ - // TODO set transient - Window::setResizable(false); - Window::setTitle("About"); - - if (image.isValid()) - Window::setSize(image.getSize()); -} - -ImageAboutWindow::ImageAboutWindow(TopLevelWidget* const parentTopLevelWidget, const Image& image) - : StandaloneWindow(parentTopLevelWidget->getApp()), - fImgBackground(image) -{ - // TODO set transient - Window::setResizable(false); - Window::setTitle("About"); - - if (image.isValid()) - Window::setSize(image.getSize()); -} - -void ImageAboutWindow::setImage(const Image& image) -{ - if (fImgBackground == image) - return; - - fImgBackground = image; - Window::setSize(image.getSize()); -} - -void ImageAboutWindow::onDisplay() -{ - fImgBackground.draw(); -} - -bool ImageAboutWindow::onKeyboard(const KeyboardEvent& ev) -{ - if (ev.press && ev.key == kCharEscape) - { - Window::close(); - return true; - } - - return false; -} - -bool ImageAboutWindow::onMouse(const MouseEvent& ev) -{ - if (ev.press) - { - Window::close(); - return true; - } - - return false; -} - -void ImageAboutWindow::onReshape(uint width, uint height) -{ - Widget::setSize(width, height); - Window::onReshape(width, height); -} - -// ----------------------------------------------------------------------- - struct ImageButton::PrivateData { ButtonImpl impl; Image imageNormal; diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 13021068..d9fecc8a 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -15,6 +15,8 @@ */ #include "../OpenGL.hpp" +#include "../ImageWidgets.hpp" + #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" @@ -110,39 +112,6 @@ void Rectangle::_draw(const bool outline) glEnd(); } -// ----------------------------------------------------------------------- -// Possible template data types - -#ifndef DPF_TEST_DEMO -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -#endif - // ----------------------------------------------------------------------- OpenGLImage::OpenGLImage() @@ -152,13 +121,6 @@ OpenGLImage::OpenGLImage() fTextureId(0), setupCalled(false) {} -OpenGLImage::OpenGLImage(const OpenGLImage& image) - : ImageBase(image), - fFormat(image.fFormat), - fType(image.fType), - fTextureId(0), - setupCalled(false) {} - OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) : ImageBase(rawData, width, height), fFormat(format), @@ -173,6 +135,13 @@ OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, cons fTextureId(0), setupCalled(false) {} +OpenGLImage::OpenGLImage(const OpenGLImage& image) + : ImageBase(image), + fFormat(image.fFormat), + fType(image.fType), + fTextureId(0), + setupCalled(false) {} + OpenGLImage::~OpenGLImage() { if (setupCalled) { @@ -324,72 +293,13 @@ void OpenGLImage::drawAt(const Point& pos) // ----------------------------------------------------------------------- -#if 0 -void Widget::PrivateData::display(const uint width, - const uint height, - const double autoScaleFactor, - const bool renderingSubWidget) +template <> +void ImageBaseAboutWindow::onDisplay() { - printf("Widget::PrivateData::display INIT\n"); - - if (/*(skipDisplay && ! renderingSubWidget) ||*/ size.isInvalid() || ! visible) - return; - - bool needsDisableScissor = false; - - // reset color - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - -#if 0 - if (/*needsFullViewport ||*/ (absolutePos.isZero() && size == Size(width, height))) -#endif - { - // full viewport size - glViewport(0, - -(height * autoScaleFactor - height), - width * autoScaleFactor, - height * autoScaleFactor); - } -#if 0 - else if (needsScaling) - { - // limit viewport to widget bounds - glViewport(absolutePos.getX(), - height - self->getHeight() - absolutePos.getY(), - self->getWidth(), - self->getHeight()); - } - else - { - // only set viewport pos - glViewport(absolutePos.getX() * autoScaleFactor, - -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), - std::round(width * autoScaleFactor), - std::round(height * autoScaleFactor)); - - // then cut the outer bounds - glScissor(absolutePos.getX() * autoScaleFactor, - height - std::round((self->getHeight() + absolutePos.getY()) * autoScaleFactor), - std::round(self->getWidth() * autoScaleFactor), - std::round(self->getHeight() * autoScaleFactor)); - - glEnable(GL_SCISSOR_TEST); - needsDisableScissor = true; - } -#endif - - // display widget - self->onDisplay(); - - if (needsDisableScissor) - { - glDisable(GL_SCISSOR_TEST); - needsDisableScissor = false; - } - - displaySubWidgets(width, height, autoScaleFactor); + img.draw(); } -#endif + +// ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) { @@ -453,9 +363,9 @@ void TopLevelWidget::PrivateData::display() // full viewport size if (window.pData->autoScaling) - glViewport(0, -height, width, height); - else glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); + else + glViewport(0, -height, width, height); // main widget drawing self->onDisplay(); @@ -472,5 +382,52 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept } // ----------------------------------------------------------------------- +// Possible template data types + +#ifndef DPF_TEST_DEMO +// // FIXME +// template class Line; +// template class Line; +// template class Line; +// template class Line; +// template class Line; +// template class Line; +// +// template class Circle; +// template class Circle; +// template class Circle; +// template class Circle; +// template class Circle; +// template class Circle; +// +// template class Triangle; +// template class Triangle; +// template class Triangle; +// template class Triangle; +// template class Triangle; +// template class Triangle; +// +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +#endif + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +// ----------------------------------------------------------------------- +// templated classes + +#include "ImageBaseWidgets.cpp" + +START_NAMESPACE_DGL + +template class ImageBaseAboutWindow; END_NAMESPACE_DGL + +// ----------------------------------------------------------------------- From 8a143c8de6aad8e5a4765fe9d5a08123087ce658 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 16:46:16 +0100 Subject: [PATCH 076/159] Cleanup and changed needed to make Mini-Series fully work Signed-off-by: falkTX --- dgl/Makefile | 6 -- dgl/OpenGL.hpp | 10 --- dgl/StandaloneWindow.hpp | 31 ++------- dgl/SubWidget.hpp | 7 ++ dgl/Window.hpp | 1 - dgl/src/ImageWidgets.cpp | 4 +- dgl/src/OpenGL.cpp | 113 +++++++++++++++---------------- dgl/src/StandaloneWindow.cpp | 65 ------------------ dgl/src/SubWidget.cpp | 14 +++- dgl/src/SubWidgetPrivateData.cpp | 1 + dgl/src/SubWidgetPrivateData.hpp | 2 + dgl/src/WindowPrivateData.cpp | 38 +++++++---- dgl/src/WindowPrivateData.hpp | 3 +- 13 files changed, 109 insertions(+), 186 deletions(-) delete mode 100644 dgl/src/StandaloneWindow.cpp diff --git a/dgl/Makefile b/dgl/Makefile index 2e228b68..2612c014 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -40,7 +40,6 @@ OBJS_common = \ ../build/dgl/Window.cpp.o \ ../build/dgl/WindowPrivateData.cpp.o -# ../build/dgl/StandaloneWindow.cpp.o \ # ../build/dgl/WindowFileBrowser.cpp.o # TODO: ImageWidgets.cpp @@ -65,11 +64,6 @@ OBJS_opengl = $(OBJS_common) \ ../build/dgl/NanoVG.cpp.opengl.o \ ../build/dgl/pugl.cpp.opengl.o -# ../build/dgl/Image.cpp.opengl.o \ -# ../build/dgl/ImageWidgets.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 diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 55905bec..3f44072e 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -185,16 +185,6 @@ public: const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept; - /** - TODO document this. - */ - void setup(); - - /** - TODO document this. - */ - void cleanup(); - /** Get the image format. */ diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 5db33926..67031385 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -53,33 +53,10 @@ public: uint getHeight() const noexcept { return Window::getHeight(); } const Size getSize() const noexcept { return Window::getSize(); } void repaint() noexcept { Window::repaint(); } - - /** - Overloaded functions to ensure size changes apply on both TopLevelWidget and Window classes. - */ - void setWidth(uint width) - { - TopLevelWidget::setWidth(width); - Window::setWidth(width); - } - - void setHeight(uint height) - { - TopLevelWidget::setHeight(height); - Window::setHeight(height); - } - - void setSize(uint width, uint height) - { - TopLevelWidget::setSize(width, height); - Window::setSize(width, height); - } - - void setSize(const Size& size) - { - TopLevelWidget::setSize(size); - Window::setSize(size); - } + void setWidth(uint width) { Window::setWidth(width); } + void setHeight(uint height) { Window::setHeight(height); } + void setSize(uint width, uint height) { Window::setSize(width, height); } + void setSize(const Size& size) { Window::setSize(size); } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) }; diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index 83118d32..ce51c0af 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -52,12 +52,14 @@ public: /** Check if this widget contains the point defined by @a x and @a y. */ + // TODO rename as containsRelativePos template bool contains(T x, T y) const noexcept; /** Check if this widget contains the point @a pos. */ + // TODO rename as containsRelativePos template bool contains(const Point& pos) const noexcept; @@ -119,6 +121,11 @@ public: */ void repaint() noexcept override; + /** + Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing. + */ + void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true); + protected: /** A function called when the subwidget's absolute position is changed. diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 8351b019..6c27f84a 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -271,7 +271,6 @@ protected: /** A function called when the window is resized. If there is a top-level widget associated with this window, its size will be set right after this function. - TODO this seems wrong, top-level widget should be resized here */ virtual void onReshape(uint width, uint height); diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index a22d8b6e..371c9cca 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -551,9 +551,7 @@ ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcep fEndPos(), fSliderArea() { - /* TODO - setNeedsFullViewport(); - */ + setNeedsFullViewportDrawing(); } float ImageSlider::getValue() const noexcept diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index d9fecc8a..60924ac2 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -114,44 +114,81 @@ void Rectangle::_draw(const bool outline) // ----------------------------------------------------------------------- +static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) +{ + DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + static_cast(image.getWidth()), + static_cast(image.getHeight()), + 0, + image.getFormat(), image.getType(), image.getRawData()); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + OpenGLImage::OpenGLImage() : ImageBase(), fFormat(0), fType(0), fTextureId(0), - setupCalled(false) {} + setupCalled(false) +{ + glGenTextures(1, &fTextureId); + DISTRHO_SAFE_ASSERT(fTextureId != 0); +} OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) : ImageBase(rawData, width, height), fFormat(format), fType(type), fTextureId(0), - setupCalled(false) {} + setupCalled(false) +{ + glGenTextures(1, &fTextureId); + DISTRHO_SAFE_ASSERT(fTextureId != 0); +} OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const GLenum format, const GLenum type) : ImageBase(rawData, size), fFormat(format), fType(type), fTextureId(0), - setupCalled(false) {} + setupCalled(false) +{ + glGenTextures(1, &fTextureId); + DISTRHO_SAFE_ASSERT(fTextureId != 0); +} OpenGLImage::OpenGLImage(const OpenGLImage& image) : ImageBase(image), fFormat(image.fFormat), fType(image.fType), fTextureId(0), - setupCalled(false) {} + setupCalled(false) +{ + glGenTextures(1, &fTextureId); + DISTRHO_SAFE_ASSERT(fTextureId != 0); +} OpenGLImage::~OpenGLImage() { - if (setupCalled) { - // FIXME test if this is still necessary with new pugl -#ifndef DISTRHO_OS_MAC - if (fTextureId != 0) - cleanup(); -#endif - DISTRHO_SAFE_ASSERT(fTextureId == 0); - } + if (fTextureId != 0) + glDeleteTextures(1, &fTextureId); } void OpenGLImage::loadFromMemory(const char* const rawData, @@ -175,43 +212,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, setupCalled = false; } -void OpenGLImage::setup() -{ - setupCalled = true; - DISTRHO_SAFE_ASSERT_RETURN(fTextureId == 0,); - DISTRHO_SAFE_ASSERT_RETURN(isValid(),); - - glGenTextures(1, &fTextureId); - DISTRHO_SAFE_ASSERT_RETURN(fTextureId != 0,); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fTextureId); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(size.getWidth()), static_cast(size.getHeight()), 0, - fFormat, fType, rawData); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -void OpenGLImage::cleanup() -{ - DISTRHO_SAFE_ASSERT_RETURN(fTextureId != 0,); - glDeleteTextures(1, &fTextureId); - fTextureId = 0; -} - GLenum OpenGLImage::getFormat() const noexcept { return fFormat; @@ -249,18 +249,15 @@ void OpenGLImage::drawAt(const int x, const int y) void OpenGLImage::drawAt(const Point& pos) { - if (isInvalid()) + if (fTextureId == 0 || isInvalid()) return; if (! setupCalled) { - // TODO check if this is valid, give warning about needing to call setup/cleanup manually - setup(); + setupOpenGLImage(*this, fTextureId); + setupCalled = true; } - if (fTextureId == 0) - return; - glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fTextureId); @@ -305,7 +302,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const { bool needsDisableScissor = false; - if (absolutePos.isZero() && self->getSize() == Size(width, height)) + if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size(width, height))) { // full viewport size glViewport(0, @@ -348,7 +345,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const needsDisableScissor = false; } -// displaySubWidgets(width, height, autoScaleFactor); + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- @@ -365,7 +362,7 @@ void TopLevelWidget::PrivateData::display() if (window.pData->autoScaling) glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); else - glViewport(0, -height, width, height); + glViewport(0, 0, width, height); // main widget drawing self->onDisplay(); diff --git a/dgl/src/StandaloneWindow.cpp b/dgl/src/StandaloneWindow.cpp deleted file mode 100644 index 6de8a291..00000000 --- a/dgl/src/StandaloneWindow.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho - * - * 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 "../TopLevelWidget.hpp" - -START_NAMESPACE_DGL - -// ----------------------------------------------------------------------- - -StandaloneWindow::StandaloneWindow(Application& app) - : Window(app), - TopLevelWidget(this) {} - -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); -} - -#if 0 -void StandaloneWindow::_addWidget(TopLevelWidget* widget) -{ - if (fWidget == nullptr) - { - fWidget = widget; - fWidget->pData->needsFullViewport = true; - } - Window::_addWidget(widget); -} - -void StandaloneWindow::_removeWidget(TopLevelWidget* widget) -{ - if (fWidget == widget) - { - fWidget->pData->needsFullViewport = false; - fWidget = nullptr; - } - Window::_removeWidget(widget); -} -#endif - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 6cd8e6d9..b22360ca 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -33,7 +33,7 @@ SubWidget::~SubWidget() template bool SubWidget::contains(T x, T y) const noexcept { - return Rectangle(getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight()).contains(x, y); + return Rectangle(0, 0, getWidth(), getHeight()).contains(x, y); } template @@ -111,7 +111,17 @@ void SubWidget::repaint() noexcept return; if (TopLevelWidget* const topw = getTopLevelWidget()) - topw->repaint(getConstrainedAbsoluteArea()); + { + if (pData->needsFullViewportForDrawing) + topw->repaint(); + else + topw->repaint(getConstrainedAbsoluteArea()); + } +} + +void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing) +{ + pData->needsFullViewportForDrawing = needsFullViewportForDrawing; } void SubWidget::onPositionChanged(const PositionChangedEvent&) diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp index 039aa9b8..737b7024 100644 --- a/dgl/src/SubWidgetPrivateData.cpp +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -23,6 +23,7 @@ START_NAMESPACE_DGL SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) : self(s), + selfw((Widget*)s), parentWidget(pw), absolutePos(), needsViewportScaling(false) diff --git a/dgl/src/SubWidgetPrivateData.hpp b/dgl/src/SubWidgetPrivateData.hpp index 8a219f52..bb8f7bad 100644 --- a/dgl/src/SubWidgetPrivateData.hpp +++ b/dgl/src/SubWidgetPrivateData.hpp @@ -25,8 +25,10 @@ START_NAMESPACE_DGL struct SubWidget::PrivateData { SubWidget* const self; + Widget* const selfw; Widget* const parentWidget; Point absolutePos; + bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds bool needsViewportScaling; // needed for NanoVG explicit PrivateData(SubWidget* const s, Widget* const pw); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 90aa11b7..e55ad5ae 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -205,6 +205,7 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r rect.width = width; rect.height = height; puglSetFrame(view, rect); + puglSetWindowSize(view, width, height); // FIXME this is bad puglRealize(view); @@ -293,16 +294,6 @@ void Window::PrivateData::hide() // ----------------------------------------------------------------------- -void Window::PrivateData::focus() -{ - if (! isEmbed) - puglRaiseWindow(view); - - puglGrabFocus(view); -} - -// ----------------------------------------------------------------------- - void Window::PrivateData::close() { DGL_DBG("Window close\n"); @@ -317,6 +308,16 @@ void Window::PrivateData::close() // ----------------------------------------------------------------------- +void Window::PrivateData::focus() +{ + if (! isEmbed) + puglRaiseWindow(view); + + puglGrabFocus(view); +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::setResizable(const bool resizable) { DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,); @@ -360,6 +361,10 @@ void Window::PrivateData::startModal() // make parent give focus to us modal.parent->modal.child = this; + // FIXME? + PuglRect rect = puglGetFrame(view); + puglSetDefaultSize(view, rect.width, rect.height); + // make sure both parent and ourselves are visible modal.parent->show(); show(); @@ -446,6 +451,9 @@ void Window::PrivateData::onPuglConfigure(const int width, const int height) if (topLevelWidget != nullptr) topLevelWidget->setSize(width, height); #endif + + // always repaint after a resize + puglPostRedisplay(view); } void Window::PrivateData::onPuglExpose() @@ -488,9 +496,7 @@ void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) if (modal.child != nullptr) return modal.child->focus(); -#ifndef DPF_TEST_WINDOW_CPP self->onFocus(focus, mode); -#endif } void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) @@ -571,12 +577,16 @@ void Window::PrivateData::onPuglScroll(const Events::ScrollEvent& ev) #endif } +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); +#endif PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) { - printEvent(event, "pugl event: ", true); Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) + printEvent(event, "pugl event: ", true); +#endif switch (event->type) { @@ -739,6 +749,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu // ----------------------------------------------------------------------- +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) static int printModifiers(const uint32_t mods) { return fprintf(stderr, "Modifiers:%s%s%s%s\n", @@ -863,6 +874,7 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver return 0; } +#endif #undef DGL_DBG #undef DGL_DBGF diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 859992c9..e00f97b7 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -118,7 +118,6 @@ struct Window::PrivateData : IdleCallback { void show(); void hide(); - void focus(); /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). @@ -129,6 +128,8 @@ struct Window::PrivateData : IdleCallback { */ void close(); + void focus(); + void setResizable(bool resizable); const GraphicsContext& getGraphicsContext() const noexcept; From 538ae8ab2401111df543878878137f179ab7bc07 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 17:01:35 +0100 Subject: [PATCH 077/159] Allow modals to prevent themselves from closing Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index e55ad5ae..cadd93b9 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -472,18 +472,25 @@ void Window::PrivateData::onPuglClose() { DGL_DBG("PUGL: onClose\n"); - if (! self->onClose()) - return; + // if we have a parent or running as standalone we can prevent closing in certain conditions + if (modal.parent != nullptr || appData->isStandalone) + { + // parent gives focus to us as modal, prevent closing + if (modal.child != nullptr) + return modal.child->focus(); + + // ask window if we should close + if (! self->onClose()) + return; + } if (modal.enabled) stopModal(); - if (modal.child != nullptr) + if (PrivateData* const child = modal.child) { - if (modal.child->modal.enabled) - modal.child->stopModal(); - - modal.child->close(); + modal.child = nullptr; + child->close(); } close(); From 20199dfd053e2a00777f6b3b10de82852a8cf8f5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 17:02:53 +0100 Subject: [PATCH 078/159] Do not build big demo for now Signed-off-by: falkTX --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 71ab42e7..cd43a698 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,7 +25,7 @@ ifeq ($(HAVE_CAIRO),true) WTESTS = Window.cairo endif ifeq ($(HAVE_OPENGL),true) -TESTS += Demo +# TESTS += Demo WTESTS = Window.opengl endif ifeq ($(HAVE_VULKAN),true) From 1f1e3ca9d983389022b0257e48a250793136764d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 17:52:48 +0100 Subject: [PATCH 079/159] Implement custom timers Signed-off-by: falkTX --- Makefile.base.mk | 18 +++++++++++++++++- dgl/Application.hpp | 4 ++-- dgl/TopLevelWidget.hpp | 2 ++ dgl/Window.hpp | 19 +++++++++++++++++++ dgl/src/TopLevelWidget.cpp | 10 ++++++++++ dgl/src/Window.cpp | 14 ++++++++++++++ dgl/src/WindowPrivateData.cpp | 27 +++++++++++++++++++++++++++ dgl/src/WindowPrivateData.hpp | 4 ++++ 8 files changed, 95 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 5eaea285..6568471e 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -225,7 +225,10 @@ HAVE_OPENGL = true else HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) ifneq ($(HAIKU),true) -HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) +HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) +HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) +HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) +HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) endif endif @@ -254,6 +257,19 @@ ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) ifeq ($(HAVE_X11),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) +ifeq ($(HAVE_XCURSOR),true) +# TODO -DHAVE_XCURSOR +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor) +DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xcursor) +endif +ifeq ($(HAVE_XEXT),true) +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xext) -DHAVE_XEXT -DHAVE_XSYNC +DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xext) +endif +ifeq ($(HAVE_XRANDR),true) +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR +DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) +endif endif endif diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 3d574ca0..2c166b8e 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -77,12 +77,12 @@ public: Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle). There are no guarantees in terms of timing. */ - void addIdleCallback(IdleCallback* const callback); + void addIdleCallback(IdleCallback* callback); /** Remove an idle callback previously added via addIdleCallback(). */ - void removeIdleCallback(IdleCallback* const callback); + void removeIdleCallback(IdleCallback* callback); private: struct PrivateData; diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index fe859766..e7ac44d3 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -67,6 +67,8 @@ public: Window& getWindow() const noexcept; // TODO group stuff after here, convenience functions present in Window class + bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); + bool removeIdleCallback(IdleCallback* callback); double getScaleFactor() const noexcept; void repaint() noexcept; void repaint(const Rectangle& rect) noexcept; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 6c27f84a..5defcf6b 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -190,6 +190,25 @@ public: */ void setIgnoringKeyRepeat(bool ignore) noexcept; + /** + Add a callback function to be triggered on every idle cycle or on a specific timer frequency. + You can add more than one, and remove them at anytime with removeIdleCallback(). + This can be used to perform some action at a regular interval with relatively low frequency. + + If providing a timer frequency, there are a few things to note: + 1. There is a platform-specific limit to the number of supported timers, and overhead associated with each, + so you should create only a few timers and perform several tasks in one if necessary. + 2. This timer frequency is not guaranteed to have a resolution better than 10ms + (the maximum timer resolution on Windows) and may be rounded up if it is too short. + On X11 and MacOS, a resolution of about 1ms can usually be relied on. + */ + bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); + + /** + Remove an idle callback previously added via addIdleCallback(). + */ + bool removeIdleCallback(IdleCallback* callback); + /** Get the application associated with this window. */ diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 9d553604..9f68360e 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -40,6 +40,16 @@ Window& TopLevelWidget::getWindow() const noexcept return pData->window; } +bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) +{ + return pData->window.addIdleCallback(callback, timerFrequencyInMs); +} + +bool TopLevelWidget::removeIdleCallback(IdleCallback* const callback) +{ + return pData->window.removeIdleCallback(callback); +} + double TopLevelWidget::getScaleFactor() const noexcept { return pData->window.getScaleFactor(); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index b68302ef..800daab3 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -153,6 +153,20 @@ void Window::setIgnoringKeyRepeat(const bool ignore) noexcept puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); } +bool Window::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) +{ + DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false) + + return pData->addIdleCallback(callback, timerFrequencyInMs); +} + +bool Window::removeIdleCallback(IdleCallback* const callback) +{ + DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false) + + return pData->removeIdleCallback(callback); +} + Application& Window::getApp() const noexcept { return pData->app; diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index cadd93b9..9d962127 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -347,6 +347,31 @@ void Window::PrivateData::idleCallback() // modal.parent->idleCallback(); } +// ----------------------------------------------------------------------- + +bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) +{ + if (timerFrequencyInMs == 0) + { + appData->idleCallbacks.push_back(callback); + return true; + } + + return puglStartTimer(view, (uintptr_t)callback, static_cast(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS; +} + +bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback) +{ + if (std::find(appData->idleCallbacks.begin(), + appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end()) + { + appData->idleCallbacks.remove(callback); + return true; + } + + return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS; +} + // ----------------------------------------------------------------------- // modal handling @@ -740,6 +765,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Timer triggered, a #PuglEventTimer case PUGL_TIMER: + if (IdleCallback* const idleCallback = reinterpret_cast(event->timer.id)) + idleCallback->idleCallback(); break; ///< Recursive loop entered, a #PuglEventLoopEnter diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index e00f97b7..fafbd0aa 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -136,6 +136,9 @@ struct Window::PrivateData : IdleCallback { void idleCallback() override; + bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs); + bool removeIdleCallback(IdleCallback* callback); + // modal handling void startModal(); void stopModal(); @@ -152,6 +155,7 @@ struct Window::PrivateData : IdleCallback { void onPuglMouse(const Events::MouseEvent& ev); void onPuglMotion(const Events::MotionEvent& ev); void onPuglScroll(const Events::ScrollEvent& ev); + void onPuglTimer(IdleCallback* idleCallback); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); From 4281406e68aeef25c5e8a9dff5542f0e0eee337a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 18:04:55 +0100 Subject: [PATCH 080/159] Cleanup unused function Signed-off-by: falkTX --- dgl/src/WindowPrivateData.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index fafbd0aa..b4fc5cb9 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -155,7 +155,6 @@ struct Window::PrivateData : IdleCallback { void onPuglMouse(const Events::MouseEvent& ev); void onPuglMotion(const Events::MotionEvent& ev); void onPuglScroll(const Events::ScrollEvent& ev); - void onPuglTimer(IdleCallback* idleCallback); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); From 51233543614e25b47361a66bac917a75ee90cafe Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 21:12:26 +0100 Subject: [PATCH 081/159] Fix getGraphicsContext, fix nanovg linkage, cleanup Signed-off-by: falkTX --- dgl/Cairo.hpp | 2 + dgl/Geometry.hpp | 58 +++++++++---- dgl/NanoVG.hpp | 12 +-- dgl/StandaloneWindow.hpp | 4 + dgl/Widget.hpp | 2 +- dgl/Window.hpp | 8 ++ dgl/src/Cairo.cpp | 96 ++++++++++++--------- dgl/src/Geometry.cpp | 120 +++++++++++++++------------ dgl/src/NanoVG.cpp | 60 ++------------ dgl/src/OpenGL.cpp | 103 ++++++++++++++--------- dgl/src/Widget.cpp | 7 ++ dgl/src/Window.cpp | 7 ++ tests/Demo.cpp | 22 +---- tests/Makefile | 6 +- tests/Window.cpp | 2 +- tests/widgets/ExampleColorWidget.hpp | 71 +++++++++------- tests/widgets/ExampleTextWidget.hpp | 49 +++++++---- 17 files changed, 352 insertions(+), 277 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index d0f8014e..f5eb1536 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -105,6 +105,8 @@ protected: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoSubWidget); }; +// -------------------------------------------------------------------------------------------------------------------- + /** Cairo TopLevelWidget, handy class that takes graphics context during onDisplay and passes it in a new function. */ diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index 9b90ae7c..bd654be8 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -195,7 +195,7 @@ public: /** Return true if size is not null (0x0). - A non-null size is still invalid if its width or height is negative. + A non-null size is still invalid if its width or height are negative. */ bool isNotNull() const noexcept; @@ -732,17 +732,38 @@ public: */ bool containsY(const T& y) const noexcept; -#ifndef DPF_TEST_POINT_CPP /** - Draw this rectangle using the current OpenGL state. + Return true if size is null (0x0). + An null size is also invalid. */ - void draw(); + bool isNull() const noexcept; /** - Draw lines (outline of this rectangle) using the current OpenGL state. + Return true if size is not null (0x0). + A non-null size is still invalid if its width or height are negative. */ - void drawOutline(); -#endif + bool isNotNull() const noexcept; + + /** + Return true if size is valid (width and height are higher than zero). + */ + bool isValid() const noexcept; + + /** + Return true if size is invalid (width or height are lower or equal to zero). + An invalid size might not be null under some circumstances. + */ + bool isInvalid() const noexcept; + + /** + Draw this rectangle using the provided graphics context. + */ + void draw(const GraphicsContext& context); + + /** + Draw lines (outline of this rectangle) using the provided graphics context. + */ + void drawOutline(const GraphicsContext& context); Rectangle& operator=(const Rectangle& rect) noexcept; Rectangle& operator*=(double m) noexcept; @@ -750,14 +771,23 @@ public: bool operator==(const Rectangle& size) const noexcept; bool operator!=(const Rectangle& size) const noexcept; -private: - Point fPos; - Size fSize; + /** + Draw this rectangle using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. + */ + // TODO mark deprecated + void draw(); -#ifndef DPF_TEST_POINT_CPP - /** @internal */ - void _draw(const bool outline); -#endif + /** DEPRECATED + Draw lines (outline of this rectangle) using the current OpenGL state. + DEPRECATED please use drawOutline(const GraphicsContext&) instead. + */ + // TODO mark deprecated + void drawOutline(); + +private: + Point pos; + Size size; }; // ----------------------------------------------------------------------- diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 618b5fdb..584a5977 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -903,7 +903,7 @@ public: /** Destructor. */ - virtual ~NanoWidget(); + virtual ~NanoWidget() {} protected: /** @@ -913,14 +913,16 @@ protected: virtual void onNanoDisplay() = 0; private: - struct PrivateData; - PrivateData* const nData; - /** Widget display function. Implemented internally to wrap begin/endFrame() automatically. */ - void onDisplay() override; + inline void onDisplay() override + { + NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); + onNanoDisplay(); + NanoVG::endFrame(); + } // these should not be used void beginFrame(uint,uint) {} diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 67031385..fad3354e 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -57,6 +57,10 @@ public: void setHeight(uint height) { Window::setHeight(height); } void setSize(uint width, uint height) { Window::setSize(width, height); } void setSize(const Size& size) { Window::setSize(size); } + bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) + { return Window::addIdleCallback(callback, timerFrequencyInMs); } + bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } + const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) }; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 5cb69b6a..0db175b1 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -153,7 +153,7 @@ public: Window& getWindow() const noexcept; /** - Get the graphics context associated with this widget. + Get the graphics context associated with this widget's window. GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, for example GraphicsContext. @see CairoSubWidget, CairoTopLevelWidget diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 5defcf6b..f363820a 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -214,6 +214,14 @@ public: */ Application& getApp() const noexcept; + /** + Get the graphics context associated with this window. + GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable, + for example GraphicsContext. + @see CairoSubWidget, CairoTopLevelWidget + */ + const GraphicsContext& getGraphicsContext() const noexcept; + /** Get the "native" window handle. Returned value depends on the platform: diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 79eca970..0a2a9420 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -22,7 +22,7 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -static void notImplemented(const char *name) +static void notImplemented(const char* const name) { d_stderr2("cairo function not implemented: %s", name); } @@ -36,6 +36,13 @@ void Line::draw() notImplemented("Line::draw"); } +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + // ----------------------------------------------------------------------- // Circle @@ -45,6 +52,13 @@ void Circle::_draw(const bool outline) notImplemented("Circle::draw"); } +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + // ----------------------------------------------------------------------- // Triangle @@ -54,15 +68,56 @@ void Triangle::_draw(const bool outline) notImplemented("Triangle::draw"); } +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + + // ----------------------------------------------------------------------- // Rectangle template -void Rectangle::_draw(const bool outline) +static void drawRectangle(const Rectangle& rect, const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); + + // TODO +} + +template +void Rectangle::draw(const GraphicsContext&) +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext&) +{ + drawRectangle(*this, true); +} + +template +void Rectangle::draw() { notImplemented("Rectangle::draw"); } +template +void Rectangle::drawOutline() +{ + notImplemented("Rectangle::drawOutline"); +} + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + // ----------------------------------------------------------------------- // CairoImage @@ -94,6 +149,8 @@ void ImageBaseAboutWindow::onDisplay() img.draw(getGraphicsContext()); } +template class ImageBaseAboutWindow; + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) @@ -122,45 +179,10 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { GraphicsContext& context((GraphicsContext&)graphicsContext); -#ifdef DGL_CAIRO ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); -#endif return context; } -// ----------------------------------------------------------------------- -// Possible template data types - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; - -template class ImageBaseAboutWindow; - // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index 1df6e9b7..e3eda15e 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -734,162 +734,162 @@ bool Triangle::operator!=(const Triangle& tri) const noexcept template Rectangle::Rectangle() noexcept - : fPos(0, 0), - fSize(0, 0) {} + : pos(0, 0), + size(0, 0) {} template Rectangle::Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept - : fPos(x, y), - fSize(width, height) {} + : pos(x, y), + size(width, height) {} template Rectangle::Rectangle(const T& x, const T& y, const Size& size) noexcept - : fPos(x, y), - fSize(size) {} + : pos(x, y), + size(size) {} template Rectangle::Rectangle(const Point& pos, const T& width, const T& height) noexcept - : fPos(pos), - fSize(width, height) {} + : pos(pos), + size(width, height) {} template Rectangle::Rectangle(const Point& pos, const Size& size) noexcept - : fPos(pos), - fSize(size) {} + : pos(pos), + size(size) {} template Rectangle::Rectangle(const Rectangle& rect) noexcept - : fPos(rect.fPos), - fSize(rect.fSize) {} + : pos(rect.pos), + size(rect.size) {} template const T& Rectangle::getX() const noexcept { - return fPos.fX; + return pos.fX; } template const T& Rectangle::getY() const noexcept { - return fPos.fY; + return pos.fY; } template const T& Rectangle::getWidth() const noexcept { - return fSize.fWidth; + return size.fWidth; } template const T& Rectangle::getHeight() const noexcept { - return fSize.fHeight; + return size.fHeight; } template const Point& Rectangle::getPos() const noexcept { - return fPos; + return pos; } template const Size& Rectangle::getSize() const noexcept { - return fSize; + return size; } template void Rectangle::setX(const T& x) noexcept { - fPos.fX = x; + pos.fX = x; } template void Rectangle::setY(const T& y) noexcept { - fPos.fY = y; + pos.fY = y; } template void Rectangle::setPos(const T& x, const T& y) noexcept { - fPos.fX = x; - fPos.fY = y; + pos.fX = x; + pos.fY = y; } template -void Rectangle::setPos(const Point& pos) noexcept +void Rectangle::setPos(const Point& pos2) noexcept { - fPos = pos; + pos = pos2; } template void Rectangle::moveBy(const T& x, const T& y) noexcept { - fPos.moveBy(x, y); + pos.moveBy(x, y); } template -void Rectangle::moveBy(const Point& pos) noexcept +void Rectangle::moveBy(const Point& pos2) noexcept { - fPos.moveBy(pos); + pos.moveBy(pos2); } template void Rectangle::setWidth(const T& width) noexcept { - fSize.fWidth = width; + size.fWidth = width; } template void Rectangle::setHeight(const T& height) noexcept { - fSize.fHeight = height; + size.fHeight = height; } template void Rectangle::setSize(const T& width, const T& height) noexcept { - fSize.fWidth = width; - fSize.fHeight = height; + size.fWidth = width; + size.fHeight = height; } template -void Rectangle::setSize(const Size& size) noexcept +void Rectangle::setSize(const Size& size2) noexcept { - fSize = size; + size = size2; } template void Rectangle::growBy(double multiplier) noexcept { - fSize.growBy(multiplier); + size.growBy(multiplier); } template void Rectangle::shrinkBy(double divider) noexcept { - fSize.shrinkBy(divider); + size.shrinkBy(divider); } template -void Rectangle::setRectangle(const Point& pos, const Size& size) noexcept +void Rectangle::setRectangle(const Point& pos2, const Size& size2) noexcept { - fPos = pos; - fSize = size; + pos = pos2; + size = size2; } template void Rectangle::setRectangle(const Rectangle& rect) noexcept { - fPos = rect.fPos; - fSize = rect.fSize; + pos = rect.pos; + size = rect.size; } template bool Rectangle::contains(const T& x, const T& y) const noexcept { - return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight); + return (x >= pos.fX && y >= pos.fY && x <= pos.fX+size.fWidth && y <= pos.fY+size.fHeight); } template @@ -901,61 +901,71 @@ bool Rectangle::contains(const Point& pos) const noexcept template bool Rectangle::containsX(const T& x) const noexcept { - return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth); + return (x >= pos.fX && x <= pos.fX + size.fWidth); } template bool Rectangle::containsY(const T& y) const noexcept { - return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight); + return (y >= pos.fY && y <= pos.fY + size.fHeight); } -#ifndef DPF_TEST_POINT_CPP template -void Rectangle::draw() +bool Rectangle::isNull() const noexcept { - _draw(false); + return size.isNull(); } template -void Rectangle::drawOutline() +bool Rectangle::isNotNull() const noexcept { - _draw(true); + return size.isNotNull(); +} + +template +bool Rectangle::isValid() const noexcept +{ + return size.isValid(); +} + +template +bool Rectangle::isInvalid() const noexcept +{ + return size.isInvalid(); } -#endif template Rectangle& Rectangle::operator=(const Rectangle& rect) noexcept { - fPos = rect.fPos; - fSize = rect.fSize; + pos = rect.pos; + size = rect.size; return *this; } template Rectangle& Rectangle::operator*=(double m) noexcept { - fSize *= m; + size *= m; return *this; } template Rectangle& Rectangle::operator/=(double d) noexcept { - fSize /= d; + size /= d; return *this; } template bool Rectangle::operator==(const Rectangle& rect) const noexcept { - return (fPos == rect.fPos && fSize == rect.fSize); + return (pos == rect.pos && size == rect.size); } template bool Rectangle::operator!=(const Rectangle& rect) const noexcept { - return (fPos != rect.fPos || fSize != rect.fSize); + return (pos != rect.pos || size != rect.size); } // ----------------------------------------------------------------------- diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 37cb9d35..f777ae76 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -942,37 +942,18 @@ bool NanoVG::loadSharedResources() } #endif -// ----------------------------------------------------------------------- - -template -struct NanoWidget::PrivateData { - NanoWidget* const self; - - PrivateData(NanoWidget* const s) - : self(s) {} - - ~PrivateData() - { - } -}; - // ----------------------------------------------------------------------- // SubWidget template <> NanoWidget::NanoWidget(Widget* const parent, int flags) : SubWidget(parent), - NanoVG(flags), - nData(new PrivateData(this)) + NanoVG(flags) { pData->needsViewportScaling = true; } -template <> -NanoWidget::~NanoWidget() -{ - delete nData; -} +template class NanoWidget; // ----------------------------------------------------------------------- // TopLevelWidget @@ -980,16 +961,11 @@ NanoWidget::~NanoWidget() template <> NanoWidget::NanoWidget(Window& windowToMapTo, int flags) : TopLevelWidget(windowToMapTo), - NanoVG(flags), - nData(new PrivateData(this)) + NanoVG(flags) { } -template <> -NanoWidget::~NanoWidget() -{ - delete nData; -} +template class NanoWidget; // ----------------------------------------------------------------------- // StandaloneWindow @@ -997,35 +973,11 @@ NanoWidget::~NanoWidget() template <> NanoWidget::NanoWidget(Application& app, int flags) : StandaloneWindow(app), - NanoVG(flags), - nData(new PrivateData(this)) + NanoVG(flags) { } -template <> -NanoWidget::~NanoWidget() -{ - delete nData; -} - -// ----------------------------------------------------------------------- - -template -void NanoWidget::onDisplay() -{ - NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); - onNanoDisplay(); - - /* - for (std::vector::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it) - { - NanoWidget* const widget(*it); - widget->onNanoDisplay(); - } - */ - - NanoVG::endFrame(); -} +template class NanoWidget; // ----------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 60924ac2..235f15cf 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -42,6 +42,13 @@ void Line::draw() glEnd(); } +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + // ----------------------------------------------------------------------- // Circle @@ -66,6 +73,13 @@ void Circle::_draw(const bool outline) glEnd(); } +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + // ----------------------------------------------------------------------- // Triangle @@ -85,33 +99,76 @@ void Triangle::_draw(const bool outline) glEnd(); } +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + // ----------------------------------------------------------------------- // Rectangle template -void Rectangle::_draw(const bool outline) +static void drawRectangle(const Rectangle& rect, const bool outline) { - DISTRHO_SAFE_ASSERT_RETURN(fSize.isValid(),); + DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); glBegin(outline ? GL_LINE_LOOP : GL_QUADS); { + const T x = rect.getX(); + const T y = rect.getY(); + const T w = rect.getWidth(); + const T h = rect.getHeight(); + glTexCoord2f(0.0f, 0.0f); - glVertex2d(fPos.fX, fPos.fY); + glVertex2d(x, y); glTexCoord2f(1.0f, 0.0f); - glVertex2d(fPos.fX+fSize.fWidth, fPos.fY); + glVertex2d(x+w, y); glTexCoord2f(1.0f, 1.0f); - glVertex2d(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight); + glVertex2d(x+w, y+h); glTexCoord2f(0.0f, 1.0f); - glVertex2d(fPos.fX, fPos.fY+fSize.fHeight); + glVertex2d(x, y+h); } glEnd(); } +template +void Rectangle::draw(const GraphicsContext&) +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext&) +{ + drawRectangle(*this, true); +} + +template +void Rectangle::draw() +{ + drawRectangle(*this, true); +} + +template +void Rectangle::drawOutline() +{ + drawRectangle(*this, true); +} + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + // ----------------------------------------------------------------------- static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) @@ -378,40 +435,6 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept return (const GraphicsContext&)graphicsContext; } -// ----------------------------------------------------------------------- -// Possible template data types - -#ifndef DPF_TEST_DEMO -// // FIXME -// template class Line; -// template class Line; -// template class Line; -// template class Line; -// template class Line; -// template class Line; -// -// template class Circle; -// template class Circle; -// template class Circle; -// template class Circle; -// template class Circle; -// template class Circle; -// -// template class Triangle; -// template class Triangle; -// template class Triangle; -// template class Triangle; -// template class Triangle; -// template class Triangle; -// -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -#endif - // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index c00e3a97..31095002 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -16,6 +16,7 @@ #include "WidgetPrivateData.hpp" #include "../TopLevelWidget.hpp" +#include "../Window.hpp" START_NAMESPACE_DGL @@ -134,6 +135,12 @@ Window& Widget::getWindow() const noexcept return pData->topLevelWidget->getWindow(); } +const GraphicsContext& Widget::getGraphicsContext() const noexcept +{ + DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); + return pData->topLevelWidget->getWindow().getGraphicsContext(); +} + TopLevelWidget* Widget::getTopLevelWidget() const noexcept { return pData->topLevelWidget; diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 800daab3..66a02ef4 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -172,6 +172,13 @@ Application& Window::getApp() const noexcept return pData->app; } +#ifndef DPF_TEST_WINDOW_CPP +const GraphicsContext& Window::getGraphicsContext() const noexcept +{ + return pData->getGraphicsContext(); +} +#endif + uintptr_t Window::getNativeWindowHandle() const noexcept { return puglGetNativeWindow(pData->view); diff --git a/tests/Demo.cpp b/tests/Demo.cpp index db2564a3..fd441033 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -20,26 +20,8 @@ #include "tests.hpp" -#define DPF_TEST_DEMO -#include "dgl/OpenGL.hpp" -#include "dgl/src/pugl.cpp" -#include "dgl/src/Application.cpp" -#include "dgl/src/ApplicationPrivateData.cpp" -#include "dgl/src/Color.cpp" -#include "dgl/src/Geometry.cpp" -#include "dgl/src/ImageBase.cpp" -#include "dgl/src/NanoVG.cpp" -#include "dgl/src/OpenGL.cpp" -#include "dgl/src/Resources.cpp" -#include "dgl/src/SubWidget.cpp" -#include "dgl/src/SubWidgetPrivateData.cpp" -#include "dgl/src/TopLevelWidget.cpp" -#include "dgl/src/TopLevelWidgetPrivateData.cpp" -#include "dgl/src/Widget.cpp" -#include "dgl/src/WidgetPrivateData.cpp" -#include "dgl/src/Window.cpp" -#include "dgl/src/WindowPrivateData.cpp" -#include "dgl/StandaloneWindow.hpp" +// TODO backend agnostic +#include "../dgl/OpenGL.hpp" #include "widgets/ExampleColorWidget.hpp" #include "widgets/ExampleImagesWidget.hpp" diff --git a/tests/Makefile b/tests/Makefile index cd43a698..2ca0a4e7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,7 +25,7 @@ ifeq ($(HAVE_CAIRO),true) WTESTS = Window.cairo endif ifeq ($(HAVE_OPENGL),true) -# TESTS += Demo +TESTS += Demo WTESTS = Window.opengl endif ifeq ($(HAVE_VULKAN),true) @@ -119,9 +119,9 @@ clean: @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ -../build/tests/Demo: ../build/tests/Demo.cpp.o +../build/tests/Demo: ../build/tests/Demo.cpp.o ../build/libdgl-opengl.a @echo "Linking Demo" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ ../build/tests/Testing: ../build/tests/Testing.cpp.o @echo "Linking Testing" diff --git a/tests/Window.cpp b/tests/Window.cpp index 2c726278..32724cdd 100644 --- a/tests/Window.cpp +++ b/tests/Window.cpp @@ -20,8 +20,8 @@ #include "tests.hpp" -#define DPF_TEST_WINDOW_CPP #define DPF_TEST_POINT_CPP +#define DPF_TEST_WINDOW_CPP #include "dgl/src/pugl.cpp" #include "dgl/src/Application.cpp" #include "dgl/src/ApplicationPrivateData.cpp" diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index a2379aad..eb982884 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -20,8 +20,8 @@ // ------------------------------------------------------ // DGL Stuff +#include "../../dgl/StandaloneWindow.hpp" #include "../../dgl/SubWidget.hpp" -#include "../../dgl/TopLevelWidget.hpp" START_NAMESPACE_DGL @@ -42,37 +42,13 @@ public: static constexpr const char* kExampleWidgetName = "Color"; // SubWidget - explicit ExampleColorWidget(Widget* const parent) - : BaseWidget(parent), - cur('r'), - reverse(false), - r(0), g(0), b(0) - { - BaseWidget::setSize(300, 300); - parent->getApp().addIdleCallback(this); - } + explicit ExampleColorWidget(Widget* const parent); // TopLevelWidget - explicit ExampleColorWidget(Window& windowToMapTo) - : BaseWidget(windowToMapTo), - cur('r'), - reverse(false), - r(0), g(0), b(0) - { - BaseWidget::setSize(300, 300); - windowToMapTo.getApp().addIdleCallback(this); - } + explicit ExampleColorWidget(Window& windowToMapTo); // StandaloneWindow - explicit ExampleColorWidget(Application& app) - : BaseWidget(app), - cur('r'), - reverse(false), - r(0), g(0), b(0) - { - BaseWidget::setSize(300, 300); - app.addIdleCallback(this); - } + explicit ExampleColorWidget(Application& app); protected: void idleCallback() noexcept override @@ -130,9 +106,11 @@ protected: void onDisplay() override { + const GraphicsContext& context(BaseWidget::getGraphicsContext()); + // paint bg color (in full size) glColor3b(r, g, b); - bgFull.draw(); + bgFull.draw(context); // paint inverted color (in 2/3 size) glColor3b(100-r, 100-g, 100-b); @@ -152,6 +130,41 @@ protected: } }; +template<> inline +ExampleColorWidget::ExampleColorWidget(Widget* const parent) + : SubWidget(parent), + cur('r'), + reverse(false), + r(0), g(0), b(0) +{ + setSize(300, 300); + parent->getApp().addIdleCallback(this); +} + +// TopLevelWidget +template<> inline +ExampleColorWidget::ExampleColorWidget(Window& windowToMapTo) + : TopLevelWidget(windowToMapTo), + cur('r'), + reverse(false), + r(0), g(0), b(0) +{ + setSize(300, 300); + addIdleCallback(this); +} + +// StandaloneWindow +template<> inline +ExampleColorWidget::ExampleColorWidget(Application& app) + : StandaloneWindow(app), + cur('r'), + reverse(false), + r(0), g(0), b(0) +{ + setSize(300, 300); + addIdleCallback(this); +} + typedef ExampleColorWidget ExampleColorSubWidget; typedef ExampleColorWidget ExampleColorTopLevelWidget; typedef ExampleColorWidget ExampleColorStandaloneWindow; diff --git a/tests/widgets/ExampleTextWidget.hpp b/tests/widgets/ExampleTextWidget.hpp index c8d4d8e3..2d3b802c 100644 --- a/tests/widgets/ExampleTextWidget.hpp +++ b/tests/widgets/ExampleTextWidget.hpp @@ -34,28 +34,13 @@ public: static constexpr const char* kExampleWidgetName = "Text"; // SubWidget - explicit ExampleTextWidget(Widget* const parent) - : BaseWidget(parent) - { - NanoVG::loadSharedResources(); - BaseWidget::setSize(500, 300); - } + explicit ExampleTextWidget(Widget* const parent); // TopLevelWidget - explicit ExampleTextWidget(Window& windowToMapTo) - : BaseWidget(windowToMapTo) - { - NanoVG::loadSharedResources(); - BaseWidget::setSize(500, 300); - } + explicit ExampleTextWidget(Window& windowToMapTo); // StandaloneWindow - explicit ExampleTextWidget(Application& app) - : BaseWidget(app) - { - NanoVG::loadSharedResources(); - BaseWidget::setSize(500, 300); - } + explicit ExampleTextWidget(Application& app); protected: void onNanoDisplay() override @@ -77,6 +62,34 @@ protected: } }; +template<> inline +ExampleTextWidget::ExampleTextWidget(Widget* const parent) + : NanoSubWidget(parent) +{ + loadSharedResources(); + setSize(500, 300); +} + +template<> inline +ExampleTextWidget::ExampleTextWidget(Window& windowToMapTo) + : NanoTopLevelWidget(windowToMapTo) +{ + loadSharedResources(); + setSize(500, 300); +} + +template<> inline +ExampleTextWidget::ExampleTextWidget(Application& app) + : NanoStandaloneWindow(app) +{ + loadSharedResources(); + setSize(500, 300); +} + +template class ExampleTextWidget; +template class ExampleTextWidget; +template class ExampleTextWidget; + typedef ExampleTextWidget ExampleTextSubWidget; typedef ExampleTextWidget ExampleTextTopLevelWidget; typedef ExampleTextWidget ExampleTextStandaloneWindow; From 077b572d56a1e07c66e7b0e6ee4265703c99cd9e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 23:27:52 +0100 Subject: [PATCH 082/159] Simplify Image class, start of making Demo test Cairo+GL compat Signed-off-by: falkTX --- dgl/Cairo.hpp | 11 ++- dgl/ImageBase.hpp | 33 ++++++- dgl/OpenGL.hpp | 52 ++++------- dgl/src/Cairo.cpp | 47 ++++++++-- dgl/src/ImageBase.cpp | 36 +++++-- dgl/src/OpenGL.cpp | 109 +++++++++------------- dgl/src/pugl.cpp | 1 + tests/Demo.cpp | 84 +++++++++++------ tests/Makefile | 37 ++++---- tests/widgets/ExampleColorWidget.hpp | 2 + tests/widgets/ExampleImagesWidget.hpp | 49 +++++----- tests/widgets/ExampleRectanglesWidget.hpp | 6 ++ tests/widgets/ExampleShapesWidget.hpp | 2 + 13 files changed, 275 insertions(+), 194 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index f5eb1536..9345dbb0 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -55,16 +55,13 @@ public: Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - CairoImage(const char* const rawData, - const uint width, - const uint height); + CairoImage(const char* rawData, uint width, uint height, ImageFormat format); /** Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - CairoImage(const char* const rawData, - const Size& size); + CairoImage(const char* rawData, const Size& size, ImageFormat format); /** Constructor using another image data. @@ -80,6 +77,10 @@ public: Draw this image at position @a pos using the graphics context @a context. */ void drawAt(const GraphicsContext& context, const Point& pos) override; + + // FIXME this should not be needed + inline void drawAt(const GraphicsContext& context, int x, int y) + { drawAt(context, Point(x, y)); }; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp index 8f71aa42..a03c1567 100644 --- a/dgl/ImageBase.hpp +++ b/dgl/ImageBase.hpp @@ -23,6 +23,13 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +enum ImageFormat { + kImageFormatBGR, + kImageFormatBGRA, + kImageFormatRGB, + kImageFormatRGBA, +}; + /** Base DGL Image class. @@ -44,13 +51,13 @@ protected: Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - ImageBase(const char* const rawData, const uint width, const uint height); + ImageBase(const char* rawData, uint width, uint height, ImageFormat format); /** Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - ImageBase(const char* const rawData, const Size& size); + ImageBase(const char* rawData, const Size& size, ImageFormat format); /** Constructor using another image data. @@ -93,6 +100,25 @@ public: */ const char* getRawData() const noexcept; + /** + Get the image format. + */ + ImageFormat getFormat() const noexcept; + + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + void loadFromMemory(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA) noexcept; + + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + virtual void loadFromMemory(const char* rawData, + const Size& size, + ImageFormat format = kImageFormatBGRA) noexcept; + /** Draw this image at (0, 0) point using the current OpenGL context. */ @@ -101,7 +127,7 @@ public: /** Draw this image at (x, y) point using the current OpenGL context. */ - void drawAt(const GraphicsContext& context, const int x, const int y); + void drawAt(const GraphicsContext& context, int x, int y); /** Draw this image at position @a pos using the current OpenGL context. @@ -118,6 +144,7 @@ public: protected: const char* rawData; Size size; + ImageFormat format; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 3f44072e..8dff7f74 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -141,20 +141,13 @@ public: Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - OpenGLImage(const char* const rawData, - const uint width, - const uint height, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE); + OpenGLImage(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA); /** Constructor using raw image data. @note @a rawData must remain valid for the lifetime of this Image. */ - OpenGLImage(const char* const rawData, - const Size& size, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE); + OpenGLImage(const char* rawData, const Size& size, ImageFormat format = kImageFormatBGRA); /** Constructor using another image data. @@ -170,30 +163,9 @@ public: Load image data from memory. @note @a rawData must remain valid for the lifetime of this Image. */ - void loadFromMemory(const char* const rawData, - const uint width, - const uint height, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE) noexcept; - - /** - Load image data from memory. - @note @a rawData must remain valid for the lifetime of this Image. - */ - void loadFromMemory(const char* const rawData, + void loadFromMemory(const char* rawData, const Size& size, - const GLenum format = GL_BGRA, - const GLenum type = GL_UNSIGNED_BYTE) noexcept; - - /** - Get the image format. - */ - GLenum getFormat() const noexcept; - - /** - Get the image type. - */ - GLenum getType() const noexcept; + ImageFormat format = kImageFormatBGRA) noexcept override; /** Draw this image at position @a pos using the graphics context @a context. @@ -223,10 +195,20 @@ public: // TODO mark as deprecated void drawAt(const Point& pos); + // FIXME this should not be needed + inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) + { loadFromMemory(rawData, Size(w, h), format); }; + inline void drawAt(const GraphicsContext& context, int x, int y) + { drawAt(context, Point(x, y)); }; + + /** + Get the image type. + */ + // TODO mark as deprecated + GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } + private: - GLenum fFormat; - GLenum fType; - GLuint fTextureId; + GLuint textureId; bool setupCalled; }; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 0a2a9420..a946b374 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -15,7 +15,10 @@ */ #include "../Cairo.hpp" + #include "SubWidgetPrivateData.hpp" +#include "TopLevelWidgetPrivateData.hpp" +#include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" START_NAMESPACE_DGL @@ -124,14 +127,14 @@ template class Rectangle; CairoImage::CairoImage() : ImageBase() {} -CairoImage::CairoImage(const char* const rawData, const uint width, const uint height) - : ImageBase(rawData, width, height) {} +CairoImage::CairoImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) + : ImageBase(rawData, width, height, format) {} -CairoImage::CairoImage(const char* const rawData, const Size& size) - : ImageBase(rawData, size) {} +CairoImage::CairoImage(const char* const rawData, const Size& size, const ImageFormat format) + : ImageBase(rawData, size, format) {} CairoImage::CairoImage(const CairoImage& image) - : ImageBase(image.rawData, image.size) {} + : ImageBase(image.rawData, image.size, image.format) {} CairoImage::~CairoImage() { @@ -171,7 +174,32 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const cairo_set_matrix(cr, &matrix); -// displaySubWidgets(width, height, autoScaleFactor); + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); +} + +// ----------------------------------------------------------------------- + +void TopLevelWidget::PrivateData::display() +{ + const Size size(window.getSize()); + const uint width = size.getWidth(); + const uint height = size.getHeight(); + + const double autoScaleFactor = window.pData->autoScaleFactor; + +#if 0 + // full viewport size + if (window.pData->autoScaling) + glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); + else + glViewport(0, 0, width, height); +#endif + + // main widget drawing + self->onDisplay(); + + // now draw subwidgets if there are any + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- @@ -186,3 +214,10 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept // ----------------------------------------------------------------------- END_NAMESPACE_DGL + +// ----------------------------------------------------------------------- +// templated classes + +#include "ImageBaseWidgets.cpp" + +// ----------------------------------------------------------------------- diff --git a/dgl/src/ImageBase.cpp b/dgl/src/ImageBase.cpp index 82151cc3..2691c4a7 100644 --- a/dgl/src/ImageBase.cpp +++ b/dgl/src/ImageBase.cpp @@ -23,19 +23,23 @@ START_NAMESPACE_DGL ImageBase::ImageBase() : rawData(nullptr), - size(0, 0) {} + size(0, 0), + format(kImageFormatBGRA) {} -ImageBase::ImageBase(const char* const rdata, const uint width, const uint height) +ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) : rawData(rdata), - size(width, height) {} + size(width, height), + format(fmt) {} -ImageBase::ImageBase(const char* const rdata, const Size& s) +ImageBase::ImageBase(const char* const rdata, const Size& s, const ImageFormat fmt) : rawData(rdata), - size(s) {} + size(s), + format(fmt) {} ImageBase::ImageBase(const ImageBase& image) : rawData(image.rawData), - size(image.size) {} + size(image.size), + format(image.format) {} // -------------------------------------------------------------------------------------------------------------------- // public methods @@ -72,6 +76,26 @@ const char* ImageBase::getRawData() const noexcept return rawData; } +ImageFormat ImageBase::getFormat() const noexcept +{ + return format; +} + +void ImageBase::loadFromMemory(const char* const rawData, + const uint width, + const uint height, + const ImageFormat format) noexcept +{ + loadFromMemory(rawData, Size(width, height), format); +} + +void ImageBase::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept +{ + rawData = rdata; + size = s; + format = fmt; +} + void ImageBase::draw(const GraphicsContext& context) { drawAt(context, Point(0, 0)); diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 235f15cf..acf6aa27 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -171,6 +171,23 @@ template class Rectangle; // ----------------------------------------------------------------------- +static GLenum asOpenGLImageFormat(const ImageFormat format) +{ + switch (format) + { + case kImageFormatBGR: + return GL_BGR; + case kImageFormatBGRA: + return GL_BGRA; + case kImageFormatRGB: + return GL_RGB; + case kImageFormatRGBA: + return GL_RGBA; + } + + return GL_BGRA; +} + static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) { DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); @@ -192,7 +209,7 @@ static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) static_cast(image.getWidth()), static_cast(image.getHeight()), 0, - image.getFormat(), image.getType(), image.getRawData()); + asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData()); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); @@ -200,85 +217,52 @@ static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) OpenGLImage::OpenGLImage() : ImageBase(), - fFormat(0), - fType(0), - fTextureId(0), + textureId(0), setupCalled(false) { - glGenTextures(1, &fTextureId); - DISTRHO_SAFE_ASSERT(fTextureId != 0); + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); } -OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type) - : ImageBase(rawData, width, height), - fFormat(format), - fType(type), - fTextureId(0), +OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) + : ImageBase(rawData, width, height, format), + textureId(0), setupCalled(false) { - glGenTextures(1, &fTextureId); - DISTRHO_SAFE_ASSERT(fTextureId != 0); + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); } -OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const GLenum format, const GLenum type) - : ImageBase(rawData, size), - fFormat(format), - fType(type), - fTextureId(0), +OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const ImageFormat format) + : ImageBase(rawData, size, format), + textureId(0), setupCalled(false) { - glGenTextures(1, &fTextureId); - DISTRHO_SAFE_ASSERT(fTextureId != 0); + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::OpenGLImage(const OpenGLImage& image) : ImageBase(image), - fFormat(image.fFormat), - fType(image.fType), - fTextureId(0), + textureId(0), setupCalled(false) { - glGenTextures(1, &fTextureId); - DISTRHO_SAFE_ASSERT(fTextureId != 0); + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::~OpenGLImage() { - if (fTextureId != 0) - glDeleteTextures(1, &fTextureId); + if (textureId != 0) + glDeleteTextures(1, &textureId); } -void OpenGLImage::loadFromMemory(const char* const rawData, - const uint width, - const uint height, - const GLenum format, - const GLenum type) noexcept +void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept { - loadFromMemory(rawData, Size(width, height), format, type); -} - -void OpenGLImage::loadFromMemory(const char* const rdata, - const Size& s, - const GLenum format, - const GLenum type) noexcept -{ - rawData = rdata; - size = s; - fFormat = format; - fType = type; + ImageBase::loadFromMemory(rdata, s, fmt); setupCalled = false; } -GLenum OpenGLImage::getFormat() const noexcept -{ - return fFormat; -} - -GLenum OpenGLImage::getType() const noexcept -{ - return fType; -} - void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) { drawAt(pos); @@ -288,8 +272,7 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept { rawData = image.rawData; size = image.size; - fFormat = image.fFormat; - fType = image.fType; + format = image.format; setupCalled = false; return *this; } @@ -306,17 +289,17 @@ void OpenGLImage::drawAt(const int x, const int y) void OpenGLImage::drawAt(const Point& pos) { - if (fTextureId == 0 || isInvalid()) + if (textureId == 0 || isInvalid()) return; if (! setupCalled) { - setupOpenGLImage(*this, fTextureId); + setupOpenGLImage(*this, textureId); setupCalled = true; } glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fTextureId); + glBindTexture(GL_TEXTURE_2D, textureId); glBegin(GL_QUADS); @@ -353,6 +336,8 @@ void ImageBaseAboutWindow::onDisplay() img.draw(); } +template class ImageBaseAboutWindow; + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) @@ -444,10 +429,4 @@ END_NAMESPACE_DGL #include "ImageBaseWidgets.cpp" -START_NAMESPACE_DGL - -template class ImageBaseAboutWindow; - -END_NAMESPACE_DGL - // ----------------------------------------------------------------------- diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 49bb29ca..df706041 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -72,6 +72,7 @@ START_NAMESPACE_DGL #elif defined(DISTRHO_OS_WINDOWS) #else # include "pugl-upstream/src/x11.c" +# include "pugl-upstream/src/x11_stub.c" # ifdef DGL_CAIRO # include "pugl-upstream/src/x11_cairo.c" # endif diff --git a/tests/Demo.cpp b/tests/Demo.cpp index fd441033..d54154e2 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -14,33 +14,49 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef DGL_OPENGL -#error OpenGL build required for Demo -#endif +// #ifndef DGL_OPENGL +// #error OpenGL build required for Demo +// #endif #include "tests.hpp" -// TODO backend agnostic -#include "../dgl/OpenGL.hpp" - #include "widgets/ExampleColorWidget.hpp" #include "widgets/ExampleImagesWidget.hpp" #include "widgets/ExampleRectanglesWidget.hpp" -#include "widgets/ExampleTextWidget.hpp" #include "widgets/ExampleShapesWidget.hpp" +#ifdef DGL_OPENGL +#include "widgets/ExampleTextWidget.hpp" +#endif #include "demo_res/DemoArtwork.cpp" #include "images_res/CatPics.cpp" +#ifdef DGL_CAIRO +#include "../dgl/Cairo.hpp" +typedef DGL_NAMESPACE::CairoImage DemoImage; +#endif +#ifdef DGL_OPENGL +#include "../dgl/OpenGL.hpp" +typedef DGL_NAMESPACE::OpenGLImage DemoImage; +#endif + START_NAMESPACE_DGL +typedef ExampleImagesWidget ExampleImagesSubWidget; +typedef ExampleImagesWidget ExampleImagesTopLevelWidget; +typedef ExampleImagesWidget ExampleImagesStandaloneWindow; + // -------------------------------------------------------------------------------------------------------------------- // Left side tab-like widget class LeftSideWidget : public SubWidget { public: +#ifdef DGL_OPENGL static const int kPageCount = 5; +#else + static const int kPageCount = 4; +#endif class Callback { @@ -55,22 +71,26 @@ public: curPage(0), curHover(-1) { +#ifdef DGL_OPENGL // for text nvg.loadSharedResources(); +#endif using namespace DemoArtwork; - img1.loadFromMemory(ico1Data, ico1Width, ico1Height, GL_BGR); - img2.loadFromMemory(ico2Data, ico2Width, ico2Height, GL_BGR); - img3.loadFromMemory(ico3Data, ico3Width, ico2Height, GL_BGR); - img4.loadFromMemory(ico4Data, ico4Width, ico4Height, GL_BGR); - img5.loadFromMemory(ico5Data, ico5Width, ico5Height, GL_BGR); + img1.loadFromMemory(ico1Data, ico1Width, ico1Height, kImageFormatBGR); + img2.loadFromMemory(ico2Data, ico2Width, ico2Height, kImageFormatBGR); + img3.loadFromMemory(ico3Data, ico3Width, ico2Height, kImageFormatBGR); + img4.loadFromMemory(ico4Data, ico4Width, ico4Height, kImageFormatBGR); + img5.loadFromMemory(ico5Data, ico5Width, ico5Height, kImageFormatBGR); } protected: void onDisplay() override { + const GraphicsContext& context(getGraphicsContext()); const int iconSize = bgIcon.getWidth(); +#if 0 /* TODO make generic */ glColor3f(0.027f, 0.027f, 0.027f); Rectangle(0, 0, getSize()).draw(); @@ -99,15 +119,17 @@ protected: // reset color glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +#endif const int pad = iconSize/2 - DemoArtwork::ico1Width/2; - img1.drawAt(pad, pad); - img2.drawAt(pad, pad + 3 + iconSize); - img3.drawAt(pad, pad + 6 + iconSize*2); - img4.drawAt(pad, pad + 9 + iconSize*3); - img5.drawAt(pad, pad + 12 + iconSize*4); + img1.drawAt(context, pad, pad); + img2.drawAt(context, pad, pad + 3 + iconSize); + img3.drawAt(context, pad, pad + 6 + iconSize*2); + img4.drawAt(context, pad, pad + 9 + iconSize*3); + img5.drawAt(context, pad, pad + 12 + iconSize*4); +#ifdef DGL_OPENGL // draw some text nvg.beginFrame(this); @@ -120,6 +142,7 @@ protected: nvg.textBox(15, 440, iconSize, "Look!", nullptr); nvg.endFrame(); +#endif } bool onMouse(const MouseEvent& ev) override @@ -203,10 +226,12 @@ private: int curPage, curHover; Rectangle bgIcon; Line lineSep; - OpenGLImage img1, img2, img3, img4, img5; + DemoImage img1, img2, img3, img4, img5; +#ifdef DGL_OPENGL // for text NanoVG nvg; +#endif }; // -------------------------------------------------------------------------------------------------------------------- @@ -226,7 +251,9 @@ public: wImages(this), wRects(this), wShapes(this), +#ifdef DGL_OPENGL wText(this), +#endif wLeft(this, this), curWidget(nullptr) { @@ -234,16 +261,18 @@ public: wImages.hide(); wRects.hide(); wShapes.hide(); +#ifdef DGL_OPENGL wText.hide(); -// //wPerf.hide(); +#endif wColor.setAbsoluteX(kSidebarWidth); wImages.setAbsoluteX(kSidebarWidth); wRects.setAbsoluteX(kSidebarWidth); wShapes.setAbsoluteX(kSidebarWidth); +#ifdef DGL_OPENGL wText.setAbsoluteX(kSidebarWidth); +#endif wLeft.setAbsolutePos(2, 2); -// wPerf.setAbsoluteY(5); setSize(600, 500); setTitle("DGL Demo"); @@ -271,9 +300,11 @@ protected: case 3: curWidget = &wShapes; break; +#ifdef DGL_OPENGL case 4: curWidget = &wText; break; +#endif default: curWidget = nullptr; break; @@ -299,13 +330,10 @@ protected: wImages.setSize(size); wRects.setSize(size); wShapes.setSize(size); +#ifdef DGL_OPENGL wText.setSize(size); - +#endif wLeft.setSize(kSidebarWidth-4, height-4); - //wRezHandle.setAbsoluteX(width-wRezHandle.getWidth()); - //wRezHandle.setAbsoluteY(height-wRezHandle.getHeight()); - -// wPerf.setAbsoluteX(width-wPerf.getWidth()-5); } private: @@ -313,10 +341,10 @@ private: ExampleImagesSubWidget wImages; ExampleRectanglesSubWidget wRects; ExampleShapesSubWidget wShapes; +#ifdef DGL_OPENGL ExampleTextSubWidget wText; +#endif LeftSideWidget wLeft; - //ResizeHandle wRezHandle; -// NanoPerfWidget wPerf; Widget* curWidget; }; @@ -357,8 +385,10 @@ int main(int argc, char* argv[]) createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "shapes") == 0) createAndShowExampleWidgetStandaloneWindow(app); +#ifdef DGL_OPENGL else if (std::strcmp(argv[1], "text") == 0) createAndShowExampleWidgetStandaloneWindow(app); +#endif else d_stderr2("Invalid demo mode, must be one of: color, rectangles, shapes"); } diff --git a/tests/Makefile b/tests/Makefile index 2ca0a4e7..4500f406 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -22,11 +22,12 @@ BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra TESTS = Application Color Point ifeq ($(HAVE_CAIRO),true) -WTESTS = Window.cairo +TESTS += Demo.cairo +WTESTS += Window.cairo endif ifeq ($(HAVE_OPENGL),true) -TESTS += Demo -WTESTS = Window.opengl +TESTS += Demo.opengl +WTESTS += Window.opengl endif ifeq ($(HAVE_VULKAN),true) WTESTS = Window.vulkan @@ -88,16 +89,6 @@ clean: @echo "Compiling $< (Cairo)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -../build/tests/Demo.cpp.o: Demo.cpp - -@mkdir -p ../build/tests - @echo "Compiling $< (OpenGL)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ - -../build/tests/Testing.cpp.o: Testing.cpp - -@mkdir -p ../build/tests - @echo "Compiling $< (OpenGL)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ - ../build/tests/%.cpp.opengl.o: %.cpp -@mkdir -p ../build/tests @echo "Compiling $< (OpenGL)" @@ -119,14 +110,6 @@ clean: @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ -../build/tests/Demo: ../build/tests/Demo.cpp.o ../build/libdgl-opengl.a - @echo "Linking Demo" - $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ - -../build/tests/Testing: ../build/tests/Testing.cpp.o - @echo "Linking Testing" - $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ - ../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ @@ -135,6 +118,18 @@ clean: @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ +../build/tests/Demo.cairo: ../build/tests/Demo.cpp.cairo.o ../build/libdgl-cairo.a + @echo "Linking Demo (Cairo)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ + +../build/tests/Demo.opengl: ../build/tests/Demo.cpp.opengl.o ../build/libdgl-opengl.a + @echo "Linking Demo (OpenGL)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + +../build/tests/Demo.vulkan: ../build/tests/Demo.cpp.vulkan.o ../build/libdgl-vulkan.a + @echo "Linking Demo (OpenGL)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ + # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS:%.o=%.d) diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index eb982884..4d55a808 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -108,6 +108,7 @@ protected: { const GraphicsContext& context(BaseWidget::getGraphicsContext()); +#if 0 /* TODO make generic */ // paint bg color (in full size) glColor3b(r, g, b); bgFull.draw(context); @@ -115,6 +116,7 @@ protected: // paint inverted color (in 2/3 size) glColor3b(100-r, 100-g, 100-b); bgSmall.draw(); +#endif } void onResize(const ResizeEvent& ev) override diff --git a/tests/widgets/ExampleImagesWidget.hpp b/tests/widgets/ExampleImagesWidget.hpp index 9f33e801..1fcb24d2 100644 --- a/tests/widgets/ExampleImagesWidget.hpp +++ b/tests/widgets/ExampleImagesWidget.hpp @@ -20,11 +20,10 @@ // ------------------------------------------------------ // DGL Stuff -#include "../../dgl/Image.hpp" +#include "../../dgl/ImageBase.hpp" #include "../../dgl/SubWidget.hpp" #include "../../dgl/TopLevelWidget.hpp" - // ------------------------------------------------------ // Images @@ -35,7 +34,7 @@ START_NAMESPACE_DGL // ------------------------------------------------------ // our widget -template +template class ExampleImagesWidget : public BaseWidget, public IdleCallback { @@ -50,7 +49,7 @@ class ExampleImagesWidget : public BaseWidget, int imgTop1st, imgTop2nd, imgTop3rd; int img1x, img2x, img3y; bool img1rev, img2rev, img3rev; - Image img1, img2, img3; + BaseImage img1, img2, img3; public: static constexpr const char* kExampleWidgetName = "Images"; @@ -67,9 +66,9 @@ public: img1rev(false), img2rev(true), img3rev(true), - img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), - img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), - img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, kImageFormatBGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, kImageFormatBGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, kImageFormatBGR) { BaseWidget::setSize(500, 400); @@ -88,9 +87,9 @@ public: img1rev(false), img2rev(true), img3rev(true), - img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), - img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), - img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, kImageFormatBGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, kImageFormatBGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, kImageFormatBGR) { BaseWidget::setSize(500, 400); @@ -109,9 +108,9 @@ public: img1rev(false), img2rev(true), img3rev(true), - img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, GL_BGR), - img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, GL_BGR), - img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, GL_BGR) + img1(CatPics::cat1Data, CatPics::cat1Width, CatPics::cat1Height, kImageFormatBGR), + img2(CatPics::cat2Data, CatPics::cat2Width, CatPics::cat2Height, kImageFormatBGR), + img3(CatPics::cat3Data, CatPics::cat3Width, CatPics::cat3Height, kImageFormatBGR) { BaseWidget::setSize(500, 400); @@ -183,42 +182,44 @@ protected: void onDisplay() override { + const GraphicsContext& context(BaseWidget::getGraphicsContext()); + switch (imgTop3rd) { case 1: - img1.drawAt(img1x, kImg1y); + img1.drawAt(context, img1x, kImg1y); break; case 2: - img2.drawAt(img2x, kImg2y); + img2.drawAt(context, img2x, kImg2y); break; case 3: - img3.drawAt(kImg3x, img3y); + img3.drawAt(context, kImg3x, img3y); break; }; switch (imgTop2nd) { case 1: - img1.drawAt(img1x, kImg1y); + img1.drawAt(context, img1x, kImg1y); break; case 2: - img2.drawAt(img2x, kImg2y); + img2.drawAt(context, img2x, kImg2y); break; case 3: - img3.drawAt(kImg3x, img3y); + img3.drawAt(context, kImg3x, img3y); break; }; switch (imgTop1st) { case 1: - img1.drawAt(img1x, kImg1y); + img1.drawAt(context, img1x, kImg1y); break; case 2: - img2.drawAt(img2x, kImg2y); + img2.drawAt(context, img2x, kImg2y); break; case 3: - img3.drawAt(kImg3x, img3y); + img3.drawAt(context, kImg3x, img3y); break; }; } @@ -242,10 +243,6 @@ private: } }; -typedef ExampleImagesWidget ExampleImagesSubWidget; -typedef ExampleImagesWidget ExampleImagesTopLevelWidget; -typedef ExampleImagesWidget ExampleImagesStandaloneWindow; - // ------------------------------------------------------ END_NAMESPACE_DGL diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index 425dade2..72ad1e99 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -81,30 +81,36 @@ protected: // 1st r.setY(3); +#if 0 /* TODO make generic */ if (clicked[0+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); +#endif r.draw(); // 2nd r.setY(3 + height/3); +#if 0 /* TODO make generic */ if (clicked[3+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); +#endif r.draw(); // 3rd r.setY(3 + height*2/3); +#if 0 /* TODO make generic */ if (clicked[6+i]) glColor3f(0.8f, 0.5f, 0.3f); else glColor3f(0.3f, 0.5f, 0.8f); +#endif r.draw(); } diff --git a/tests/widgets/ExampleShapesWidget.hpp b/tests/widgets/ExampleShapesWidget.hpp index c0298e07..f4f9be6f 100644 --- a/tests/widgets/ExampleShapesWidget.hpp +++ b/tests/widgets/ExampleShapesWidget.hpp @@ -69,6 +69,7 @@ protected: glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); #endif +#if 0 /* TODO make generic */ glLineWidth(1.0f); glColor3f(0.302f, 0.337f, 0.361f); bg.draw(); @@ -92,6 +93,7 @@ protected: glLineWidth(2.0f); glColor3f(0.176f/4, 0.212f/4, 0.235f/4); cir.drawOutline(); +#endif } void onResize(const ResizeEvent& ev) override From f55f3c85ae9c543f3c767559fdedd075f0840340 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 00:15:28 +0100 Subject: [PATCH 083/159] Make CairoWidget a templated class, just like done with NanoVG --- dgl/Cairo.hpp | 72 ++++++++++++++++++++++++---------------- dgl/NanoVG.hpp | 8 ++++- dgl/StandaloneWindow.hpp | 4 +-- dgl/src/Cairo.cpp | 31 +++++++++++++++++ dgl/src/NanoVG.cpp | 19 ++++++----- 5 files changed, 94 insertions(+), 40 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 9345dbb0..dbf7f338 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -20,7 +20,6 @@ #include "ImageBase.hpp" #include "ImageBaseWidgets.hpp" #include "SubWidget.hpp" -#include "TopLevelWidget.hpp" #include @@ -86,49 +85,66 @@ public: // -------------------------------------------------------------------------------------------------------------------- /** - Cairo SubWidget, handy class that takes graphics context during onDisplay and passes it in a new function. + CairoWidget, handy class that takes graphics context during onDisplay and passes it in a new function. */ -class CairoSubWidget : public SubWidget +template +class CairoWidget : public BaseWidget { public: - CairoSubWidget(Widget* widgetToGroupTo) - : SubWidget(widgetToGroupTo) {} - -protected: - void onDisplay() override - { - const CairoGraphicsContext& context((const CairoGraphicsContext&)getGraphicsContext()); - onCairoDisplay(context); - } + /** + Constructor for a CairoSubWidget. + @see CreateFlags + */ + explicit CairoWidget(Widget* const parentGroupWidget); - virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; + /** + Constructor for a CairoTopLevelWidget. + @see CreateFlags + */ + explicit CairoWidget(Window& windowToMapTo); - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoSubWidget); -}; + /** + Constructor for a CairoStandaloneWindow without parent window. + @see CreateFlags + */ + explicit CairoWidget(Application& app); -// -------------------------------------------------------------------------------------------------------------------- + /** + Constructor for a CairoStandaloneWindow with parent window. + @see CreateFlags + */ + explicit CairoWidget(Application& app, Window& parentWindow); -/** - Cairo TopLevelWidget, handy class that takes graphics context during onDisplay and passes it in a new function. - */ -class CairoTopLevelWidget : public TopLevelWidget -{ -public: - CairoTopLevelWidget(Window& windowToMapTo) - : TopLevelWidget(windowToMapTo) {} + /** + Destructor. + */ + virtual ~CairoWidget() {} protected: + /** + New virtual onDisplay function. + @see onDisplay + */ + virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; + +private: + /** + Widget display function. + Implemented internally to wrap begin/endFrame() automatically. + */ void onDisplay() override { - const CairoGraphicsContext& context((const CairoGraphicsContext&)getGraphicsContext()); + const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext()); onCairoDisplay(context); } - virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoTopLevelWidget); + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoWidget); }; +typedef CairoWidget CairoSubWidget; +typedef CairoWidget CairoTopLevelWidget; +typedef CairoWidget CairoStandaloneWindow; + // -------------------------------------------------------------------------------------------------------------------- typedef ImageBaseAboutWindow CairoImageAboutWindow; diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 584a5977..1edf16fe 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -895,11 +895,17 @@ public: explicit NanoWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); /** - Constructor for a NanoStandaloneWindow. + Constructor for a NanoStandaloneWindow without parent window. @see CreateFlags */ explicit NanoWidget(Application& app, int flags = CREATE_ANTIALIAS); + /** + Constructor for a NanoStandaloneWindow with parent window. + @see CreateFlags + */ + explicit NanoWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS); + /** Destructor. */ diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index fad3354e..148626fa 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -38,8 +38,8 @@ public: /** Constructor with parent window, typically used to run as modal. */ - StandaloneWindow(Application& app, Window& parent) - : Window(app, parent), + StandaloneWindow(Application& app, Window& parentWindow) + : Window(app, parentWindow), TopLevelWidget((Window&)*this) {} /** diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index a946b374..9843ae7f 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -144,6 +144,37 @@ void CairoImage::drawAt(const GraphicsContext&, const Point&) { } +// ----------------------------------------------------------------------- +// CairoSubWidget + +template <> +CairoWidget::CairoWidget(Widget* const parent) + : SubWidget(parent) {} + +template class CairoWidget; + +// ----------------------------------------------------------------------- +// CairoTopLevelWidget + +template <> +CairoWidget::CairoWidget(Window& windowToMapTo) + : TopLevelWidget(windowToMapTo) {} + +template class CairoWidget; + +// ----------------------------------------------------------------------- +// CairoStandaloneWindow + +template <> +CairoWidget::CairoWidget(Application& app) + : StandaloneWindow(app) {} + +template <> +CairoWidget::CairoWidget(Application& app, Window& parentWindow) + : StandaloneWindow(app, parentWindow) {} + +template class CairoWidget; + // ----------------------------------------------------------------------- template <> diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index f777ae76..70f6e503 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -943,7 +943,7 @@ bool NanoVG::loadSharedResources() #endif // ----------------------------------------------------------------------- -// SubWidget +// NanoSubWidget template <> NanoWidget::NanoWidget(Widget* const parent, int flags) @@ -956,26 +956,27 @@ NanoWidget::NanoWidget(Widget* const parent, int flags) template class NanoWidget; // ----------------------------------------------------------------------- -// TopLevelWidget +// NanoTopLevelWidget template <> NanoWidget::NanoWidget(Window& windowToMapTo, int flags) : TopLevelWidget(windowToMapTo), - NanoVG(flags) -{ -} + NanoVG(flags) {} template class NanoWidget; // ----------------------------------------------------------------------- -// StandaloneWindow +// NanoStandaloneWindow template <> NanoWidget::NanoWidget(Application& app, int flags) : StandaloneWindow(app), - NanoVG(flags) -{ -} + NanoVG(flags) {} + +template <> +NanoWidget::NanoWidget(Application& app, Window& parentWindow, int flags) + : StandaloneWindow(app, parentWindow), + NanoVG(flags) {} template class NanoWidget; From ba4b903cbf5c5424716236333914666725d96b6f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 02:06:22 +0100 Subject: [PATCH 084/159] Rename core Geometry vars, start proper graphics context use --- dgl/Cairo.hpp | 2 +- dgl/Color.hpp | 5 + dgl/Geometry.hpp | 85 +++++--- dgl/src/Cairo.cpp | 147 ++++++++++++- dgl/src/Geometry.cpp | 244 ++++++++++------------ dgl/src/OpenGL.cpp | 136 ++++++++++-- tests/Demo.cpp | 36 ++-- tests/widgets/ExampleColorWidget.hpp | 10 +- tests/widgets/ExampleRectanglesWidget.hpp | 21 +- tests/widgets/ExampleShapesWidget.hpp | 57 ++--- 10 files changed, 475 insertions(+), 268 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index dbf7f338..fb4f11ba 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -130,7 +130,7 @@ protected: private: /** Widget display function. - Implemented internally to wrap begin/endFrame() automatically. + Implemented internally to pass context into the drawing function. */ void onDisplay() override { diff --git a/dgl/Color.hpp b/dgl/Color.hpp index 6276b0d9..42094950 100644 --- a/dgl/Color.hpp +++ b/dgl/Color.hpp @@ -95,6 +95,11 @@ struct Color { */ void fixBounds() noexcept; + /** + Set this color for use in the next drawing operation for the provided context. + */ + void setFor(const GraphicsContext& context, bool includeAlpha = false); + /** @internal Needed for NanoVG compatibility. diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index bd654be8..cc48904c 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -114,7 +114,7 @@ public: bool operator!=(const Point& pos) const noexcept; private: - T fX, fY; + T x, y; template friend class Line; template friend class Circle; template friend class Triangle; @@ -348,13 +348,6 @@ public: */ void moveBy(const Point& pos) noexcept; -#ifndef DPF_TEST_POINT_CPP - /** - Draw this line using the current OpenGL state. - */ - void draw(); -#endif - /** Return true if line is null (start and end pos are equal). */ @@ -365,12 +358,28 @@ public: */ bool isNotNull() const noexcept; +#ifndef DPF_TEST_POINT_CPP + /** + Draw this line using the provided graphics context, optionally specifying line width. + */ + void draw(const GraphicsContext& context, T width = 1); +#endif + Line& operator=(const Line& line) noexcept; bool operator==(const Line& line) const noexcept; bool operator!=(const Line& line) const noexcept; +#ifndef DPF_TEST_POINT_CPP + /** + Draw this line using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. + */ + // TODO mark deprecated + void draw(); +#endif + private: - Point fPosStart, fPosEnd; + Point posStart, posEnd; }; // ----------------------------------------------------------------------- @@ -464,22 +473,36 @@ public: */ void setNumSegments(const uint num); + /** + Draw this circle using the provided graphics context. + */ + void draw(const GraphicsContext& context); + + /** + Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width. + */ + void drawOutline(const GraphicsContext& context, T lineWidth = 1); + + Circle& operator=(const Circle& cir) noexcept; + bool operator==(const Circle& cir) const noexcept; + bool operator!=(const Circle& cir) const noexcept; + #ifndef DPF_TEST_POINT_CPP /** Draw this circle using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. */ + // TODO mark deprecated void draw(); /** Draw lines (outline of this circle) using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. */ + // TODO mark deprecated void drawOutline(); #endif - Circle& operator=(const Circle& cir) noexcept; - bool operator==(const Circle& cir) const noexcept; - bool operator!=(const Circle& cir) const noexcept; - private: Point fPos; float fSize; @@ -487,9 +510,6 @@ private: // cached values float fTheta, fCos, fSin; - - /** @internal */ - void _draw(const bool outline); }; // ----------------------------------------------------------------------- @@ -546,29 +566,38 @@ public: */ bool isInvalid() const noexcept; + /** + Draw this triangle using the provided graphics context. + */ + void draw(const GraphicsContext& context); + + /** + Draw lines (outline of this triangle) using the provided graphics context, optionally specifying line width. + */ + void drawOutline(const GraphicsContext& context, T lineWidth = 1); + + Triangle& operator=(const Triangle& tri) noexcept; + bool operator==(const Triangle& tri) const noexcept; + bool operator!=(const Triangle& tri) const noexcept; + #ifndef DPF_TEST_POINT_CPP /** Draw this triangle using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. */ + // TODO mark deprecated void draw(); /** Draw lines (outline of this triangle) using the current OpenGL state. + DEPRECATED please use draw(const GraphicsContext&) instead. */ + // TODO mark deprecated void drawOutline(); #endif - Triangle& operator=(const Triangle& tri) noexcept; - bool operator==(const Triangle& tri) const noexcept; - bool operator!=(const Triangle& tri) const noexcept; - private: - Point fPos1, fPos2, fPos3; - -#ifndef DPF_TEST_POINT_CPP - /** @internal */ - void _draw(const bool outline); -#endif + Point pos1, pos2, pos3; }; // ----------------------------------------------------------------------- @@ -761,9 +790,9 @@ public: void draw(const GraphicsContext& context); /** - Draw lines (outline of this rectangle) using the provided graphics context. + Draw lines (outline of this rectangle) using the provided graphics context, optionally specifying line width. */ - void drawOutline(const GraphicsContext& context); + void drawOutline(const GraphicsContext& context, T lineWidth = 1); Rectangle& operator=(const Rectangle& rect) noexcept; Rectangle& operator*=(double m) noexcept; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 9843ae7f..bd60fe40 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -15,6 +15,7 @@ */ #include "../Cairo.hpp" +#include "../Color.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" @@ -30,9 +31,36 @@ static void notImplemented(const char* const name) d_stderr2("cairo function not implemented: %s", name); } +// ----------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext& context, const bool includeAlpha) +{ + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + if (includeAlpha) + cairo_set_source_rgba(handle, red, green, blue, alpha); + else + cairo_set_source_rgb(handle, red, green, blue); +} + // ----------------------------------------------------------------------- // Line +template +void Line::draw(const GraphicsContext& context, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + cairo_set_line_width(handle, width); + cairo_move_to(handle, posStart.getX(), posStart.getY()); + cairo_line_to(handle, posEnd.getX(), posEnd.getY()); + cairo_stroke(handle); +} + template void Line::draw() { @@ -50,11 +78,67 @@ template class Line; // Circle template -void Circle::_draw(const bool outline) +static void drawCircle(cairo_t* const handle, + const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); + + const T origx = pos.getX(); + const T origy = pos.getY(); + double t, x = size, y = 0.0; + + /* + glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); + + for (uint i=0; i +void Circle::draw(const GraphicsContext& context) +{ + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + drawCircle(handle, fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + cairo_set_line_width(handle, lineWidth); + drawCircle(handle, fPos, fNumSegments, fSize, fSin, fCos, true); +} + +template +void Circle::draw() { notImplemented("Circle::draw"); } +template +void Circle::drawOutline() +{ + notImplemented("Circle::drawOutline"); +} + template class Circle; template class Circle; template class Circle; @@ -66,11 +150,48 @@ template class Circle; // Triangle template -void Triangle::_draw(const bool outline) +static void drawTriangle(cairo_t* const handle, + const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); + + // TODO +} + +template +void Triangle::draw(const GraphicsContext& context) +{ + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + drawTriangle(handle, pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + cairo_set_line_width(handle, lineWidth); + drawTriangle(handle, pos1, pos2, pos3, true); +} + +template +void Triangle::draw() { notImplemented("Triangle::draw"); } +template +void Triangle::drawOutline() +{ + notImplemented("Triangle::drawOutline"); +} + template class Triangle; template class Triangle; template class Triangle; @@ -83,23 +204,31 @@ template class Triangle; // Rectangle template -static void drawRectangle(const Rectangle& rect, const bool outline) +static void drawRectangle(cairo_t* const handle, const Rectangle& rect, const bool outline) { - DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); - // TODO } template -void Rectangle::draw(const GraphicsContext&) +void Rectangle::draw(const GraphicsContext& context) { - drawRectangle(*this, false); + DISTRHO_SAFE_ASSERT_RETURN(isValid(),); + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + drawRectangle(handle, *this, false); } template -void Rectangle::drawOutline(const GraphicsContext&) +void Rectangle::drawOutline(const GraphicsContext& context, const T lineWidth) { - drawRectangle(*this, true); + DISTRHO_SAFE_ASSERT_RETURN(isValid(),); + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + cairo_set_line_width(handle, lineWidth); + drawRectangle(handle, *this, true); } template diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index e3eda15e..4ad25db4 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -27,129 +27,129 @@ static const float M_2PIf = 3.14159265358979323846f*2.0f; template Point::Point() noexcept - : fX(0), - fY(0) {} + : x(0), + y(0) {} template -Point::Point(const T& x, const T& y) noexcept - : fX(x), - fY(y) {} +Point::Point(const T& x2, const T& y2) noexcept + : x(x2), + y(y2) {} template Point::Point(const Point& pos) noexcept - : fX(pos.fX), - fY(pos.fY) {} + : x(pos.x), + y(pos.y) {} template const T& Point::getX() const noexcept { - return fX; + return x; } template const T& Point::getY() const noexcept { - return fY; + return y; } template -void Point::setX(const T& x) noexcept +void Point::setX(const T& x2) noexcept { - fX = x; + x = x2; } template -void Point::setY(const T& y) noexcept +void Point::setY(const T& y2) noexcept { - fY = y; + y = y2; } template -void Point::setPos(const T& x, const T& y) noexcept +void Point::setPos(const T& x2, const T& y2) noexcept { - fX = x; - fY = y; + x = x2; + y = y2; } template void Point::setPos(const Point& pos) noexcept { - fX = pos.fX; - fY = pos.fY; + x = pos.x; + y = pos.y; } template -void Point::moveBy(const T& x, const T& y) noexcept +void Point::moveBy(const T& x2, const T& y2) noexcept { - fX = static_cast(fX+x); - fY = static_cast(fY+y); + x = static_cast(x+x2); + y = static_cast(y+y2); } template void Point::moveBy(const Point& pos) noexcept { - fX = static_cast(fX+pos.fX); - fY = static_cast(fY+pos.fY); + x = static_cast(x+pos.x); + y = static_cast(y+pos.y); } template bool Point::isZero() const noexcept { - return fX == 0 && fY == 0; + return x == 0 && y == 0; } template bool Point::isNotZero() const noexcept { - return fX != 0 || fY != 0; + return x != 0 || y != 0; } template Point Point::operator+(const Point& pos) noexcept { - return Point(fX+pos.fX, fY+pos.fY); + return Point(x+pos.x, y+pos.y); } template Point Point::operator-(const Point& pos) noexcept { - return Point(fX-pos.fX, fY-pos.fY); + return Point(x-pos.x, y-pos.y); } template Point& Point::operator=(const Point& pos) noexcept { - fX = pos.fX; - fY = pos.fY; + x = pos.x; + y = pos.y; return *this; } template Point& Point::operator+=(const Point& pos) noexcept { - fX = static_cast(fX+pos.fX); - fY = static_cast(fY+pos.fY); + x = static_cast(x+pos.x); + y = static_cast(y+pos.y); return *this; } template Point& Point::operator-=(const Point& pos) noexcept { - fX = static_cast(fX-pos.fX); - fY = static_cast(fY-pos.fY); + x = static_cast(x-pos.x); + y = static_cast(y-pos.y); return *this; } template bool Point::operator==(const Point& pos) const noexcept { - return (fX == pos.fX && fY == pos.fY); + return (x == pos.x && y == pos.y); } template bool Point::operator!=(const Point& pos) const noexcept { - return (fX != pos.fX || fY != pos.fY); + return (x != pos.x || y != pos.y); } // ----------------------------------------------------------------------- @@ -321,162 +321,162 @@ bool Size::operator!=(const Size& size) const noexcept template Line::Line() noexcept - : fPosStart(0, 0), - fPosEnd(0, 0) {} + : posStart(0, 0), + posEnd(0, 0) {} template Line::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept - : fPosStart(startX, startY), - fPosEnd(endX, endY) {} + : posStart(startX, startY), + posEnd(endX, endY) {} template Line::Line(const T& startX, const T& startY, const Point& endPos) noexcept - : fPosStart(startX, startY), - fPosEnd(endPos) {} + : posStart(startX, startY), + posEnd(endPos) {} template Line::Line(const Point& startPos, const T& endX, const T& endY) noexcept - : fPosStart(startPos), - fPosEnd(endX, endY) {} + : posStart(startPos), + posEnd(endX, endY) {} template Line::Line(const Point& startPos, const Point& endPos) noexcept - : fPosStart(startPos), - fPosEnd(endPos) {} + : posStart(startPos), + posEnd(endPos) {} template Line::Line(const Line& line) noexcept - : fPosStart(line.fPosStart), - fPosEnd(line.fPosEnd) {} + : posStart(line.posStart), + posEnd(line.posEnd) {} template const T& Line::getStartX() const noexcept { - return fPosStart.fX; + return posStart.x; } template const T& Line::getStartY() const noexcept { - return fPosStart.fY; + return posStart.y; } template const T& Line::getEndX() const noexcept { - return fPosEnd.fX; + return posEnd.x; } template const T& Line::getEndY() const noexcept { - return fPosEnd.fY; + return posEnd.y; } template const Point& Line::getStartPos() const noexcept { - return fPosStart; + return posStart; } template const Point& Line::getEndPos() const noexcept { - return fPosEnd; + return posEnd; } template void Line::setStartX(const T& x) noexcept { - fPosStart.fX = x; + posStart.x = x; } template void Line::setStartY(const T& y) noexcept { - fPosStart.fY = y; + posStart.y = y; } template void Line::setStartPos(const T& x, const T& y) noexcept { - fPosStart = Point(x, y); + posStart = Point(x, y); } template void Line::setStartPos(const Point& pos) noexcept { - fPosStart = pos; + posStart = pos; } template void Line::setEndX(const T& x) noexcept { - fPosEnd.fX = x; + posEnd.x = x; } template void Line::setEndY(const T& y) noexcept { - fPosEnd.fY = y; + posEnd.y = y; } template void Line::setEndPos(const T& x, const T& y) noexcept { - fPosEnd = Point(x, y); + posEnd = Point(x, y); } template void Line::setEndPos(const Point& pos) noexcept { - fPosEnd = pos; + posEnd = pos; } template void Line::moveBy(const T& x, const T& y) noexcept { - fPosStart.moveBy(x, y); - fPosEnd.moveBy(x, y); + posStart.moveBy(x, y); + posEnd.moveBy(x, y); } template void Line::moveBy(const Point& pos) noexcept { - fPosStart.moveBy(pos); - fPosEnd.moveBy(pos); + posStart.moveBy(pos); + posEnd.moveBy(pos); } template bool Line::isNull() const noexcept { - return fPosStart == fPosEnd; + return posStart == posEnd; } template bool Line::isNotNull() const noexcept { - return fPosStart != fPosEnd; + return posStart != posEnd; } template Line& Line::operator=(const Line& line) noexcept { - fPosStart = line.fPosStart; - fPosEnd = line.fPosEnd; + posStart = line.posStart; + posEnd = line.posEnd; return *this; } template bool Line::operator==(const Line& line) const noexcept { - return (fPosStart == line.fPosStart && fPosEnd == line.fPosEnd); + return (posStart == line.posStart && posEnd == line.posEnd); } template bool Line::operator!=(const Line& line) const noexcept { - return (fPosStart != line.fPosStart || fPosEnd != line.fPosEnd); + return (posStart != line.posStart || posEnd != line.posEnd); } // ----------------------------------------------------------------------- @@ -530,13 +530,13 @@ Circle::Circle(const Circle& cir) noexcept template const T& Circle::getX() const noexcept { - return fPos.fX; + return fPos.x; } template const T& Circle::getY() const noexcept { - return fPos.fY; + return fPos.y; } template @@ -548,20 +548,20 @@ const Point& Circle::getPos() const noexcept template void Circle::setX(const T& x) noexcept { - fPos.fX = x; + fPos.x = x; } template void Circle::setY(const T& y) noexcept { - fPos.fY = y; + fPos.y = y; } template void Circle::setPos(const T& x, const T& y) noexcept { - fPos.fX = x; - fPos.fY = y; + fPos.x = x; + fPos.y = y; } template @@ -605,20 +605,6 @@ void Circle::setNumSegments(const uint num) fSin = std::sin(fTheta); } -#ifndef DPF_TEST_POINT_CPP -template -void Circle::draw() -{ - _draw(false); -} - -template -void Circle::drawOutline() -{ - _draw(true); -} -#endif - template Circle& Circle::operator=(const Circle& cir) noexcept { @@ -648,85 +634,71 @@ bool Circle::operator!=(const Circle& cir) const noexcept template Triangle::Triangle() noexcept - : fPos1(0, 0), - fPos2(0, 0), - fPos3(0, 0) {} + : pos1(0, 0), + pos2(0, 0), + pos3(0, 0) {} template Triangle::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept - : fPos1(x1, y1), - fPos2(x2, y2), - fPos3(x3, y3) {} + : pos1(x1, y1), + pos2(x2, y2), + pos3(x3, y3) {} template Triangle::Triangle(const Point& pos1, const Point& pos2, const Point& pos3) noexcept - : fPos1(pos1), - fPos2(pos2), - fPos3(pos3) {} + : pos1(pos1), + pos2(pos2), + pos3(pos3) {} template Triangle::Triangle(const Triangle& tri) noexcept - : fPos1(tri.fPos1), - fPos2(tri.fPos2), - fPos3(tri.fPos3) {} + : pos1(tri.pos1), + pos2(tri.pos2), + pos3(tri.pos3) {} template bool Triangle::isNull() const noexcept { - return fPos1 == fPos2 && fPos1 == fPos3; + return pos1 == pos2 && pos1 == pos3; } template bool Triangle::isNotNull() const noexcept { - return fPos1 != fPos2 || fPos1 != fPos3; + return pos1 != pos2 || pos1 != pos3; } template bool Triangle::isValid() const noexcept { - return fPos1 != fPos2 && fPos1 != fPos3; + return pos1 != pos2 && pos1 != pos3; } template bool Triangle::isInvalid() const noexcept { - return fPos1 == fPos2 || fPos1 == fPos3; -} - -#ifndef DPF_TEST_POINT_CPP -template -void Triangle::draw() -{ - _draw(false); -} - -template -void Triangle::drawOutline() -{ - _draw(true); + return pos1 == pos2 || pos1 == pos3; } -#endif template Triangle& Triangle::operator=(const Triangle& tri) noexcept { - fPos1 = tri.fPos1; - fPos2 = tri.fPos2; - fPos3 = tri.fPos3; + pos1 = tri.pos1; + pos2 = tri.pos2; + pos3 = tri.pos3; return *this; } template bool Triangle::operator==(const Triangle& tri) const noexcept { - return (fPos1 == tri.fPos1 && fPos2 == tri.fPos2 && fPos3 == tri.fPos3); + return (pos1 == tri.pos1 && pos2 == tri.pos2 && pos3 == tri.pos3); } template bool Triangle::operator!=(const Triangle& tri) const noexcept { - return (fPos1 != tri.fPos1 || fPos2 != tri.fPos2 || fPos3 != tri.fPos3); + return (pos1 != tri.pos1 || pos2 != tri.pos2 || pos3 != tri.pos3); } // ----------------------------------------------------------------------- @@ -765,13 +737,13 @@ Rectangle::Rectangle(const Rectangle& rect) noexcept template const T& Rectangle::getX() const noexcept { - return pos.fX; + return pos.x; } template const T& Rectangle::getY() const noexcept { - return pos.fY; + return pos.y; } template @@ -801,20 +773,20 @@ const Size& Rectangle::getSize() const noexcept template void Rectangle::setX(const T& x) noexcept { - pos.fX = x; + pos.x = x; } template void Rectangle::setY(const T& y) noexcept { - pos.fY = y; + pos.y = y; } template void Rectangle::setPos(const T& x, const T& y) noexcept { - pos.fX = x; - pos.fY = y; + pos.x = x; + pos.y = y; } template @@ -889,25 +861,25 @@ void Rectangle::setRectangle(const Rectangle& rect) noexcept template bool Rectangle::contains(const T& x, const T& y) const noexcept { - return (x >= pos.fX && y >= pos.fY && x <= pos.fX+size.fWidth && y <= pos.fY+size.fHeight); + return (x >= pos.x && y >= pos.y && x <= pos.x+size.fWidth && y <= pos.y+size.fHeight); } template bool Rectangle::contains(const Point& pos) const noexcept { - return contains(pos.fX, pos.fY); + return contains(pos.x, pos.y); } template bool Rectangle::containsX(const T& x) const noexcept { - return (x >= pos.fX && x <= pos.fX + size.fWidth); + return (x >= pos.x && x <= pos.x + size.fWidth); } template bool Rectangle::containsY(const T& y) const noexcept { - return (y >= pos.fY && y <= pos.fY + size.fHeight); + return (y >= pos.y && y <= pos.y + size.fHeight); } template diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index acf6aa27..e8567146 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -15,6 +15,7 @@ */ #include "../OpenGL.hpp" +#include "../Color.hpp" #include "../ImageWidgets.hpp" #include "SubWidgetPrivateData.hpp" @@ -24,24 +25,50 @@ START_NAMESPACE_DGL +// ----------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext&, const bool includeAlpha) +{ + if (includeAlpha) + glColor4f(red, green, blue, alpha); + else + glColor3f(red, green, blue); +} + // ----------------------------------------------------------------------- // Line template -void Line::draw() +static void drawLine(const Point& posStart, const Point& posEnd) { - DISTRHO_SAFE_ASSERT_RETURN(fPosStart != fPosEnd,); + DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); glBegin(GL_LINES); { - glVertex2d(fPosStart.fX, fPosStart.fY); - glVertex2d(fPosEnd.fX, fPosEnd.fY); + glVertex2d(posStart.getX(), posStart.getY()); + glVertex2d(posEnd.getX(), posEnd.getY()); } glEnd(); } +template +void Line::draw(const GraphicsContext&, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + glLineWidth(width); + drawLine(posStart, posEnd); +} + +template +void Line::draw() +{ + drawLine(posStart, posEnd); +} + template class Line; template class Line; template class Line; @@ -53,26 +80,60 @@ template class Line; // Circle template -void Circle::_draw(const bool outline) +static void drawCircle(const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) { - DISTRHO_SAFE_ASSERT_RETURN(fNumSegments >= 3 && fSize > 0.0f,); + DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); - double t, x = fSize, y = 0.0; + const T origx = pos.getX(); + const T origy = pos.getY(); + double t, x = size, y = 0.0; glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); - for (uint i=0; i +void Circle::draw(const GraphicsContext&) +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext&, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(lineWidth); + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +} + +template +void Circle::draw() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +} + template class Circle; template class Circle; template class Circle; @@ -84,21 +145,51 @@ template class Circle; // Triangle template -void Triangle::_draw(const bool outline) +static void drawTriangle(const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) { - DISTRHO_SAFE_ASSERT_RETURN(fPos1 != fPos2 && fPos1 != fPos3,); + DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); { - glVertex2d(fPos1.fX, fPos1.fY); - glVertex2d(fPos2.fX, fPos2.fY); - glVertex2d(fPos3.fX, fPos3.fY); + glVertex2d(pos1.getX(), pos1.getY()); + glVertex2d(pos2.getX(), pos2.getY()); + glVertex2d(pos3.getX(), pos3.getY()); } glEnd(); } +template +void Triangle::draw(const GraphicsContext&) +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(lineWidth); + drawTriangle(pos1, pos2, pos3, true); +} + +template +void Triangle::draw() +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline() +{ + drawTriangle(pos1, pos2, pos3, true); +} + template class Triangle; template class Triangle; template class Triangle; @@ -141,25 +232,28 @@ static void drawRectangle(const Rectangle& rect, const bool outline) template void Rectangle::draw(const GraphicsContext&) { - drawRectangle(*this, false); + drawRectangle(*this, false); } template -void Rectangle::drawOutline(const GraphicsContext&) +void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) { - drawRectangle(*this, true); + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(lineWidth); + drawRectangle(*this, true); } template void Rectangle::draw() { - drawRectangle(*this, true); + drawRectangle(*this, false); } template void Rectangle::drawOutline() { - drawRectangle(*this, true); + drawRectangle(*this, true); } template class Rectangle; diff --git a/tests/Demo.cpp b/tests/Demo.cpp index d54154e2..72f5b16a 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -14,10 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -// #ifndef DGL_OPENGL -// #error OpenGL build required for Demo -// #endif - #include "tests.hpp" #include "widgets/ExampleColorWidget.hpp" @@ -90,36 +86,33 @@ protected: const GraphicsContext& context(getGraphicsContext()); const int iconSize = bgIcon.getWidth(); -#if 0 /* TODO make generic */ - glColor3f(0.027f, 0.027f, 0.027f); - Rectangle(0, 0, getSize()).draw(); + Color(0.027f, 0.027f, 0.027f).setFor(context); + Rectangle(0, 0, getSize()).draw(context); bgIcon.setY(curPage*iconSize + curPage*3); - glColor3f(0.129f, 0.129f, 0.129f); - bgIcon.draw(); + Color(0.129f, 0.129f, 0.129f).setFor(context); + bgIcon.draw(context); - glColor3f(0.184f, 0.184f, 0.184f); - bgIcon.drawOutline(); + Color(0.184f, 0.184f, 0.184f).setFor(context); + bgIcon.drawOutline(context); if (curHover != curPage && curHover != -1) { Rectangle rHover(1, curHover*iconSize + curHover*3, iconSize-2, iconSize-2); - glColor3f(0.071f, 0.071f, 0.071f); - rHover.draw(); + Color(0.071f, 0.071f, 0.071f).setFor(context); + rHover.draw(context); - glColor3f(0.102f, 0.102f, 0.102f); - rHover.drawOutline(); + Color(0.102f, 0.102f, 0.102f).setFor(context); + rHover.drawOutline(context); } - glLineWidth(2.0f); - glColor3f(0.184f, 0.184f, 0.184f); - lineSep.draw(); + Color(0.184f, 0.184f, 0.184f).setFor(context); + lineSep.draw(context); // reset color - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -#endif + Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true); const int pad = iconSize/2 - DemoArtwork::ico1Width/2; @@ -274,6 +267,7 @@ public: #endif wLeft.setAbsolutePos(2, 2); + setResizable(true); setSize(600, 500); setTitle("DGL Demo"); @@ -375,8 +369,6 @@ int main(int argc, char* argv[]) if (argc > 1) { - // TODO images, text - /**/ if (std::strcmp(argv[1], "color") == 0) createAndShowExampleWidgetStandaloneWindow(app); else if (std::strcmp(argv[1], "images") == 0) diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index 4d55a808..e47c923f 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -20,6 +20,7 @@ // ------------------------------------------------------ // DGL Stuff +#include "../../dgl/Color.hpp" #include "../../dgl/StandaloneWindow.hpp" #include "../../dgl/SubWidget.hpp" @@ -108,15 +109,13 @@ protected: { const GraphicsContext& context(BaseWidget::getGraphicsContext()); -#if 0 /* TODO make generic */ // paint bg color (in full size) - glColor3b(r, g, b); + Color(r, g, b).setFor(context); bgFull.draw(context); // paint inverted color (in 2/3 size) - glColor3b(100-r, 100-g, 100-b); - bgSmall.draw(); -#endif + Color(100-r, 100-g, 100-b).setFor(context); + bgSmall.draw(context); } void onResize(const ResizeEvent& ev) override @@ -132,6 +131,7 @@ protected: } }; +// SubWidget template<> inline ExampleColorWidget::ExampleColorWidget(Widget* const parent) : SubWidget(parent), diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index 72ad1e99..c001674c 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -20,6 +20,7 @@ // ------------------------------------------------------ // DGL Stuff +#include "../../dgl/Color.hpp" #include "../../dgl/SubWidget.hpp" #include "../../dgl/TopLevelWidget.hpp" @@ -65,6 +66,8 @@ public: protected: void onDisplay() override { + const GraphicsContext& context(BaseWidget::getGraphicsContext()); + const uint width = BaseWidget::getWidth(); const uint height = BaseWidget::getHeight(); @@ -81,36 +84,30 @@ protected: // 1st r.setY(3); -#if 0 /* TODO make generic */ if (clicked[0+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); -#endif + Color(0.3f, 0.5f, 0.8f).setFor(context); r.draw(); // 2nd r.setY(3 + height/3); -#if 0 /* TODO make generic */ if (clicked[3+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); -#endif + Color(0.3f, 0.5f, 0.8f).setFor(context); r.draw(); // 3rd r.setY(3 + height*2/3); -#if 0 /* TODO make generic */ if (clicked[6+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); -#endif + Color(0.3f, 0.5f, 0.8f).setFor(context); r.draw(); } diff --git a/tests/widgets/ExampleShapesWidget.hpp b/tests/widgets/ExampleShapesWidget.hpp index f4f9be6f..d5f213fb 100644 --- a/tests/widgets/ExampleShapesWidget.hpp +++ b/tests/widgets/ExampleShapesWidget.hpp @@ -20,6 +20,7 @@ // ------------------------------------------------------ // DGL Stuff +#include "../../dgl/Color.hpp" #include "../../dgl/SubWidget.hpp" #include "../../dgl/TopLevelWidget.hpp" @@ -60,40 +61,28 @@ public: protected: void onDisplay() override { -#if 0 - glEnable(GL_MULTISAMPLE); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_SRC_ALPHA); - glEnable(GL_ONE_MINUS_SRC_ALPHA); - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); -#endif - -#if 0 /* TODO make generic */ - glLineWidth(1.0f); - glColor3f(0.302f, 0.337f, 0.361f); - bg.draw(); - - glColor3f(0.235f, 0.271f, 0.294f); - rect.draw(); - - glColor3f(0.176f, 0.212f, 0.235f); - rect.drawOutline(); - - glColor3f(0.302f*2, 0.337f*2, 0.361f*2); - tri.draw(); - - glLineWidth(3.0f); - glColor3f(0.302f/2.0f, 0.337f/2.0f, 0.361f/2.0f); - tri.drawOutline(); - - glColor3f(0.235f, 0.271f, 0.294f); - cir.draw(); - - glLineWidth(2.0f); - glColor3f(0.176f/4, 0.212f/4, 0.235f/4); - cir.drawOutline(); -#endif + const GraphicsContext& context(BaseWidget::getGraphicsContext()); + + Color(0.302f, 0.337f, 0.361f).setFor(context);; + bg.draw(context); + + Color(0.235f, 0.271f, 0.294f).setFor(context); + rect.draw(context); + + Color(0.176f, 0.212f, 0.235f).setFor(context); + rect.drawOutline(context, 1); + + Color(0.302f*2, 0.337f*2, 0.361f*2).setFor(context); + tri.draw(context); + + Color(0.302f/2.0f, 0.337f/2.0f, 0.361f/2.0f).setFor(context); + tri.drawOutline(context, 3); + + Color(0.235f, 0.271f, 0.294f).setFor(context); + cir.draw(context); + + Color(0.176f/4, 0.212f/4, 0.235f/4).setFor(context); + cir.drawOutline(context, 2); } void onResize(const ResizeEvent& ev) override From 6e141323c833cbd4aa024c20a8c65f2ff207324b Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 04:52:37 +0100 Subject: [PATCH 085/159] Implement core cairo support (shapes and images) --- dgl/Cairo.hpp | 20 ++ dgl/ImageBase.hpp | 1 + dgl/StandaloneWindow.hpp | 3 + dgl/src/Cairo.cpp | 218 +++++++++++++++++++--- dgl/src/ImageBase.cpp | 2 +- dgl/src/OpenGL.cpp | 12 +- dgl/src/SubWidgetPrivateData.cpp | 1 + tests/Demo.cpp | 18 +- tests/widgets/ExampleRectanglesWidget.hpp | 6 +- 9 files changed, 241 insertions(+), 40 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index fb4f11ba..ba97dd4c 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -72,14 +72,34 @@ public: */ ~CairoImage() override; + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + void loadFromMemory(const char* rawData, + const Size& size, + ImageFormat format = kImageFormatBGRA) noexcept override; + /** Draw this image at position @a pos using the graphics context @a context. */ void drawAt(const GraphicsContext& context, const Point& pos) override; + /** + TODO document this. + */ + CairoImage& operator=(const CairoImage& image) noexcept; + // FIXME this should not be needed + inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) + { loadFromMemory(rawData, Size(w, h), format); }; inline void drawAt(const GraphicsContext& context, int x, int y) { drawAt(context, Point(x, y)); }; + +private: + cairo_surface_t* surface; + uchar* surfacedata; + int* datarefcount; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp index a03c1567..1e41bd63 100644 --- a/dgl/ImageBase.hpp +++ b/dgl/ImageBase.hpp @@ -24,6 +24,7 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- enum ImageFormat { + kImageFormatNull, kImageFormatBGR, kImageFormatBGRA, kImageFormatRGB, diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index 148626fa..5f2d553d 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -61,6 +61,9 @@ public: { return Window::addIdleCallback(callback, timerFrequencyInMs); } bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } + void setGeometryConstraints(uint minimumWidth, uint minimumHeight, + bool keepAspectRatio = false, bool automaticallyScale = false) + { Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) }; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index bd60fe40..e51cda98 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -92,20 +92,28 @@ static void drawCircle(cairo_t* const handle, const T origy = pos.getY(); double t, x = size, y = 0.0; + // TODO use arc /* - glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); + cairo_arc(handle, origx, origy, size, sin, cos); + */ + + cairo_move_to(handle, x + origx, y + origy); - for (uint i=0; i @@ -158,7 +166,15 @@ static void drawTriangle(cairo_t* const handle, { DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); - // TODO + cairo_move_to(handle, pos1.getX(), pos1.getY()); + cairo_line_to(handle, pos2.getX(), pos2.getY()); + cairo_line_to(handle, pos3.getX(), pos3.getY()); + cairo_line_to(handle, pos1.getX(), pos1.getY()); + + if (outline) + cairo_stroke(handle); + else + cairo_fill(handle); } template @@ -206,7 +222,12 @@ template class Triangle; template static void drawRectangle(cairo_t* const handle, const Rectangle& rect, const bool outline) { - // TODO + cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + + if (outline) + cairo_stroke(handle); + else + cairo_fill(handle); } template @@ -253,24 +274,152 @@ template class Rectangle; // ----------------------------------------------------------------------- // CairoImage +static cairo_format_t asCairoImageFormat(const ImageFormat format) +{ + switch (format) + { + case kImageFormatNull: + break; + case kImageFormatBGR: + case kImageFormatRGB: + return CAIRO_FORMAT_RGB24; + case kImageFormatBGRA: + case kImageFormatRGBA: + return CAIRO_FORMAT_ARGB32; + } + + return CAIRO_FORMAT_INVALID; +} + CairoImage::CairoImage() - : ImageBase() {} + : ImageBase(), + surface(nullptr), + surfacedata(nullptr), + datarefcount(nullptr) {} CairoImage::CairoImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) - : ImageBase(rawData, width, height, format) {} + : ImageBase(rawData, width, height, format), + surface(nullptr), + surfacedata(nullptr), + datarefcount(nullptr) +{ + loadFromMemory(rawData, width, height, format); +} CairoImage::CairoImage(const char* const rawData, const Size& size, const ImageFormat format) - : ImageBase(rawData, size, format) {} + : ImageBase(rawData, size, format), + surface(nullptr), + surfacedata(nullptr), + datarefcount(nullptr) +{ + loadFromMemory(rawData, size, format); +} CairoImage::CairoImage(const CairoImage& image) - : ImageBase(image.rawData, image.size, image.format) {} + : ImageBase(image.rawData, image.size, image.format), + surface(cairo_surface_reference(image.surface)), + surfacedata(image.surfacedata), + datarefcount(image.datarefcount) +{ + if (datarefcount != nullptr) + ++(*datarefcount); +} CairoImage::~CairoImage() { + cairo_surface_destroy(surface); + + if (datarefcount != nullptr && --(*datarefcount) == 0) + { + std::free(surfacedata); + std::free(datarefcount); + } } -void CairoImage::drawAt(const GraphicsContext&, const Point&) +void CairoImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept { + const cairo_format_t cairoformat = asCairoImageFormat(fmt); + const uint width = s.getWidth(); + const uint height = s.getHeight(); + const int stride = cairo_format_stride_for_width(cairoformat, width); + + uchar* const newdata = (uchar*)std::malloc(width * height * stride * 4); + DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,); + + cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); + DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),); + DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),); + + cairo_surface_destroy(surface); + + if (datarefcount != nullptr && --(*datarefcount) == 0) + std::free(surfacedata); + else + datarefcount = (int*)malloc(sizeof(*datarefcount)); + + surface = newsurface; + surfacedata = newdata; + *datarefcount = 1; + + switch (fmt) + { + case kImageFormatNull: + break; + case kImageFormatBGR: + // BGR8 to CAIRO_FORMAT_RGB24 + for (uint h = 0; h < height; ++h) + { + for (uint w = 0; w < width; ++w) + { + newdata[h*width*4+w*4+0] = rdata[h*width*3+w*3+0]; + newdata[h*width*4+w*4+1] = rdata[h*width*3+w*3+1]; + newdata[h*width*4+w*4+2] = rdata[h*width*3+w*3+2]; + newdata[h*width*4+w*4+3] = 0; + } + } + break; + case kImageFormatBGRA: + // RGB8 to CAIRO_FORMAT_ARGB32 + // TODO + break; + case kImageFormatRGB: + // RGB8 to CAIRO_FORMAT_RGB24 + // TODO + break; + case kImageFormatRGBA: + // RGBA8 to CAIRO_FORMAT_ARGB32 + // TODO + break; + } + + ImageBase::loadFromMemory(rdata, s, fmt); +} + +void CairoImage::drawAt(const GraphicsContext& context, const Point& pos) +{ + if (surface == nullptr) + return; + + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + + cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); + cairo_paint(handle); +} + +CairoImage& CairoImage::operator=(const CairoImage& image) noexcept +{ + cairo_surface_t* newsurface = cairo_surface_reference(image.surface); + cairo_surface_destroy(surface); + surface = newsurface; + rawData = image.rawData; + size = image.size; + format = image.format; + surfacedata = image.surfacedata; + datarefcount = image.datarefcount; + if (datarefcount != nullptr) + ++(*datarefcount); + return *this; } // ----------------------------------------------------------------------- @@ -318,21 +467,47 @@ template class ImageBaseAboutWindow; void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) { - /* - if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) - return; - */ + cairo_t* const handle = static_cast(self->getGraphicsContext()).handle; + + bool needsResetClip = false; - cairo_t* cr = static_cast(self->getGraphicsContext()).handle; cairo_matrix_t matrix; - cairo_get_matrix(cr, &matrix); - cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); - // TODO: autoScaling and cropping + cairo_get_matrix(handle, &matrix); + + if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size(width, height))) + { + // full viewport size + cairo_translate(handle, 0, 0); + } + else if (needsViewportScaling) + { + // limit viewport to widget bounds + // NOTE only used for nanovg for now, which is not relevant here + cairo_translate(handle, 0, 0); + } + else + { + // set viewport pos + cairo_translate(handle, absolutePos.getX(), absolutePos.getY()); + + // then cut the outer bounds + cairo_rectangle(handle, + 0, + 0, + std::round(self->getWidth() * autoScaleFactor), + std::round(self->getHeight() * autoScaleFactor)); + + cairo_clip(handle); + needsResetClip = true; + } // display widget self->onDisplay(); - cairo_set_matrix(cr, &matrix); + if (needsResetClip) + cairo_reset_clip(handle); + + cairo_set_matrix(handle, &matrix); selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } @@ -347,6 +522,7 @@ void TopLevelWidget::PrivateData::display() const double autoScaleFactor = window.pData->autoScaleFactor; + // FIXME anything needed here? #if 0 // full viewport size if (window.pData->autoScaling) diff --git a/dgl/src/ImageBase.cpp b/dgl/src/ImageBase.cpp index 2691c4a7..ea3477b6 100644 --- a/dgl/src/ImageBase.cpp +++ b/dgl/src/ImageBase.cpp @@ -24,7 +24,7 @@ START_NAMESPACE_DGL ImageBase::ImageBase() : rawData(nullptr), size(0, 0), - format(kImageFormatBGRA) {} + format(kImageFormatNull) {} ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) : rawData(rdata), diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index e8567146..6e733bc9 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -264,11 +264,14 @@ template class Rectangle; template class Rectangle; // ----------------------------------------------------------------------- +// OpenGLImage static GLenum asOpenGLImageFormat(const ImageFormat format) { switch (format) { + case kImageFormatNull: + break; case kImageFormatBGR: return GL_BGR; case kImageFormatBGRA: @@ -279,7 +282,7 @@ static GLenum asOpenGLImageFormat(const ImageFormat format) return GL_RGBA; } - return GL_BGRA; + return 0x0; } static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) @@ -353,8 +356,8 @@ OpenGLImage::~OpenGLImage() void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept { - ImageBase::loadFromMemory(rdata, s, fmt); setupCalled = false; + ImageBase::loadFromMemory(rdata, s, fmt); } void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) @@ -456,7 +459,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const } else { - // only set viewport pos + // set viewport pos glViewport(absolutePos.getX() * autoScaleFactor, -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), std::round(width * autoScaleFactor), @@ -476,10 +479,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const self->onDisplay(); if (needsDisableScissor) - { glDisable(GL_SCISSOR_TEST); - needsDisableScissor = false; - } selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp index 737b7024..342d017a 100644 --- a/dgl/src/SubWidgetPrivateData.cpp +++ b/dgl/src/SubWidgetPrivateData.cpp @@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) selfw((Widget*)s), parentWidget(pw), absolutePos(), + needsFullViewportForDrawing(false), needsViewportScaling(false) { parentWidget->pData->subWidgets.push_back(self); diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 72f5b16a..92acd481 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -67,17 +67,19 @@ public: curPage(0), curHover(-1) { -#ifdef DGL_OPENGL - // for text - nvg.loadSharedResources(); -#endif using namespace DemoArtwork; img1.loadFromMemory(ico1Data, ico1Width, ico1Height, kImageFormatBGR); img2.loadFromMemory(ico2Data, ico2Width, ico2Height, kImageFormatBGR); img3.loadFromMemory(ico3Data, ico3Width, ico2Height, kImageFormatBGR); img4.loadFromMemory(ico4Data, ico4Width, ico4Height, kImageFormatBGR); + +#ifdef DGL_OPENGL img5.loadFromMemory(ico5Data, ico5Width, ico5Height, kImageFormatBGR); + + // for text + nvg.loadSharedResources(); +#endif } protected: @@ -120,9 +122,10 @@ protected: img2.drawAt(context, pad, pad + 3 + iconSize); img3.drawAt(context, pad, pad + 6 + iconSize*2); img4.drawAt(context, pad, pad + 9 + iconSize*3); - img5.drawAt(context, pad, pad + 12 + iconSize*4); #ifdef DGL_OPENGL + img5.drawAt(context, pad, pad + 12 + iconSize*4); + // draw some text nvg.beginFrame(this); @@ -267,10 +270,6 @@ public: #endif wLeft.setAbsolutePos(2, 2); - setResizable(true); - setSize(600, 500); - setTitle("DGL Demo"); - curPageChanged(0); } @@ -350,6 +349,7 @@ template void createAndShowExampleWidgetStandaloneWindow(Application& app) { ExampleWidgetStandaloneWindow swin(app); + swin.setResizable(true); swin.setSize(600, 500); swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); swin.show(); diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index c001674c..9dee758a 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -89,7 +89,7 @@ protected: else Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // 2nd r.setY(3 + height/3); @@ -99,7 +99,7 @@ protected: else Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // 3rd r.setY(3 + height*2/3); @@ -109,7 +109,7 @@ protected: else Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); } } From 77ef4b9b31e4d5ec9511869e8a943b668e9abfd9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 05:58:07 +0100 Subject: [PATCH 086/159] Fix Demo test rebuilds --- tests/Demo.cpp | 7 ++++++- tests/Makefile | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 92acd481..e86abdcd 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -239,7 +239,12 @@ class DemoWindow : public StandaloneWindow, static const int kSidebarWidth = 81; public: - static constexpr const char* const kExampleWidgetName = "Demo"; +#ifdef DGL_CAIRO + static constexpr const char* const kExampleWidgetName = "Demo - Cairo"; +#endif +#ifdef DGL_OPENGL + static constexpr const char* const kExampleWidgetName = "Demo - OpenGL"; +#endif DemoWindow(Application& app) : StandaloneWindow(app), diff --git a/tests/Makefile b/tests/Makefile index 4500f406..5deecdd2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -133,5 +133,8 @@ clean: # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS:%.o=%.d) +-include ../build/tests/Demo.cpp.cairo.d +-include ../build/tests/Demo.cpp.opengl.d +-include ../build/tests/Demo.cpp.vulkan.d # --------------------------------------------------------------------------------------------------------------------- From a3d11e4dc0aec477d0cef99785c299dea5f46bb1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 17:37:12 +0100 Subject: [PATCH 087/159] Build on windows, though doesnt quite work yet Signed-off-by: falkTX --- dgl/Makefile | 96 ++++++++++++++--------- dgl/src/WindowPrivateData.cpp | 8 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 139 ++++++++++++++++++++++++++++------ dgl/src/pugl.hpp | 15 ++++ tests/Makefile | 20 ++--- 6 files changed, 207 insertions(+), 73 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 2612c014..d9390462 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -14,7 +14,7 @@ BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) # TODO fix these after pugl-upstream is done -BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra -Wno-narrowing +BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -Wno-narrowing # ifneq ($(MACOS_OLD),true) # needed by sofd right now, fix later @@ -39,36 +39,42 @@ OBJS_common = \ ../build/dgl/WidgetPrivateData.cpp.o \ ../build/dgl/Window.cpp.o \ ../build/dgl/WindowPrivateData.cpp.o - # ../build/dgl/WindowFileBrowser.cpp.o -# TODO: ImageWidgets.cpp - # --------------------------------------------------------------------------------------------------------------------- OBJS_cairo = $(OBJS_common) \ - ../build/dgl/Cairo.cpp.cairo.o \ - ../build/dgl/pugl.cpp.cairo.o + ../build/dgl/Cairo.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/pugl.mm.cairo.o +else +OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o +endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ ../build/dgl/ImageWidgets.cpp.o \ ../build/dgl/OpenGL.cpp.opengl.o \ - ../build/dgl/NanoVG.cpp.opengl.o \ - ../build/dgl/pugl.cpp.opengl.o + ../build/dgl/NanoVG.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/pugl.mm.opengl.o +else +OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o +endif + +# --------------------------------------------------------------------------------------------------------------------- + +OBJS_vulkan = $(OBJS_common) \ + ../build/dgl/Cairo.cpp.vulkan.o + +ifeq ($(MACOS),true) +OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o +else +OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o +endif # --------------------------------------------------------------------------------------------------------------------- @@ -82,6 +88,10 @@ TARGETS += ../build/libdgl-opengl.a TARGETS += ../build/libdgl.a endif +ifeq ($(HAVE_VULKAN),true) +TARGETS += ../build/libdgl-vulkan.a +endif + # --------------------------------------------------------------------------------------------------------------------- all: $(TARGETS) @@ -100,6 +110,12 @@ all: $(TARGETS) $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ +../build/libdgl-vulkan.a: $(OBJS_vulkan) + -@mkdir -p ../build + @echo "Creating libdgl-vulkan.a" + $(SILENT)rm -f $@ + $(SILENT)$(AR) crs $@ $^ + # Compat name, to be removed soon ../build/libdgl.a: ../build/libdgl-opengl.a @echo "Symlinking libdgl.a" @@ -117,6 +133,11 @@ all: $(TARGETS) @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ +../build/dgl/%.mm.o: src/%.mm + -@mkdir -p ../build/dgl + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@ + # --------------------------------------------------------------------------------------------------------------------- ../build/dgl/%.cpp.cairo.o: src/%.cpp @@ -124,15 +145,10 @@ all: $(TARGETS) @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -# ../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (Cairo variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -# -# ../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (Cairo variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@ +../build/dgl/%.mm.cairo.o: src/%.mm + -@mkdir -p ../build/dgl + @echo "Compiling $< (Cairo variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- @@ -141,15 +157,22 @@ all: $(TARGETS) @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -# ../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (OpenGL variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -# -# ../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl-upstream/* -# -@mkdir -p ../build/dgl -# @echo "Compiling $< (OpenGL variant)" -# $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@ +../build/dgl/%.mm.opengl.o: src/%.mm + -@mkdir -p ../build/dgl + @echo "Compiling $< (OpenGL variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ + +# --------------------------------------------------------------------------------------------------------------------- + +../build/dgl/%.cpp.vulkan.o: src/%.cpp + -@mkdir -p ../build/dgl + @echo "Compiling $< (Vulkan variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ + +../build/dgl/%.mm.vulkan.o: src/%.mm + -@mkdir -p ../build/dgl + @echo "Compiling $< (Vulkan variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- @@ -164,5 +187,6 @@ debug: -include $(OBJS_common:%.o=%.d) -include $(OBJS_cairo:%.o=%.d) -include $(OBJS_opengl:%.o=%.d) +-include $(OBJS_vulkan:%.o=%.d) # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 9d962127..fc5413ab 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -19,11 +19,13 @@ #include "pugl.hpp" -#include +#define DGL_DEBUG_EVENTS -START_NAMESPACE_DGL +#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) +# include +#endif -#define DGL_DEBUG_EVENTS +START_NAMESPACE_DGL #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index b0a018a0..9cab7cdc 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit b0a018a006c5d7d7796074b86c1b69f8671d2d83 +Subproject commit 9cab7cdcba159aec5f3df4d5841b83c12ef64185 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index df706041..f83efb78 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -25,7 +25,36 @@ #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) +# import +# include +# include +# ifdef DGL_CAIRO +# include +# include +# endif +# ifdef DGL_OPENGL +# include +# endif +# ifdef DGL_VULKAN +# import +# include +# include +# endif #elif defined(DISTRHO_OS_WINDOWS) +# include +# include +# include +# ifdef DGL_CAIRO +# include +# include +# endif +# ifdef DGL_OPENGL +# include +# endif +# ifdef DGL_VULKAN +# include +# include +# endif #else # include # include @@ -51,6 +80,7 @@ # include # endif # ifdef DGL_OPENGL +# include # include # endif # ifdef DGL_VULKAN @@ -67,9 +97,33 @@ START_NAMESPACE_DGL #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) +/* # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) +*/ +# import "pugl-upstream/src/mac.m" +# import "pugl-upstream/src/mac_stub.m" +# ifdef DGL_CAIRO +# import "pugl-upstream/src/mac_cairo.m" +# endif +# ifdef DGL_OPENGL +# import "pugl-upstream/src/mac_gl.m" +# endif +# ifdef DGL_VULKAN +# import "pugl-upstream/src/mac_vulkan.m" +# endif #elif defined(DISTRHO_OS_WINDOWS) +# include "pugl-upstream/src/win.c" +# include "pugl-upstream/src/win_stub.c" +# ifdef DGL_CAIRO +# include "pugl-upstream/src/win_cairo.c" +# endif +# ifdef DGL_OPENGL +# include "pugl-upstream/src/win_gl.c" +# endif +# ifdef DGL_VULKAN +# include "pugl-upstream/src/win_vulkan.c" +# endif #else # include "pugl-upstream/src/x11.c" # include "pugl-upstream/src/x11_stub.c" @@ -120,7 +174,7 @@ void puglRaiseWindow(PuglView* view) // -------------------------------------------------------------------------------------------------------------------- // set backend that matches current build -void puglSetMatchingBackendForCurrentBuild(PuglView* view) +void puglSetMatchingBackendForCurrentBuild(PuglView* const view) { #ifdef DGL_CAIRO puglSetBackend(view, puglCairoBackend()); @@ -136,7 +190,7 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* view) // -------------------------------------------------------------------------------------------------------------------- // Combine puglSetMinSize and puglSetAspectRatio -PuglStatus puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) +PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect) { view->minWidth = width; view->minHeight = height; @@ -170,16 +224,18 @@ PuglStatus puglSetGeometryConstraints(PuglView* view, unsigned int width, unsign // -------------------------------------------------------------------------------------------------------------------- // set window size without changing frame x/y position -PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) +PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height) { #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) - // TODO - const PuglRect frame = { 0.0, 0.0, (double)width, (double)height }; + // keep upstream behaviour + const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height }; return puglSetFrame(view, frame); #elif defined(DISTRHO_OS_WINDOWS) // matches upstream pugl, except we add SWP_NOMOVE flag if (view->impl->hwnd) { + const PuglRect frame = view->frame; + RECT rect = { (long)frame.x, (long)frame.y, (long)frame.x + (long)frame.width, @@ -202,23 +258,6 @@ PuglStatus puglSetWindowSize(PuglView* view, unsigned int width, unsigned int he { if (! XResizeWindow(view->world->impl->display, view->impl->win, width, height)) return PUGL_UNKNOWN_ERROR; -#if 0 - if (! fResizable) - { - XSizeHints sizeHints; - memset(&sizeHints, 0, sizeof(sizeHints)); - - sizeHints.flags = PSize|PMinSize|PMaxSize; - sizeHints.width = static_cast(width); - sizeHints.height = static_cast(height); - sizeHints.min_width = static_cast(width); - sizeHints.min_height = static_cast(height); - sizeHints.max_width = static_cast(width); - sizeHints.max_height = static_cast(height); - - XSetWMNormalHints(xDisplay, xWindow, &sizeHints); - } -#endif } #endif @@ -241,7 +280,7 @@ void puglOnDisplayPrepare(PuglView*) // -------------------------------------------------------------------------------------------------------------------- // DGL specific, build-specific fallback resize -void puglFallbackOnResize(PuglView* view) +void puglFallbackOnResize(PuglView* const view) { #ifdef DGL_OPENGL glEnable(GL_BLEND); @@ -255,6 +294,60 @@ void puglFallbackOnResize(PuglView* view) #endif } +#ifdef DISTRHO_OS_WINDOWS +// -------------------------------------------------------------------------------------------------------------------- +// win32 specific, call ShowWindow with SW_RESTORE + +void puglWin32RestoreWindow(PuglView* const view) +{ + PuglInternals* impl = view->impl; + DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); + + ShowWindow(impl->hwnd, SW_RESTORE); + SetFocus(impl->hwnd); +} + +// -------------------------------------------------------------------------------------------------------------------- +// win32 specific, center view based on parent coordinates (if there is one) + +void puglWin32ShowWindowCentered(PuglView* const view) +{ + PuglInternals* impl = view->impl; + DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); + + RECT rectChild, rectParent; + + if (view->transientParent != 0 && + GetWindowRect(impl->hwnd, &rectChild) && + GetWindowRect((HWND)view->transientParent, &rectParent)) + { + SetWindowPos(impl->hwnd, (HWND)view->transientParent, + rectParent.left + (rectChild.right-rectChild.left)/2, + rectParent.top + (rectChild.bottom-rectChild.top)/2, + 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); + } + else + { + ShowWindow(impl->hwnd, SW_SHOWNORMAL); + } + + SetFocus(impl->hwnd); +} + +// -------------------------------------------------------------------------------------------------------------------- +// win32 specific, set or unset WS_SIZEBOX style flag + +void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) +{ + PuglInternals* impl = view->impl; + DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); + + const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX + : GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX; + SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); +} +#endif + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 1bc9a632..8013e9c4 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -28,6 +28,7 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +#define PUGL_API #define PUGL_DISABLE_DEPRECATED #include "pugl-upstream/include/pugl/pugl.h" @@ -65,6 +66,20 @@ puglOnDisplayPrepare(PuglView* view); PUGL_API void puglFallbackOnResize(PuglView* view); +#ifdef DISTRHO_OS_WINDOWS +// win32 specific, call ShowWindow with SW_RESTORE +PUGL_API void +puglWin32RestoreWindow(PuglView* view); + +// win32 specific, center view based on parent coordinates (if there is one) +PUGL_API void +puglWin32ShowWindowCentered(PuglView* view); + +// win32 specific, set or unset WS_SIZEBOX style flag +PUGL_API void +puglWin32SetWindowResizable(PuglView* view, bool resizable); +#endif + PUGL_END_DECLS // -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Makefile b/tests/Makefile index 5deecdd2..e1d3e8c3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,7 +16,7 @@ BUILD_CXX_FLAGS += $(DGL_FLAGS) -I.. -I../dgl/src/pugl-upstream/include -DDONT_S LINK_FLAGS += -lpthread # TODO fix within pugl -BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-extra +BUILD_CXX_FLAGS += -Wno-extra -Wno-missing-field-initializers # --------------------------------------------------------------------------------------------------------------------- @@ -33,8 +33,8 @@ ifeq ($(HAVE_VULKAN),true) WTESTS = Window.vulkan endif -TARGETS = $(TESTS:%=../build/tests/%) -TARGETS += $(WTESTS:Window.%=../build/tests/Window.%) +TARGETS = $(TESTS:%=../build/tests/%$(APP_EXT)) +TARGETS += $(WTESTS:Window.%=../build/tests/Window.%$(APP_EXT)) OBJS = $(TESTS:%=../build/tests/%.cpp.o) OBJS += $(WTESTS:Window.%=../build/tests/Window.cpp.%.o) @@ -102,31 +102,31 @@ clean: # --------------------------------------------------------------------------------------------------------------------- # linking steps -../build/tests/%: ../build/tests/%.cpp.o +../build/tests/%$(APP_EXT): ../build/tests/%.cpp.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ -../build/tests/%.cairo: ../build/tests/%.cpp.cairo.o +../build/tests/%.cairo$(APP_EXT): ../build/tests/%.cpp.cairo.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ -../build/tests/%.opengl: ../build/tests/%.cpp.opengl.o +../build/tests/%.opengl$(APP_EXT): ../build/tests/%.cpp.opengl.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ -../build/tests/%.vulkan: ../build/tests/%.cpp.vulkan.o +../build/tests/%.vulkan$(APP_EXT): ../build/tests/%.cpp.vulkan.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ -../build/tests/Demo.cairo: ../build/tests/Demo.cpp.cairo.o ../build/libdgl-cairo.a +../build/tests/Demo.cairo$(APP_EXT): ../build/tests/Demo.cpp.cairo.o ../build/libdgl-cairo.a @echo "Linking Demo (Cairo)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ -../build/tests/Demo.opengl: ../build/tests/Demo.cpp.opengl.o ../build/libdgl-opengl.a +../build/tests/Demo.opengl$(APP_EXT): ../build/tests/Demo.cpp.opengl.o ../build/libdgl-opengl.a @echo "Linking Demo (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ -../build/tests/Demo.vulkan: ../build/tests/Demo.cpp.vulkan.o ../build/libdgl-vulkan.a +../build/tests/Demo.vulkan$(APP_EXT): ../build/tests/Demo.cpp.vulkan.o ../build/libdgl-vulkan.a @echo "Linking Demo (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ From 058cab051567ebb93c55878816eb0b731486cdd6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 17:49:20 +0100 Subject: [PATCH 088/159] Add stubs for vulkan Signed-off-by: falkTX --- Makefile.base.mk | 13 +++ dgl/Makefile | 4 +- dgl/Vulkan.hpp | 39 +++++++++ dgl/src/Vulkan.cpp | 213 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 dgl/Vulkan.hpp create mode 100644 dgl/src/Vulkan.cpp diff --git a/Makefile.base.mk b/Makefile.base.mk index 6568471e..0ce426fc 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -219,6 +219,7 @@ endif # Check for required libraries HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) +HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) ifeq ($(MACOS_OR_WINDOWS),true) HAVE_OPENGL = true @@ -316,6 +317,18 @@ HAVE_CAIRO_OR_OPENGL = true endif +# --------------------------------------------------------------------------------------------------------------------- +# Set Vulkan specific stuff + +ifeq ($(HAVE_VULKAN),true) + +DGL_FLAGS += -DHAVE_VULKAN + +VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan) +VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan) + +endif + # --------------------------------------------------------------------------------------------------------------------- # Set optional libraries specific stuff diff --git a/dgl/Makefile b/dgl/Makefile index d9390462..ff7fa1e7 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -55,8 +55,8 @@ endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ - ../build/dgl/ImageWidgets.cpp.o \ ../build/dgl/OpenGL.cpp.opengl.o \ + ../build/dgl/ImageWidgets.cpp.o \ ../build/dgl/NanoVG.cpp.opengl.o ifeq ($(MACOS),true) @@ -68,7 +68,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- OBJS_vulkan = $(OBJS_common) \ - ../build/dgl/Cairo.cpp.vulkan.o + ../build/dgl/Vulkan.cpp.vulkan.o ifeq ($(MACOS),true) OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o diff --git a/dgl/Vulkan.hpp b/dgl/Vulkan.hpp new file mode 100644 index 00000000..c636a8a3 --- /dev/null +++ b/dgl/Vulkan.hpp @@ -0,0 +1,39 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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_VULKAN_HPP_INCLUDED +#define DGL_VULKAN_HPP_INCLUDED + +#include "Base.hpp" + +#include + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +/** + Vulkan Graphics context. + */ +struct VulkanGraphicsContext : GraphicsContext +{ +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif diff --git a/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp new file mode 100644 index 00000000..ed499222 --- /dev/null +++ b/dgl/src/Vulkan.cpp @@ -0,0 +1,213 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "../Vulkan.hpp" +#include "../Color.hpp" + +#include "SubWidgetPrivateData.hpp" +#include "TopLevelWidgetPrivateData.hpp" +#include "WidgetPrivateData.hpp" +#include "WindowPrivateData.hpp" + +START_NAMESPACE_DGL + +// ----------------------------------------------------------------------- + +static void notImplemented(const char* const name) +{ + d_stderr2("vulkan function not implemented: %s", name); +} + +// ----------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext&, bool) +{ + notImplemented("Color::setFor"); +} + +// ----------------------------------------------------------------------- +// Line + +template +void Line::draw(const GraphicsContext&, T) +{ + notImplemented("Line::draw"); +} + +template +void Line::draw() +{ + notImplemented("Line::draw"); +} + +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +// ----------------------------------------------------------------------- +// Circle + +template +void Circle::draw(const GraphicsContext&) +{ + notImplemented("Circle::draw"); +} + +template +void Circle::drawOutline(const GraphicsContext&, T) +{ + notImplemented("Circle::drawOutline"); +} + +template +void Circle::draw() +{ + notImplemented("Circle::draw"); +} + +template +void Circle::drawOutline() +{ + notImplemented("Circle::drawOutline"); +} + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +// ----------------------------------------------------------------------- +// Triangle + +template +void Triangle::draw(const GraphicsContext&) +{ + notImplemented("Triangle::draw"); +} + +template +void Triangle::drawOutline(const GraphicsContext&, T) +{ + notImplemented("Triangle::drawOutline"); +} + +template +void Triangle::draw() +{ + notImplemented("Triangle::draw"); +} + +template +void Triangle::drawOutline() +{ + notImplemented("Triangle::drawOutline"); +} + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + + +// ----------------------------------------------------------------------- +// Rectangle + +template +void Rectangle::draw(const GraphicsContext&) +{ + notImplemented("Rectangle::draw"); +} + +template +void Rectangle::drawOutline(const GraphicsContext&, T) +{ + notImplemented("Rectangle::drawOutline"); +} + +template +void Rectangle::draw() +{ + notImplemented("Rectangle::draw"); +} + +template +void Rectangle::drawOutline() +{ + notImplemented("Rectangle::drawOutline"); +} + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; + +// ----------------------------------------------------------------------- + +void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) +{ + // TODO + + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); +} + +// ----------------------------------------------------------------------- + +void TopLevelWidget::PrivateData::display() +{ + const Size size(window.getSize()); + const uint width = size.getWidth(); + const uint height = size.getHeight(); + + const double autoScaleFactor = window.pData->autoScaleFactor; + + // FIXME anything needed here? +#if 0 + // full viewport size + if (window.pData->autoScaling) + glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); + else + glViewport(0, 0, width, height); +#endif + + // main widget drawing + self->onDisplay(); + + // now draw subwidgets if there are any + selfw->pData->displaySubWidgets(width, height, autoScaleFactor); +} + +// ----------------------------------------------------------------------- + +const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept +{ + return (const GraphicsContext&)graphicsContext; +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DGL + +// ----------------------------------------------------------------------- From f626ea0f7ea1ef09a5fa912825dcc1a16eb751df Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 22:06:30 +0100 Subject: [PATCH 089/159] Define DISTRHO_DEPRECATED_BY and use it in a few places Signed-off-by: falkTX --- dgl/Base.hpp | 6 +++--- dgl/Geometry.hpp | 20 ++++++++++---------- dgl/Image.hpp | 2 +- dgl/OpenGL.hpp | 11 ++++++----- dgl/TopLevelWidget.hpp | 4 +++- dgl/Widget.hpp | 4 +++- dgl/Window.hpp | 8 ++++++-- distrho/src/DistrhoDefines.h | 9 +++++++++ 8 files changed, 41 insertions(+), 23 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index e64a28cf..d726c6fc 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -67,9 +67,9 @@ enum Key { kKeyDelete = 0x7F, // Backwards compatibility with old DPF - kCharBackspace = kKeyBackspace, - kCharEscape = kKeyEscape, - kCharDelete = kKeyDelete, + kCharBackspace DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace, + kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape, + kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete, // Unicode Private Use Area kKeyF1 = 0xE000, diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index cc48904c..078b9c35 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -374,7 +374,7 @@ public: Draw this line using the current OpenGL state. DEPRECATED please use draw(const GraphicsContext&) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); #endif @@ -492,14 +492,14 @@ public: Draw this circle using the current OpenGL state. DEPRECATED please use draw(const GraphicsContext&) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** Draw lines (outline of this circle) using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + DEPRECATED please use draw(const GraphicsContext&,T) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); #endif @@ -585,14 +585,14 @@ public: Draw this triangle using the current OpenGL state. DEPRECATED please use draw(const GraphicsContext&) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** Draw lines (outline of this triangle) using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + DEPRECATED please use draw(const GraphicsContext&,T) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); #endif @@ -804,14 +804,14 @@ public: Draw this rectangle using the current OpenGL state. DEPRECATED please use draw(const GraphicsContext&) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** DEPRECATED Draw lines (outline of this rectangle) using the current OpenGL state. - DEPRECATED please use drawOutline(const GraphicsContext&) instead. + DEPRECATED please use drawOutline(const GraphicsContext&,T) instead. */ - // TODO mark deprecated + DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); private: diff --git a/dgl/Image.hpp b/dgl/Image.hpp index 138a9147..0a3a5db9 100644 --- a/dgl/Image.hpp +++ b/dgl/Image.hpp @@ -22,7 +22,7 @@ START_NAMESPACE_DGL -// TODO mark as deprecated +DISTRHO_DEPRECATED_BY("OpenGLImage") typedef OpenGLImage Image; END_NAMESPACE_DGL diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 8dff7f74..8c0d87d0 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -180,19 +180,19 @@ public: /** Draw this image at (0, 0) point using the current OpenGL context. */ - // TODO mark as deprecated + DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** Draw this image at (x, y) point using the current OpenGL context. */ - // TODO mark as deprecated + DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,int,int)") void drawAt(const int x, const int y); /** Draw this image at position @a pos using the current OpenGL context. */ - // TODO mark as deprecated + DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,const Point&)") void drawAt(const Point& pos); // FIXME this should not be needed @@ -203,8 +203,9 @@ public: /** Get the image type. + DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE. */ - // TODO mark as deprecated + DISTRHO_DEPRECATED GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } private: @@ -216,7 +217,7 @@ private: typedef ImageBaseAboutWindow OpenGLImageAboutWindow; -// TODO deprecated +DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") typedef OpenGLImageAboutWindow ImageAboutWindow; // ----------------------------------------------------------------------- diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index e7ac44d3..165f41bd 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -77,8 +77,10 @@ public: bool keepAspectRatio = false, bool automaticallyScale = false); - // TODO deprecated + DISTRHO_DEPRECATED_BY("getApp()") Application& getParentApp() const noexcept { return getApp(); } + + DISTRHO_DEPRECATED_BY("getWindow()") Window& getParentWindow() const noexcept { return getWindow(); } private: diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 0db175b1..fe01ed1e 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -172,8 +172,10 @@ public: */ virtual void repaint() noexcept; - // TODO deprecated + DISTRHO_DEPRECATED_BY("getApp()") Application& getParentApp() const noexcept { return getApp(); } + + DISTRHO_DEPRECATED_BY("getWindow()") Window& getParentWindow() const noexcept { return getWindow(); } protected: diff --git a/dgl/Window.hpp b/dgl/Window.hpp index f363820a..d4d109bb 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -276,9 +276,13 @@ public: void setTransientWinId(uintptr_t winId); */ - // TODO deprecated + DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } - inline double getScaling() const noexcept { return getScaling(); } + + DISTRHO_DEPRECATED_BY("getScaleFactor()") + inline double getScaling() const noexcept { return getScaleFactor(); } + + DISTRHO_DEPRECATED_BY("runAsModal(bool)") inline void exec(bool blockWait = false) { runAsModal(blockWait); } protected: diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index a2e5522c..a30bc296 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -75,6 +75,15 @@ # define DISTRHO_DEPRECATED #endif +/* Define DISTRHO_DEPRECATED_BY */ +#if defined(__clang__) +# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("", other))) +#elif defined(__GNUC__) +# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("Use " other))) +#else +# define DISTRHO_DEPRECATED_BY(other) DISTRHO_DEPRECATED +#endif + /* Define DISTRHO_SAFE_ASSERT* */ #define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__); #define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; } From 2e947574715a19246f5b16e00c4568210e7f5d31 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 22:25:44 +0100 Subject: [PATCH 090/159] Handle deprecated functions in core code Signed-off-by: falkTX --- Makefile.base.mk | 4 ++ dgl/ImageWidgets.hpp | 10 ++++ dgl/OpenGL.hpp | 2 + dgl/src/ImageBaseWidgets.cpp | 2 +- dgl/src/ImageWidgets.cpp | 12 ++++- dgl/src/OpenGL.cpp | 93 ++++++++++++++++++++---------------- tests/Makefile | 3 -- 7 files changed, 80 insertions(+), 46 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 0ce426fc..5320c54f 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -327,6 +327,10 @@ DGL_FLAGS += -DHAVE_VULKAN VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan) VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan) +ifneq ($(WINDOWS),true) +VULKAN_LIBS += -ldl +endif + endif # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index 245daea9..8a2fe040 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -21,6 +21,12 @@ #include "ImageBaseWidgets.hpp" #include "SubWidget.hpp" +// TODO switch to use templated image type after merging widget-related PRs +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -241,4 +247,8 @@ private: END_NAMESPACE_DGL +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic pop +#endif + #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 8c0d87d0..f7dc1e4e 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -198,6 +198,8 @@ public: // FIXME this should not be needed inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) { loadFromMemory(rawData, Size(w, h), format); }; + inline void draw(const GraphicsContext& context) + { drawAt(context, Point(0, 0)); }; inline void drawAt(const GraphicsContext& context, int x, int y) { drawAt(context, Point(x, y)); }; diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index e01e2b10..45aca12f 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -57,7 +57,7 @@ void ImageBaseAboutWindow::setImage(const ImageType& image) template bool ImageBaseAboutWindow::onKeyboard(const KeyboardEvent& ev) { - if (ev.press && ev.key == kCharEscape) + if (ev.press && ev.key == kKeyEscape) { close(); return true; diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index 371c9cca..37061969 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -20,9 +20,15 @@ #include "Common.hpp" #include "WidgetPrivateData.hpp" -// FIXME make this code more generic and move GL specific bits to OpenGL.cpp +// TODO make this code more generic and move GL specific bits to OpenGL.cpp #include "../OpenGL.hpp" +// TODO switch to use templated image type after merging widget-related PRs +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -946,3 +952,7 @@ bool ImageSwitch::onMouse(const MouseEvent& ev) // ----------------------------------------------------------------------- END_NAMESPACE_DGL + +#if defined(__GNUC__) && (__GNUC__ >= 6) +# pragma GCC diagnostic pop +#endif diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 6e733bc9..4b23758b 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -63,6 +63,7 @@ void Line::draw(const GraphicsContext&, const T width) drawLine(posStart, posEnd); } +// deprecated calls template void Line::draw() { @@ -122,6 +123,7 @@ void Circle::drawOutline(const GraphicsContext&, const T lineWidth) drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); } +// deprecated calls template void Circle::draw() { @@ -178,6 +180,7 @@ void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) drawTriangle(pos1, pos2, pos3, true); } +// deprecated calls template void Triangle::draw() { @@ -244,6 +247,7 @@ void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) drawRectangle(*this, true); } +// deprecated calls template void Rectangle::draw() { @@ -312,6 +316,47 @@ static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) glDisable(GL_TEXTURE_2D); } +static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, const GLuint textureId, bool& setupCalled) +{ + if (textureId == 0 || image.isInvalid()) + return; + + if (! setupCalled) + { + setupOpenGLImage(image, textureId); + setupCalled = true; + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + + glBegin(GL_QUADS); + + { + const int x = pos.getX(); + const int y = pos.getY(); + const int w = static_cast(image.getWidth()); + const int h = static_cast(image.getHeight()); + + glTexCoord2f(0.0f, 0.0f); + glVertex2d(x, y); + + glTexCoord2f(1.0f, 0.0f); + glVertex2d(x+w, y); + + glTexCoord2f(1.0f, 1.0f); + glVertex2d(x+w, y+h); + + glTexCoord2f(0.0f, 1.0f); + glVertex2d(x, y+h); + } + + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + OpenGLImage::OpenGLImage() : ImageBase(), textureId(0), @@ -362,7 +407,7 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, c void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) { - drawAt(pos); + drawOpenGLImage(*this, pos, textureId, setupCalled); } OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept @@ -374,55 +419,20 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept return *this; } +// deprecated calls void OpenGLImage::draw() { - drawAt(Point(0, 0)); + drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); } void OpenGLImage::drawAt(const int x, const int y) { - drawAt(Point(x, y)); + drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); } void OpenGLImage::drawAt(const Point& pos) { - if (textureId == 0 || isInvalid()) - return; - - if (! setupCalled) - { - setupOpenGLImage(*this, textureId); - setupCalled = true; - } - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textureId); - - glBegin(GL_QUADS); - - { - const int x = pos.getX(); - const int y = pos.getY(); - const int w = static_cast(size.getWidth()); - const int h = static_cast(size.getHeight()); - - glTexCoord2f(0.0f, 0.0f); - glVertex2d(x, y); - - glTexCoord2f(1.0f, 0.0f); - glVertex2d(x+w, y); - - glTexCoord2f(1.0f, 1.0f); - glVertex2d(x+w, y+h); - - glTexCoord2f(0.0f, 1.0f); - glVertex2d(x, y+h); - } - - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + drawOpenGLImage(*this, pos, textureId, setupCalled); } // ----------------------------------------------------------------------- @@ -430,7 +440,8 @@ void OpenGLImage::drawAt(const Point& pos) template <> void ImageBaseAboutWindow::onDisplay() { - img.draw(); + const GraphicsContext& context(getGraphicsContext()); + img.draw(context); } template class ImageBaseAboutWindow; diff --git a/tests/Makefile b/tests/Makefile index e1d3e8c3..24c9385b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,9 +4,6 @@ # Created by falkTX # -# debug mode by default -DEBUG=true - include ../Makefile.base.mk # --------------------------------------------------------------------------------------------------------------------- From 37679fe3d75caff0b4a5e8ee965fc80855e8bdae Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 22:28:53 +0100 Subject: [PATCH 091/159] Fix 1 more deprecated use Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 04ff34a8..f7cf2431 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -252,10 +252,10 @@ public: switch (value) { // convert some specials to normal keys - case 1: index = kCharBackspace; break; - case 6: index = kCharEscape; break; - case 7: index = ' '; break; - case 22: index = kCharDelete; break; + case 1: index = kKeyBackspace; break; + case 6: index = kKeyEscape; break; + case 7: index = ' '; break; + case 22: index = kKeyDelete; break; // handle rest of special keys case 40: special = kKeyF1; break; From 030aee3a156b70a64c4865a9711cab7d86648632 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 22:34:18 +0100 Subject: [PATCH 092/159] Fix ImageKnob after last couple of changes Signed-off-by: falkTX --- dgl/OpenGL.hpp | 22 ++++++++++++++++++++++ dgl/src/ImageWidgets.cpp | 5 +++-- dgl/src/OpenGL.cpp | 19 ------------------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index f7dc1e4e..29c9018f 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -117,6 +117,28 @@ struct OpenGLGraphicsContext : GraphicsContext // ----------------------------------------------------------------------- +static inline +GLenum asOpenGLImageFormat(const ImageFormat format) +{ + switch (format) + { + case kImageFormatNull: + break; + case kImageFormatBGR: + return GL_BGR; + case kImageFormatBGRA: + return GL_BGRA; + case kImageFormatRGB: + return GL_RGB; + case kImageFormatRGBA: + return GL_RGBA; + } + + return 0x0; +} + +// ----------------------------------------------------------------------- + /** OpenGL Image class. diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index 37061969..e71e1069 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -358,13 +358,14 @@ void ImageKnob::onDisplay() const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); - const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == GL_BGRA || fImage.getFormat() == GL_RGBA) ? 4 : 3); + const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || + fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(getWidth()), static_cast(getHeight()), 0, - fImage.getFormat(), fImage.getType(), fImage.getRawData() + imageDataOffset); + asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); fIsReady = true; } diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 4b23758b..f96a6ae6 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -270,25 +270,6 @@ template class Rectangle; // ----------------------------------------------------------------------- // OpenGLImage -static GLenum asOpenGLImageFormat(const ImageFormat format) -{ - switch (format) - { - case kImageFormatNull: - break; - case kImageFormatBGR: - return GL_BGR; - case kImageFormatBGRA: - return GL_BGRA; - case kImageFormatRGB: - return GL_RGB; - case kImageFormatRGBA: - return GL_RGBA; - } - - return 0x0; -} - static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) { DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); From 5f58bfbadbd8acff6f32fef8ae2bbfe61627f283 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 22:57:09 +0100 Subject: [PATCH 093/159] Improve backwards compatibility of OpenGLImage Signed-off-by: falkTX --- dgl/ImageBase.hpp | 1 + dgl/OpenGL.hpp | 57 +++++++++++++++++++++++++++++++++++++++------- dgl/src/Cairo.cpp | 6 +++++ dgl/src/OpenGL.cpp | 18 +++++++++++++++ 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp index 1e41bd63..36c2ab28 100644 --- a/dgl/ImageBase.hpp +++ b/dgl/ImageBase.hpp @@ -25,6 +25,7 @@ START_NAMESPACE_DGL enum ImageFormat { kImageFormatNull, + kImageFormatGrayscale, kImageFormatBGR, kImageFormatBGRA, kImageFormatRGB, diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 29c9018f..a9769355 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -117,6 +117,26 @@ struct OpenGLGraphicsContext : GraphicsContext // ----------------------------------------------------------------------- +static inline +ImageFormat asDISTRHOImageFormat(const GLenum format) +{ + switch (format) + { + case GL_LUMINANCE: + return kImageFormatGrayscale; + case GL_BGR: + return kImageFormatBGR; + case GL_BGRA: + return kImageFormatBGRA; + case GL_RGB: + return kImageFormatRGB; + case GL_RGBA: + return kImageFormatRGBA; + } + + return kImageFormatNull; +} + static inline GLenum asOpenGLImageFormat(const ImageFormat format) { @@ -124,6 +144,8 @@ GLenum asOpenGLImageFormat(const ImageFormat format) { case kImageFormatNull: break; + case kImageFormatGrayscale: + return GL_LUMINANCE; case kImageFormatBGR: return GL_BGR; case kImageFormatBGRA: @@ -199,32 +221,51 @@ public: */ OpenGLImage& operator=(const OpenGLImage& image) noexcept; + // FIXME this should not be needed + inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) + { loadFromMemory(rawData, Size(w, h), format); }; + inline void draw(const GraphicsContext& context) + { drawAt(context, Point(0, 0)); }; + inline void drawAt(const GraphicsContext& context, int x, int y) + { drawAt(context, Point(x, y)); }; + + /** + Constructor using raw image data, specifying an OpenGL image format. + @note @a rawData must remain valid for the lifetime of this Image. + DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. + */ + DISTRHO_DEPRECATED_BY("OpenGLImage(const char*,uint,uint,ImageFormat") + explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum format); + + /** + Constructor using raw image data, specifying an OpenGL image format. + @note @a rawData must remain valid for the lifetime of this Image. + DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. + */ + DISTRHO_DEPRECATED_BY("OpenGLImage(const char*,const Size&,ImageFormat") + explicit OpenGLImage(const char* rawData, const Size& size, GLenum format); + /** Draw this image at (0, 0) point using the current OpenGL context. + DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** Draw this image at (x, y) point using the current OpenGL context. + DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. */ DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,int,int)") void drawAt(const int x, const int y); /** Draw this image at position @a pos using the current OpenGL context. + DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. */ DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,const Point&)") void drawAt(const Point& pos); - // FIXME this should not be needed - inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) - { loadFromMemory(rawData, Size(w, h), format); }; - inline void draw(const GraphicsContext& context) - { drawAt(context, Point(0, 0)); }; - inline void drawAt(const GraphicsContext& context, int x, int y) - { drawAt(context, Point(x, y)); }; - /** Get the image type. DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE. diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index e51cda98..46fb232e 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -280,6 +280,8 @@ static cairo_format_t asCairoImageFormat(const ImageFormat format) { case kImageFormatNull: break; + case kImageFormatGrayscale: + return CAIRO_FORMAT_A8; case kImageFormatBGR: case kImageFormatRGB: return CAIRO_FORMAT_RGB24; @@ -366,6 +368,10 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co { case kImageFormatNull: break; + case kImageFormatGrayscale: + // Grayscale to A8 + // TODO + break; case kImageFormatBGR: // BGR8 to CAIRO_FORMAT_RGB24 for (uint h = 0; h < height; ++h) diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index f96a6ae6..43a0ded3 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -401,6 +401,24 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept } // deprecated calls +OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format) + : ImageBase(rawData, width, height, asDISTRHOImageFormat(format)), + textureId(0), + setupCalled(false) +{ + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); +} + +OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const GLenum format) + : ImageBase(rawData, size, asDISTRHOImageFormat(format)), + textureId(0), + setupCalled(false) +{ + glGenTextures(1, &textureId); + DISTRHO_SAFE_ASSERT(textureId != 0); +} + void OpenGLImage::draw() { drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); From 12d7dfb467f94cf7e56651593912289279d8b1fd Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:04:40 +0100 Subject: [PATCH 094/159] Do not require glu.h Signed-off-by: falkTX --- dgl/src/pugl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 8013e9c4..7fdde137 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -30,6 +30,7 @@ START_NAMESPACE_DGL #define PUGL_API #define PUGL_DISABLE_DEPRECATED +#define PUGL_NO_INCLUDE_GLU_H #include "pugl-upstream/include/pugl/pugl.h" PUGL_BEGIN_DECLS From d3591e3e7b1c26e1de19dfce70aaeaba5d9f648c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:13:08 +0100 Subject: [PATCH 095/159] Add VulkanImage stub, enable vulkan for Demo test Signed-off-by: falkTX --- dgl/Cairo.hpp | 4 ++- dgl/Vulkan.hpp | 66 +++++++++++++++++++++++++++++++++++++++++++++- dgl/src/Vulkan.cpp | 34 ++++++++++++++++++++++++ tests/Demo.cpp | 7 +++++ tests/Makefile | 1 + 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index ba97dd4c..bb6f5538 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -91,8 +91,10 @@ public: CairoImage& operator=(const CairoImage& image) noexcept; // FIXME this should not be needed - inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) + inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) { loadFromMemory(rawData, Size(w, h), format); }; + inline void draw(const GraphicsContext& context) + { drawAt(context, Point(0, 0)); }; inline void drawAt(const GraphicsContext& context, int x, int y) { drawAt(context, Point(x, y)); }; diff --git a/dgl/Vulkan.hpp b/dgl/Vulkan.hpp index c636a8a3..d7b73056 100644 --- a/dgl/Vulkan.hpp +++ b/dgl/Vulkan.hpp @@ -17,7 +17,7 @@ #ifndef DGL_VULKAN_HPP_INCLUDED #define DGL_VULKAN_HPP_INCLUDED -#include "Base.hpp" +#include "ImageBase.hpp" #include @@ -34,6 +34,70 @@ struct VulkanGraphicsContext : GraphicsContext // -------------------------------------------------------------------------------------------------------------------- +/** + Vulkan Image class. + + TODO ... + */ +class VulkanImage : public ImageBase +{ +public: + /** + Constructor for a null Image. + */ + VulkanImage(); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + VulkanImage(const char* rawData, uint width, uint height, ImageFormat format); + + /** + Constructor using raw image data. + @note @a rawData must remain valid for the lifetime of this Image. + */ + VulkanImage(const char* rawData, const Size& size, ImageFormat format); + + /** + Constructor using another image data. + */ + VulkanImage(const VulkanImage& image); + + /** + Destructor. + */ + ~VulkanImage() override; + + /** + Load image data from memory. + @note @a rawData must remain valid for the lifetime of this Image. + */ + void loadFromMemory(const char* rawData, + const Size& size, + ImageFormat format = kImageFormatBGRA) noexcept override; + + /** + Draw this image at position @a pos using the graphics context @a context. + */ + void drawAt(const GraphicsContext& context, const Point& pos) override; + + /** + TODO document this. + */ + VulkanImage& operator=(const VulkanImage& image) noexcept; + + // FIXME this should not be needed + inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) + { loadFromMemory(rawData, Size(w, h), format); }; + inline void draw(const GraphicsContext& context) + { drawAt(context, Point(0, 0)); }; + inline void drawAt(const GraphicsContext& context, int x, int y) + { drawAt(context, Point(x, y)); }; +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif diff --git a/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp index ed499222..7ec5db67 100644 --- a/dgl/src/Vulkan.cpp +++ b/dgl/src/Vulkan.cpp @@ -164,6 +164,40 @@ template class Rectangle; template class Rectangle; template class Rectangle; +// ----------------------------------------------------------------------- +// VulkanImage + +VulkanImage::VulkanImage() + : ImageBase() {} + +VulkanImage::VulkanImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) + : ImageBase(rawData, width, height, format) {} + +VulkanImage::VulkanImage(const char* const rawData, const Size& size, const ImageFormat format) + : ImageBase(rawData, size, format) {} + +VulkanImage::VulkanImage(const VulkanImage& image) + : ImageBase(image.rawData, image.size, image.format) {} + +VulkanImage::~VulkanImage() {} + +void VulkanImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept +{ + ImageBase::loadFromMemory(rdata, s, fmt); +} + +void VulkanImage::drawAt(const GraphicsContext&, const Point&) +{ +} + +VulkanImage& VulkanImage::operator=(const VulkanImage& image) noexcept +{ + rawData = image.rawData; + size = image.size; + format = image.format; + return *this; +} + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) diff --git a/tests/Demo.cpp b/tests/Demo.cpp index e86abdcd..f5998fef 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -35,6 +35,10 @@ typedef DGL_NAMESPACE::CairoImage DemoImage; #include "../dgl/OpenGL.hpp" typedef DGL_NAMESPACE::OpenGLImage DemoImage; #endif +#ifdef DGL_VULKAN +#include "../dgl/Vulkan.hpp" +typedef DGL_NAMESPACE::VulkanImage DemoImage; +#endif START_NAMESPACE_DGL @@ -245,6 +249,9 @@ public: #ifdef DGL_OPENGL static constexpr const char* const kExampleWidgetName = "Demo - OpenGL"; #endif +#ifdef DGL_VULKAN + static constexpr const char* const kExampleWidgetName = "Demo - Vulkan"; +#endif DemoWindow(Application& app) : StandaloneWindow(app), diff --git a/tests/Makefile b/tests/Makefile index 24c9385b..dde5918d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -27,6 +27,7 @@ TESTS += Demo.opengl WTESTS += Window.opengl endif ifeq ($(HAVE_VULKAN),true) +TESTS += Demo.vulkan WTESTS = Window.vulkan endif From 634321eb715eb64dd737a7893fa39b71c6d5d863 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:19:54 +0100 Subject: [PATCH 096/159] Add "Base" prefix to Cairo and Nano Widget classes Signed-off-by: falkTX --- dgl/Cairo.hpp | 20 ++++++++++---------- dgl/Image.hpp | 1 - dgl/NanoVG.hpp | 25 ++++++++++++++----------- dgl/SubWidget.hpp | 2 +- dgl/src/Cairo.cpp | 14 +++++++------- dgl/src/NanoVG.cpp | 19 +++++++------------ distrho/src/DistrhoUI.cpp | 6 +++--- 7 files changed, 42 insertions(+), 45 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index bb6f5538..13fac5c4 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -110,37 +110,37 @@ private: CairoWidget, handy class that takes graphics context during onDisplay and passes it in a new function. */ template -class CairoWidget : public BaseWidget +class CairoBaseWidget : public BaseWidget { public: /** Constructor for a CairoSubWidget. @see CreateFlags */ - explicit CairoWidget(Widget* const parentGroupWidget); + explicit CairoBaseWidget(Widget* const parentGroupWidget); /** Constructor for a CairoTopLevelWidget. @see CreateFlags */ - explicit CairoWidget(Window& windowToMapTo); + explicit CairoBaseWidget(Window& windowToMapTo); /** Constructor for a CairoStandaloneWindow without parent window. @see CreateFlags */ - explicit CairoWidget(Application& app); + explicit CairoBaseWidget(Application& app); /** Constructor for a CairoStandaloneWindow with parent window. @see CreateFlags */ - explicit CairoWidget(Application& app, Window& parentWindow); + explicit CairoBaseWidget(Application& app, Window& parentWindow); /** Destructor. */ - virtual ~CairoWidget() {} + virtual ~CairoBaseWidget() {} protected: /** @@ -160,12 +160,12 @@ private: onCairoDisplay(context); } - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoWidget); + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget); }; -typedef CairoWidget CairoSubWidget; -typedef CairoWidget CairoTopLevelWidget; -typedef CairoWidget CairoStandaloneWindow; +typedef CairoBaseWidget CairoSubWidget; +typedef CairoBaseWidget CairoTopLevelWidget; +typedef CairoBaseWidget CairoStandaloneWindow; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/Image.hpp b/dgl/Image.hpp index 0a3a5db9..f6489bdf 100644 --- a/dgl/Image.hpp +++ b/dgl/Image.hpp @@ -17,7 +17,6 @@ #ifndef DGL_IMAGE_HPP_INCLUDED #define DGL_IMAGE_HPP_INCLUDED -#include "ImageBase.hpp" #include "OpenGL.hpp" START_NAMESPACE_DGL diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 1edf16fe..9e0d51a3 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -878,38 +878,38 @@ private: new onNanoDisplay() needs to be overridden instead. */ template -class NanoWidget : public BaseWidget, - public NanoVG +class NanoBaseWidget : public BaseWidget, + public NanoVG { public: /** Constructor for a NanoSubWidget. @see CreateFlags */ - explicit NanoWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS); /** Constructor for a NanoTopLevelWidget. @see CreateFlags */ - explicit NanoWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); /** Constructor for a NanoStandaloneWindow without parent window. @see CreateFlags */ - explicit NanoWidget(Application& app, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS); /** Constructor for a NanoStandaloneWindow with parent window. @see CreateFlags */ - explicit NanoWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS); /** Destructor. */ - virtual ~NanoWidget() {} + virtual ~NanoBaseWidget() {} protected: /** @@ -937,12 +937,15 @@ private: void cancelFrame() {} void endFrame() {} - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget) + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget) }; -typedef NanoWidget NanoSubWidget; -typedef NanoWidget NanoTopLevelWidget; -typedef NanoWidget NanoStandaloneWindow; +typedef NanoBaseWidget NanoSubWidget; +typedef NanoBaseWidget NanoTopLevelWidget; +typedef NanoBaseWidget NanoStandaloneWindow; + +DISTRHO_DEPRECATED_BY("NanoSubWidget") +typedef NanoSubWidget NanoWidget; // ----------------------------------------------------------------------- diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index ce51c0af..1b50ef5a 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -136,7 +136,7 @@ private: struct PrivateData; PrivateData* const pData; friend class Widget; - template friend class NanoWidget; + template friend class NanoBaseWidget; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) }; diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 46fb232e..009da180 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -432,32 +432,32 @@ CairoImage& CairoImage::operator=(const CairoImage& image) noexcept // CairoSubWidget template <> -CairoWidget::CairoWidget(Widget* const parent) +CairoBaseWidget::CairoBaseWidget(Widget* const parent) : SubWidget(parent) {} -template class CairoWidget; +template class CairoBaseWidget; // ----------------------------------------------------------------------- // CairoTopLevelWidget template <> -CairoWidget::CairoWidget(Window& windowToMapTo) +CairoBaseWidget::CairoBaseWidget(Window& windowToMapTo) : TopLevelWidget(windowToMapTo) {} -template class CairoWidget; +template class CairoBaseWidget; // ----------------------------------------------------------------------- // CairoStandaloneWindow template <> -CairoWidget::CairoWidget(Application& app) +CairoBaseWidget::CairoBaseWidget(Application& app) : StandaloneWindow(app) {} template <> -CairoWidget::CairoWidget(Application& app, Window& parentWindow) +CairoBaseWidget::CairoBaseWidget(Application& app, Window& parentWindow) : StandaloneWindow(app, parentWindow) {} -template class CairoWidget; +template class CairoBaseWidget; // ----------------------------------------------------------------------- diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 70f6e503..8f9589a7 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -257,11 +257,6 @@ NanoVG::NanoVG(int flags) fInFrame(false), fIsSubWidget(false) {} -// NanoVG::NanoVG(NanoWidget* groupWidget) -// : fContext(groupWidget->fContext), -// fInFrame(false), -// fIsSubWidget(true) {} - NanoVG::~NanoVG() { DISTRHO_SAFE_ASSERT(! fInFrame); @@ -946,39 +941,39 @@ bool NanoVG::loadSharedResources() // NanoSubWidget template <> -NanoWidget::NanoWidget(Widget* const parent, int flags) +NanoBaseWidget::NanoBaseWidget(Widget* const parent, int flags) : SubWidget(parent), NanoVG(flags) { pData->needsViewportScaling = true; } -template class NanoWidget; +template class NanoBaseWidget; // ----------------------------------------------------------------------- // NanoTopLevelWidget template <> -NanoWidget::NanoWidget(Window& windowToMapTo, int flags) +NanoBaseWidget::NanoBaseWidget(Window& windowToMapTo, int flags) : TopLevelWidget(windowToMapTo), NanoVG(flags) {} -template class NanoWidget; +template class NanoBaseWidget; // ----------------------------------------------------------------------- // NanoStandaloneWindow template <> -NanoWidget::NanoWidget(Application& app, int flags) +NanoBaseWidget::NanoBaseWidget(Application& app, int flags) : StandaloneWindow(app), NanoVG(flags) {} template <> -NanoWidget::NanoWidget(Application& app, Window& parentWindow, int flags) +NanoBaseWidget::NanoBaseWidget(Application& app, Window& parentWindow, int flags) : StandaloneWindow(app, parentWindow), NanoVG(flags) {} -template class NanoWidget; +template class NanoBaseWidget; // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 023a9752..126a5e38 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -213,6 +213,6 @@ END_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- // Possible template data types -template class NanoWidget; -template class NanoWidget; -template class NanoWidget; +// template class NanoBaseWidget; +// template class NanoBaseWidget; +// template class NanoBaseWidget; From a98ae22b7a82a8a8a960fd119f8ef96ef0f09c8c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:26:56 +0100 Subject: [PATCH 097/159] Get rid of Events namespace, put everything inside Widget again Signed-off-by: falkTX --- dgl/Events.hpp | 228 -------------------------- dgl/Widget.hpp | 201 ++++++++++++++++++++++- dgl/src/Common.hpp | 4 +- dgl/src/TopLevelWidgetPrivateData.cpp | 18 +- dgl/src/TopLevelWidgetPrivateData.hpp | 12 +- dgl/src/WidgetPrivateData.cpp | 12 +- dgl/src/WidgetPrivateData.hpp | 12 +- dgl/src/WindowPrivateData.cpp | 22 +-- dgl/src/WindowPrivateData.hpp | 14 +- 9 files changed, 245 insertions(+), 278 deletions(-) delete mode 100644 dgl/Events.hpp diff --git a/dgl/Events.hpp b/dgl/Events.hpp deleted file mode 100644 index dcb468c8..00000000 --- a/dgl/Events.hpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho - * - * 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_EVENTS_HPP_INCLUDED -#define DGL_EVENTS_HPP_INCLUDED - -#include "Geometry.hpp" - -START_NAMESPACE_DGL - -// -------------------------------------------------------------------------------------------------------------------- - -namespace Events -{ - /** - Base event data. - These are the fields present on all Widget events. - - @a mod Currently active keyboard modifiers, @see Modifier. - @a mod Event flags, @see Flag. - @a time Event timestamp (if any). - */ - struct BaseEvent { - uint mod; - uint flags; - uint time; - - /** Constuctor */ - BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} - /** Destuctor */ - virtual ~BaseEvent() noexcept {} - }; - - /** - Keyboard event. - - This event represents low-level key presses and releases. - This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. - - Keys are represented portably as Unicode code points, using the "natural" code point for the key. - The @a key field is the code for the pressed key, without any modifiers applied. - For example, a press or release of the 'A' key will have `key` 97 ('a') - regardless of whether shift or control are being held. - - Alternatively, the raw @a keycode can be used to work directly with physical keys, - but note that this value is not portable and differs between platforms and hardware. - - @a press True if the key was pressed, false if released. - @a key Unicode point of the key pressed. - @a keycode Raw keycode. - @see onKeyboard - */ - struct KeyboardEvent : BaseEvent { - bool press; - uint key; - uint keycode; - - /** Constuctor */ - KeyboardEvent() noexcept - : BaseEvent(), - press(false), - key(0), - keycode(0) {} - }; - - /** - Special keyboard event. - - This event allows the use of keys that do not have unicode points. - Note that some are non-printable keys. - - @a press True if the key was pressed, false if released. - @a key The key pressed. - @see onSpecial - */ - struct SpecialEvent : BaseEvent { - bool press; - Key key; - - /** Constuctor */ - SpecialEvent() noexcept - : BaseEvent(), - press(false), - key(Key(0)) {} - }; - - /** - Character input event. - - This event represents text input, usually as the result of a key press. - The text is given both as a Unicode character code and a UTF-8 string. - - Note that this event is generated by the platform's input system, - so there is not necessarily a direct correspondence between text events and physical key presses. - For example, with some input methods a sequence of several key presses will generate a single character. - - @a keycode Raw key code. - @a character Unicode character code. - @a string UTF-8 string. - @see onCharacterInput - */ - struct CharacterInputEvent : BaseEvent { - uint keycode; - uint character; - char string[8]; - - /** Constuctor */ - CharacterInputEvent() noexcept - : BaseEvent(), - keycode(0), - character(0), - string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} - }; - - /** - Mouse press or release event. - - @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). - @a press True if the button was pressed, false if released. - @a pos The widget-relative coordinates of the pointer. - @see onMouse - */ - struct MouseEvent : BaseEvent { - uint button; - bool press; - Point pos; - - /** Constuctor */ - MouseEvent() noexcept - : BaseEvent(), - button(0), - press(false), - pos(0.0, 0.0) {} - }; - - /** - Mouse motion event. - - @a pos The widget-relative coordinates of the pointer. - @see onMotion - */ - struct MotionEvent : BaseEvent { - Point pos; - - /** Constuctor */ - MotionEvent() noexcept - : BaseEvent(), - pos(0.0, 0.0) {} - }; - - /** - Mouse scroll event. - - The scroll distance is expressed in "lines", - an arbitrary unit that corresponds to a single tick of a detented mouse wheel. - For example, `delta.y` = 1.0 scrolls 1 line up. - Some systems and devices support finer resolution and/or higher values for fast scrolls, - so programs should handle any value gracefully. - - @a pos The widget-relative coordinates of the pointer. - @a delta The scroll distance. - @a direction The direction of the scroll or "smooth". - @see onScroll - */ - struct ScrollEvent : BaseEvent { - Point pos; - Point delta; - ScrollDirection direction; - - /** Constuctor */ - ScrollEvent() noexcept - : BaseEvent(), - pos(0.0, 0.0), - delta(0.0, 0.0), - direction(kScrollSmooth) {} - }; - - /** - Resize event. - @a size The new widget size. - @a oldSize The previous size, may be null. - @see onResize - */ - struct ResizeEvent { - Size size; - Size oldSize; - - /** Constuctor */ - ResizeEvent() noexcept - : size(0, 0), - oldSize(0, 0) {} - }; - - /** - Widget position changed event. - @a pos The new absolute position of the widget. - @a oldPos The previous absolute position of the widget. - @see onPositionChanged - */ - struct PositionChangedEvent { - Point pos; - Point oldPos; - - /** Constuctor */ - PositionChangedEvent() noexcept - : pos(0, 0), - oldPos(0, 0) {} - }; -} - -// -------------------------------------------------------------------------------------------------------------------- - -END_NAMESPACE_DGL - -#endif // DGL_EVENTS_HPP_INCLUDED diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index fe01ed1e..8505c216 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -17,7 +17,7 @@ #ifndef DGL_WIDGET_HPP_INCLUDED #define DGL_WIDGET_HPP_INCLUDED -#include "Events.hpp" +#include "Geometry.hpp" START_NAMESPACE_DGL @@ -29,8 +29,6 @@ class SubWidget; class TopLevelWidget; class Window; -using namespace Events; - // -------------------------------------------------------------------------------------------------------------------- /** @@ -54,6 +52,203 @@ using namespace Events; */ class Widget { +public: + /** + Base event data. + These are the fields present on all Widget events. + + @a mod Currently active keyboard modifiers, @see Modifier. + @a mod Event flags, @see Flag. + @a time Event timestamp (if any). + */ + struct BaseEvent { + uint mod; + uint flags; + uint time; + + /** Constuctor */ + BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} + /** Destuctor */ + virtual ~BaseEvent() noexcept {} + }; + + /** + Keyboard event. + + This event represents low-level key presses and releases. + This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input. + + Keys are represented portably as Unicode code points, using the "natural" code point for the key. + The @a key field is the code for the pressed key, without any modifiers applied. + For example, a press or release of the 'A' key will have `key` 97 ('a') + regardless of whether shift or control are being held. + + Alternatively, the raw @a keycode can be used to work directly with physical keys, + but note that this value is not portable and differs between platforms and hardware. + + @a press True if the key was pressed, false if released. + @a key Unicode point of the key pressed. + @a keycode Raw keycode. + @see onKeyboard + */ + struct KeyboardEvent : BaseEvent { + bool press; + uint key; + uint keycode; + + /** Constuctor */ + KeyboardEvent() noexcept + : BaseEvent(), + press(false), + key(0), + keycode(0) {} + }; + + /** + Special keyboard event. + + This event allows the use of keys that do not have unicode points. + Note that some are non-printable keys. + + @a press True if the key was pressed, false if released. + @a key The key pressed. + @see onSpecial + */ + struct SpecialEvent : BaseEvent { + bool press; + Key key; + + /** Constuctor */ + SpecialEvent() noexcept + : BaseEvent(), + press(false), + key(Key(0)) {} + }; + + /** + Character input event. + + This event represents text input, usually as the result of a key press. + The text is given both as a Unicode character code and a UTF-8 string. + + Note that this event is generated by the platform's input system, + so there is not necessarily a direct correspondence between text events and physical key presses. + For example, with some input methods a sequence of several key presses will generate a single character. + + @a keycode Raw key code. + @a character Unicode character code. + @a string UTF-8 string. + @see onCharacterInput + */ + struct CharacterInputEvent : BaseEvent { + uint keycode; + uint character; + char string[8]; + + /** Constuctor */ + CharacterInputEvent() noexcept + : BaseEvent(), + keycode(0), + character(0), + string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} + }; + + /** + Mouse press or release event. + + @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). + @a press True if the button was pressed, false if released. + @a pos The widget-relative coordinates of the pointer. + @see onMouse + */ + struct MouseEvent : BaseEvent { + uint button; + bool press; + Point pos; + + /** Constuctor */ + MouseEvent() noexcept + : BaseEvent(), + button(0), + press(false), + pos(0.0, 0.0) {} + }; + + /** + Mouse motion event. + + @a pos The widget-relative coordinates of the pointer. + @see onMotion + */ + struct MotionEvent : BaseEvent { + Point pos; + + /** Constuctor */ + MotionEvent() noexcept + : BaseEvent(), + pos(0.0, 0.0) {} + }; + + /** + Mouse scroll event. + + The scroll distance is expressed in "lines", + an arbitrary unit that corresponds to a single tick of a detented mouse wheel. + For example, `delta.y` = 1.0 scrolls 1 line up. + Some systems and devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + + @a pos The widget-relative coordinates of the pointer. + @a delta The scroll distance. + @a direction The direction of the scroll or "smooth". + @see onScroll + */ + struct ScrollEvent : BaseEvent { + Point pos; + Point delta; + ScrollDirection direction; + + /** Constuctor */ + ScrollEvent() noexcept + : BaseEvent(), + pos(0.0, 0.0), + delta(0.0, 0.0), + direction(kScrollSmooth) {} + }; + + /** + Resize event. + @a size The new widget size. + @a oldSize The previous size, may be null. + @see onResize + */ + struct ResizeEvent { + Size size; + Size oldSize; + + /** Constuctor */ + ResizeEvent() noexcept + : size(0, 0), + oldSize(0, 0) {} + }; + + /** + Widget position changed event. + @a pos The new absolute position of the widget. + @a oldPos The previous absolute position of the widget. + @see onPositionChanged + */ + struct PositionChangedEvent { + Point pos; + Point oldPos; + + /** Constuctor */ + PositionChangedEvent() noexcept + : pos(0, 0), + oldPos(0, 0) {} + }; + +private: /** Private constructor, reserved for TopLevelWidget class. */ diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index e4e4d552..6e07c20a 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -42,7 +42,7 @@ struct ButtonImpl { self(s), callback_img(nullptr) {} - bool onMouse(const Events::MouseEvent& ev) + bool onMouse(const Widget::MouseEvent& ev) { // button was released, handle it now if (button != -1 && ! ev.press) @@ -83,7 +83,7 @@ struct ButtonImpl { return false; } - bool onMotion(const Events::MotionEvent& ev) + bool onMotion(const Widget::MotionEvent& ev) { // keep pressed if (button != -1) diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 433dcaa9..540d0149 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -37,7 +37,7 @@ TopLevelWidget::PrivateData::~PrivateData() window.pData->topLevelWidget = nullptr; } -bool TopLevelWidget::PrivateData::keyboardEvent(const Events::KeyboardEvent& ev) +bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) { // give top-level widget chance to catch this event first if (self->onKeyboard(ev)) @@ -47,7 +47,7 @@ bool TopLevelWidget::PrivateData::keyboardEvent(const Events::KeyboardEvent& ev) return selfw->pData->giveKeyboardEventForSubWidgets(ev); } -bool TopLevelWidget::PrivateData::specialEvent(const Events::SpecialEvent& ev) +bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev) { // give top-level widget chance to catch this event first if (self->onSpecial(ev)) @@ -57,7 +57,7 @@ bool TopLevelWidget::PrivateData::specialEvent(const Events::SpecialEvent& ev) return selfw->pData->giveSpecialEventForSubWidgets(ev); } -bool TopLevelWidget::PrivateData::characterInputEvent(const Events::CharacterInputEvent& ev) +bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev) { // give top-level widget chance to catch this event first if (self->onCharacterInput(ev)) @@ -67,9 +67,9 @@ bool TopLevelWidget::PrivateData::characterInputEvent(const Events::CharacterInp return selfw->pData->giveCharacterInputEventForSubWidgets(ev); } -bool TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) +bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev) { - Events::MouseEvent rev = ev; + MouseEvent rev = ev; if (window.pData->autoScaling) { @@ -87,9 +87,9 @@ bool TopLevelWidget::PrivateData::mouseEvent(const Events::MouseEvent& ev) return selfw->pData->giveMouseEventForSubWidgets(rev); } -bool TopLevelWidget::PrivateData::motionEvent(const Events::MotionEvent& ev) +bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev) { - Events::MotionEvent rev = ev; + MotionEvent rev = ev; if (window.pData->autoScaling) { @@ -107,9 +107,9 @@ bool TopLevelWidget::PrivateData::motionEvent(const Events::MotionEvent& ev) return selfw->pData->giveMotionEventForSubWidgets(rev); } -bool TopLevelWidget::PrivateData::scrollEvent(const Events::ScrollEvent& ev) +bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev) { - Events::ScrollEvent rev = ev; + ScrollEvent rev = ev; if (window.pData->autoScaling) { diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index b3225c32..6c882868 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -33,12 +33,12 @@ struct TopLevelWidget::PrivateData { explicit PrivateData(TopLevelWidget* const s, Window& w); ~PrivateData(); void display(); - bool keyboardEvent(const Events::KeyboardEvent& ev); - bool specialEvent(const Events::SpecialEvent& ev); - bool characterInputEvent(const Events::CharacterInputEvent& ev); - bool mouseEvent(const Events::MouseEvent& ev); - bool motionEvent(const Events::MotionEvent& ev); - bool scrollEvent(const Events::ScrollEvent& ev); + bool keyboardEvent(const KeyboardEvent& ev); + bool specialEvent(const SpecialEvent& ev); + bool characterInputEvent(const CharacterInputEvent& ev); + bool mouseEvent(const MouseEvent& ev); + bool motionEvent(const MotionEvent& ev); + bool scrollEvent(const ScrollEvent& ev); void fallbackOnResize(); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index b641dca9..0b016490 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -69,7 +69,7 @@ void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, // ----------------------------------------------------------------------- -bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const Events::KeyboardEvent& ev) +bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev) { if (! visible) return false; @@ -87,7 +87,7 @@ bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const Events::KeyboardE return false; } -bool Widget::PrivateData::giveSpecialEventForSubWidgets(const Events::SpecialEvent& ev) +bool Widget::PrivateData::giveSpecialEventForSubWidgets(const SpecialEvent& ev) { if (! visible) return false; @@ -105,7 +105,7 @@ bool Widget::PrivateData::giveSpecialEventForSubWidgets(const Events::SpecialEve return false; } -bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const Events::CharacterInputEvent& ev) +bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev) { if (! visible) return false; @@ -123,7 +123,7 @@ bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const Events::Cha return false; } -bool Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) +bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) { if (! visible) return false; @@ -150,7 +150,7 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(Events::MouseEvent& ev) return false; } -bool Widget::PrivateData::giveMotionEventForSubWidgets(Events::MotionEvent& ev) +bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) { if (! visible) return false; @@ -177,7 +177,7 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(Events::MotionEvent& ev) return false; } -bool Widget::PrivateData::giveScrollEventForSubWidgets(Events::ScrollEvent& ev) +bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) { if (! visible) return false; diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index 81471f47..ab388522 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -43,12 +43,12 @@ struct Widget::PrivateData { void displaySubWidgets(uint width, uint height, double autoScaleFactor); - bool giveKeyboardEventForSubWidgets(const Events::KeyboardEvent& ev); - bool giveSpecialEventForSubWidgets(const Events::SpecialEvent& ev); - bool giveCharacterInputEventForSubWidgets(const Events::CharacterInputEvent& ev); - bool giveMouseEventForSubWidgets(Events::MouseEvent& ev); - bool giveMotionEventForSubWidgets(Events::MotionEvent& ev); - bool giveScrollEventForSubWidgets(Events::ScrollEvent& ev); + bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev); + bool giveSpecialEventForSubWidgets(const SpecialEvent& ev); + bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev); + bool giveMouseEventForSubWidgets(MouseEvent& ev); + bool giveMotionEventForSubWidgets(MotionEvent& ev); + bool giveScrollEventForSubWidgets(ScrollEvent& ev); static TopLevelWidget* findTopLevelWidget(Widget* const w); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index fc5413ab..509de098 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -533,7 +533,7 @@ void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) self->onFocus(focus, mode); } -void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) +void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) { DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); @@ -546,7 +546,7 @@ void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) #endif } -void Window::PrivateData::onPuglSpecial(const Events::SpecialEvent& ev) +void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev) { DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key); @@ -559,7 +559,7 @@ void Window::PrivateData::onPuglSpecial(const Events::SpecialEvent& ev) #endif } -void Window::PrivateData::onPuglText(const Events::CharacterInputEvent& ev) +void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) { DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); @@ -572,7 +572,7 @@ void Window::PrivateData::onPuglText(const Events::CharacterInputEvent& ev) #endif } -void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) +void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) { DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); @@ -585,7 +585,7 @@ void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) #endif } -void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) +void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) { DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); @@ -598,7 +598,7 @@ void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) #endif } -void Window::PrivateData::onPuglScroll(const Events::ScrollEvent& ev) +void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) { DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); @@ -684,7 +684,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu { // unused x, y, xRoot, yRoot (double) // TODO special keys? - Events::KeyboardEvent ev; + Widget::KeyboardEvent ev; ev.mod = event->key.state; ev.flags = event->key.flags; ev.time = static_cast(event->key.time * 1000.0 + 0.5); @@ -701,7 +701,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_TEXT: { // unused x, y, xRoot, yRoot (double) - Events::CharacterInputEvent ev; + Widget::CharacterInputEvent ev; ev.mod = event->text.state; ev.flags = event->text.flags; ev.time = static_cast(event->text.time * 1000.0 + 0.5); @@ -724,7 +724,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Mouse button released, a #PuglEventButton case PUGL_BUTTON_RELEASE: { - Events::MouseEvent ev; + Widget::MouseEvent ev; ev.mod = event->button.state; ev.flags = event->button.flags; ev.time = static_cast(event->button.time * 1000.0 + 0.5); @@ -738,7 +738,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Pointer moved, a #PuglEventMotion case PUGL_MOTION: { - Events::MotionEvent ev; + Widget::MotionEvent ev; ev.mod = event->motion.state; ev.flags = event->motion.flags; ev.time = static_cast(event->motion.time * 1000.0 + 0.5); @@ -750,7 +750,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Scrolled, a #PuglEventScroll case PUGL_SCROLL: { - Events::ScrollEvent ev; + Widget::ScrollEvent ev; ev.mod = event->scroll.state; ev.flags = event->scroll.flags; ev.time = static_cast(event->scroll.time * 1000.0 + 0.5); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index b4fc5cb9..75bce1f6 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -18,7 +18,7 @@ #define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED #include "../Window.hpp" -#include "../Events.hpp" +#include "../Widget.hpp" #include "ApplicationPrivateData.hpp" #include "pugl.hpp" @@ -149,12 +149,12 @@ struct Window::PrivateData : IdleCallback { void onPuglExpose(); void onPuglClose(); void onPuglFocus(bool focus, CrossingMode mode); - void onPuglKey(const Events::KeyboardEvent& ev); - void onPuglSpecial(const Events::SpecialEvent& ev); - void onPuglText(const Events::CharacterInputEvent& ev); - void onPuglMouse(const Events::MouseEvent& ev); - void onPuglMotion(const Events::MotionEvent& ev); - void onPuglScroll(const Events::ScrollEvent& ev); + void onPuglKey(const Widget::KeyboardEvent& ev); + void onPuglSpecial(const Widget::SpecialEvent& ev); + void onPuglText(const Widget::CharacterInputEvent& ev); + void onPuglMouse(const Widget::MouseEvent& ev); + void onPuglMotion(const Widget::MotionEvent& ev); + void onPuglScroll(const Widget::ScrollEvent& ev); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); From 44014bb9345bbb58b1267b7b59469e1db4443757 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:27:28 +0100 Subject: [PATCH 098/159] Add symlink file needed for macOS build Signed-off-by: falkTX --- dgl/src/pugl.mm | 1 + 1 file changed, 1 insertion(+) create mode 120000 dgl/src/pugl.mm diff --git a/dgl/src/pugl.mm b/dgl/src/pugl.mm new file mode 120000 index 00000000..8ea74827 --- /dev/null +++ b/dgl/src/pugl.mm @@ -0,0 +1 @@ +pugl.cpp \ No newline at end of file From b9be2e2e592e625849511af7deb7aff14816ec7d Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:33:59 +0100 Subject: [PATCH 099/159] Allow to build VST without vestige header (aka "real" SDK) Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index f7cf2431..1549a8c1 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -30,14 +30,16 @@ # define __cdecl #endif -#define VESTIGE_HEADER +#ifndef VESTIGE_HEADER +# define VESTIGE_HEADER 1 +#endif #define VST_FORCE_DEPRECATED 0 #include #include #include -#ifdef VESTIGE_HEADER +#if VESTIGE_HEADER # include "vestige/vestige.h" #define effFlagsProgramChunks (1 << 5) #define effSetProgramName 4 From 841f7c66213149a0c82551b907bc2cdcd3e7a0ef Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 May 2021 23:35:35 +0100 Subject: [PATCH 100/159] Fix demo test build Signed-off-by: falkTX --- tests/widgets/ExampleColorWidget.hpp | 2 +- tests/widgets/ExampleRectanglesWidget.hpp | 2 +- tests/widgets/ExampleShapesWidget.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/widgets/ExampleColorWidget.hpp b/tests/widgets/ExampleColorWidget.hpp index e47c923f..efcea92d 100644 --- a/tests/widgets/ExampleColorWidget.hpp +++ b/tests/widgets/ExampleColorWidget.hpp @@ -118,7 +118,7 @@ protected: bgSmall.draw(context); } - void onResize(const ResizeEvent& ev) override + void onResize(const Widget::ResizeEvent& ev) override { const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp index 9dee758a..7ce85581 100644 --- a/tests/widgets/ExampleRectanglesWidget.hpp +++ b/tests/widgets/ExampleRectanglesWidget.hpp @@ -113,7 +113,7 @@ protected: } } - bool onMouse(const MouseEvent& ev) override + bool onMouse(const Widget::MouseEvent& ev) override { if (ev.button != 1 || ! ev.press) return false; diff --git a/tests/widgets/ExampleShapesWidget.hpp b/tests/widgets/ExampleShapesWidget.hpp index d5f213fb..1186a749 100644 --- a/tests/widgets/ExampleShapesWidget.hpp +++ b/tests/widgets/ExampleShapesWidget.hpp @@ -85,7 +85,7 @@ protected: cir.drawOutline(context, 2); } - void onResize(const ResizeEvent& ev) override + void onResize(const Widget::ResizeEvent& ev) override { const int width = ev.size.getWidth(); const int height = ev.size.getHeight(); From 21a0c86bce9795af2f266c87a56127946aa446d3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 00:25:03 +0100 Subject: [PATCH 101/159] Correct usage of VST handlePluginKeyEvent; Fix compiler warnings Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 8 +- dgl/src/WindowPrivateData.hpp | 5 +- distrho/src/DistrhoPluginVST.cpp | 117 ++++++++++++++++++------------ distrho/src/DistrhoUIInternal.hpp | 8 +- 4 files changed, 83 insertions(+), 55 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 509de098..e7ed92a1 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -66,7 +66,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaleFactor(1.0), minWidth(0), minHeight(0), - modal(this) + modal() { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } @@ -85,7 +85,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c autoScaleFactor(1.0), minWidth(0), minHeight(0), - modal(this, ppData) + modal(ppData) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -108,7 +108,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), - modal(this) + modal() { if (isEmbed) { @@ -142,7 +142,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), - modal(this) + modal() { if (isEmbed) { diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 75bce1f6..47dbf9ab 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -70,19 +70,18 @@ struct Window::PrivateData : IdleCallback { /** Modal window setup. */ struct Modal { -// PrivateData* self; // pointer to PrivateData this Modal class belongs to PrivateData* parent; // parent of this window (so we can become modal) PrivateData* child; // child window to give focus to when modal mode is enabled bool enabled; // wherever modal mode is enabled (only possible if parent != null) /** Constructor for a non-modal window. */ - Modal(PrivateData* const s) noexcept + Modal() noexcept : parent(nullptr), child(nullptr), enabled(false) {} /** Constructor for a modal window (with a parent). */ - Modal(PrivateData* const s, PrivateData* const p) noexcept + Modal(PrivateData* const p) noexcept : parent(p), child(nullptr), enabled(false) {} diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 1549a8c1..d1996be2 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -183,20 +183,8 @@ public: nullptr, // TODO file request nullptr, plugin->getInstancePointer(), - scaleFactor), - fShouldCaptureVstKeys(false) + scaleFactor) { - // FIXME only needed for windows? -//#ifdef DISTRHO_OS_WINDOWS - char strBuf[0xff+1]; - std::memset(strBuf, 0, sizeof(char)*(0xff+1)); - hostCallback(audioMasterGetProductString, 0, 0, strBuf); - d_stdout("Plugin UI running in '%s'", strBuf); - - // TODO make a white-list of needed hosts - if (/*std::strcmp(strBuf, "") == 0*/ true) - fShouldCaptureVstKeys = true; -//#endif } // ------------------------------------------------------------------- @@ -243,9 +231,6 @@ public: int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value) { # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - if (! fShouldCaptureVstKeys) - return 0; - d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value); using namespace DGL_NAMESPACE; @@ -253,44 +238,87 @@ public: int special = 0; switch (value) { - // convert some specials to normal keys + // convert some VST special values to normal keys case 1: index = kKeyBackspace; break; + case 2: index = '\t'; break; + // 3 clear + case 4: index = '\r'; break; case 6: index = kKeyEscape; break; case 7: index = ' '; break; + // 8 next + // 17 select + // 18 print + case 19: index = '\n'; break; + // 20 snapshot case 22: index = kKeyDelete; break; + // 23 help + case 57: index = '='; break; + + // numpad stuff follows + case 24: index = '0'; break; + case 25: index = '1'; break; + case 26: index = '2'; break; + case 27: index = '3'; break; + case 28: index = '4'; break; + case 29: index = '5'; break; + case 30: index = '6'; break; + case 31: index = '7'; break; + case 32: index = '8'; break; + case 33: index = '9'; break; + case 34: index = '*'; break; + case 35: index = '+'; break; + // 36 separator + case 37: index = '-'; break; + case 38: index = '.'; break; + case 39: index = '/'; break; // handle rest of special keys - case 40: special = kKeyF1; break; - case 41: special = kKeyF2; break; - case 42: special = kKeyF3; break; - case 43: special = kKeyF4; break; - case 44: special = kKeyF5; break; - case 45: special = kKeyF6; break; - case 46: special = kKeyF7; break; - case 47: special = kKeyF8; break; - case 48: special = kKeyF9; break; - case 49: special = kKeyF10; break; - case 50: special = kKeyF11; break; - case 51: special = kKeyF12; break; - case 11: special = kKeyLeft; break; - case 12: special = kKeyUp; break; - case 13: special = kKeyRight; break; - case 14: special = kKeyDown; break; - case 15: special = kKeyPageUp; break; - case 16: special = kKeyPageDown; break; - case 10: special = kKeyHome; break; - case 9: special = kKeyEnd; break; - case 21: special = kKeyInsert; break; - case 54: special = kKeyShift; break; - case 55: special = kKeyControl; break; - case 56: special = kKeyAlt; break; + /* these special keys are missing: + - kKeySuper + - kKeyMenu + - kKeyCapsLock + - kKeyPrintScreen + */ + case 40: special = kKeyF1; break; + case 41: special = kKeyF2; break; + case 42: special = kKeyF3; break; + case 43: special = kKeyF4; break; + case 44: special = kKeyF5; break; + case 45: special = kKeyF6; break; + case 46: special = kKeyF7; break; + case 47: special = kKeyF8; break; + case 48: special = kKeyF9; break; + case 49: special = kKeyF10; break; + case 50: special = kKeyF11; break; + case 51: special = kKeyF12; break; + case 11: special = kKeyLeft; break; + case 12: special = kKeyUp; break; + case 13: special = kKeyRight; break; + case 14: special = kKeyDown; break; + case 15: special = kKeyPageUp; break; + case 16: special = kKeyPageDown; break; + case 10: special = kKeyHome; break; + case 9: special = kKeyEnd; break; + case 21: special = kKeyInsert; break; + case 54: special = kKeyShift; break; + case 55: special = kKeyControl; break; + case 56: special = kKeyAlt; break; + case 52: special = kKeyNumLock; break; + case 53: special = kKeyScrollLock; break; + case 5: special = kKeyPause; break; } if (special != 0) - return fUI.handlePluginSpecial(down, static_cast(special)); + { + fUI.handlePluginSpecial(down, static_cast(special)); + return 1; + } - if (index >= 0) - return fUI.handlePluginKeyboard(down, static_cast(index)); + if (index > 0) + { + fUI.handlePluginKeyboard(down, static_cast(index)); + return 1; + } # endif return 0; @@ -360,7 +388,6 @@ private: // Plugin UI UIExporter fUI; - bool fShouldCaptureVstKeys; // ------------------------------------------------------------------- // Callbacks diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 786bca7c..511addde 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -408,7 +408,7 @@ public: fChangingSize = false; } - void setWindowTransientWinId(const uintptr_t winId) + void setWindowTransientWinId(const uintptr_t /*winId*/) { #if 0 /* TODO */ glWindow.setTransientWinId(winId); @@ -422,18 +422,20 @@ public: return ! glApp.isQuiting(); } - bool handlePluginKeyboard(const bool press, const uint key) + bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/) { #if 0 /* TODO */ return glWindow.handlePluginKeyboard(press, key); #endif + return false; } - bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key) + bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/) { #if 0 /* TODO */ return glWindow.handlePluginSpecial(press, key); #endif + return false; } #endif From bb0c16cfdb9d477cdc4485482f8962559b3e85d2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 00:48:53 +0100 Subject: [PATCH 102/159] VST: Store keyboard modifiers from host key events Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 32 +++++++++++++++++++++++++++---- distrho/src/DistrhoUIInternal.hpp | 4 ++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index d1996be2..1f00e6f0 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -183,7 +183,8 @@ public: nullptr, // TODO file request nullptr, plugin->getInstancePointer(), - scaleFactor) + scaleFactor), + fKeyboardModifiers(0) { } @@ -275,7 +276,6 @@ public: // handle rest of special keys /* these special keys are missing: - kKeySuper - - kKeyMenu - kKeyCapsLock - kKeyPrintScreen */ @@ -303,20 +303,43 @@ public: case 54: special = kKeyShift; break; case 55: special = kKeyControl; break; case 56: special = kKeyAlt; break; + case 58: special = kKeyMenu; break; case 52: special = kKeyNumLock; break; case 53: special = kKeyScrollLock; break; case 5: special = kKeyPause; break; } + switch (special) + { + case kKeyShift: + if (down) + fKeyboardModifiers |= kModifierShift; + else + fKeyboardModifiers &= ~kModifierShift; + break; + case kKeyControl: + if (down) + fKeyboardModifiers |= kModifierControl; + else + fKeyboardModifiers &= ~kModifierControl; + break; + case kKeyAlt: + if (down) + fKeyboardModifiers |= kModifierAlt; + else + fKeyboardModifiers &= ~kModifierAlt; + break; + } + if (special != 0) { - fUI.handlePluginSpecial(down, static_cast(special)); + fUI.handlePluginSpecial(down, static_cast(special), fKeyboardModifiers); return 1; } if (index > 0) { - fUI.handlePluginKeyboard(down, static_cast(index)); + fUI.handlePluginKeyboard(down, static_cast(index), fKeyboardModifiers); return 1; } # endif @@ -388,6 +411,7 @@ private: // Plugin UI UIExporter fUI; + uint16_t fKeyboardModifiers; // ------------------------------------------------------------------- // Callbacks diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 511addde..3a04a41b 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -422,7 +422,7 @@ public: return ! glApp.isQuiting(); } - bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/) + bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/) { #if 0 /* TODO */ return glWindow.handlePluginKeyboard(press, key); @@ -430,7 +430,7 @@ public: return false; } - bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/) + bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/, const uint16_t /*mods*/) { #if 0 /* TODO */ return glWindow.handlePluginSpecial(press, key); From 432cb7c962afdab5bc5811d80f504c51c90e7ba5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 00:54:41 +0100 Subject: [PATCH 103/159] Cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 1f00e6f0..1b621c10 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -229,9 +229,9 @@ public: } # endif +# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value) { -# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value); using namespace DGL_NAMESPACE; @@ -342,10 +342,10 @@ public: fUI.handlePluginKeyboard(down, static_cast(index), fKeyboardModifiers); return 1; } -# endif return 0; } +# endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // ------------------------------------------------------------------- @@ -714,6 +714,7 @@ public: fVstUI->idle(); break; +# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI case effEditKeyDown: if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(true, index, value); @@ -723,6 +724,7 @@ public: if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(false, index, value); break; +# endif #endif // DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_WANT_STATE From 38550f31e843d1b97975f6c22f92f8535f089f41 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 01:05:32 +0100 Subject: [PATCH 104/159] Cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 1b621c10..e5bc91c8 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -130,9 +130,11 @@ private: // ----------------------------------------------------------------------- -class ParameterCheckHelper +struct ParameterCheckHelper { -public: + bool* parameterChecks; + float* parameterValues; + ParameterCheckHelper() : parameterChecks(nullptr), parameterValues(nullptr) {} @@ -151,9 +153,6 @@ public: } } - bool* parameterChecks; - float* parameterValues; - #if DISTRHO_PLUGIN_WANT_STATE virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; #endif @@ -350,16 +349,16 @@ public: // ------------------------------------------------------------------- protected: - intptr_t hostCallback(const int32_t opcode, - const int32_t index = 0, - const intptr_t value = 0, - void* const ptr = nullptr, - const float opt = 0.0f) + inline intptr_t hostCallback(const int32_t opcode, + const int32_t index = 0, + const intptr_t value = 0, + void* const ptr = nullptr, + const float opt = 0.0f) const { return fAudioMaster(fEffect, opcode, index, value, ptr, opt); } - void editParameter(const uint32_t index, const bool started) + void editParameter(const uint32_t index, const bool started) const { hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); } @@ -708,7 +707,6 @@ public: } break; - //case effIdle: case effEditIdle: if (fVstUI != nullptr) fVstUI->idle(); From 8b683996dd68baea6aa7300e1efcd65ec7441587 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 01:25:39 +0100 Subject: [PATCH 105/159] Add example code to getBackgroundColor and getForegroundColor Signed-off-by: falkTX --- distrho/DistrhoUI.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 2925d7dd..8cb4bec5 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -78,12 +78,26 @@ public: /** Get the color used for UI background (i.e. window color) in RGBA format. Returns 0 by default, in case of error or lack of host support. + + The following example code can be use to extract individual colors: + ``` + const int red = (bgColor >> 24) & 0xff; + const int green = (bgColor >> 16) & 0xff; + const int blue = (bgColor >> 8) & 0xff; + ``` */ uint getBackgroundColor() const noexcept; /** Get the color used for UI foreground (i.e. text color) in RGBA format. Returns 0xffffffff by default, in case of error or lack of host support. + + The following example code can be use to extract individual colors: + ``` + const int red = (fgColor >> 24) & 0xff; + const int green = (fgColor >> 16) & 0xff; + const int blue = (fgColor >> 8) & 0xff; + ``` */ uint getForegroundColor() const noexcept; From 94dee0ca4b2e2b66926129d731d3fd0c6502bf15 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 04:11:25 +0100 Subject: [PATCH 106/159] Fix a typo; set pugl backend as stub if it ends up being null --- dgl/src/pugl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index f83efb78..d7dac469 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -182,9 +182,11 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) #ifdef DGL_OPENGL puglSetBackend(view, puglGlBackend()); #endif -#ifdef DGL_Vulkan +#ifdef DGL_VULKAN puglSetBackend(view, puglVulkanBackend()); #endif + if (view->backend == nullptr) + puglSetBackend(view, puglStubBackend()); } // -------------------------------------------------------------------------------------------------------------------- From 1f711ed1077bd1b4810cbe3336573d8182b71ea8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 04:39:19 +0100 Subject: [PATCH 107/159] Make ImageButton cairo compatible --- dgl/Cairo.hpp | 1 + dgl/ImageBaseWidgets.hpp | 34 +++++++++++++ dgl/ImageWidgets.hpp | 32 ------------- dgl/OpenGL.hpp | 4 ++ dgl/src/Cairo.cpp | 2 + dgl/src/Common.hpp | 11 +++-- dgl/src/ImageBaseWidgets.cpp | 92 ++++++++++++++++++++++++++++++++++++ dgl/src/ImageWidgets.cpp | 85 +-------------------------------- dgl/src/OpenGL.cpp | 1 + 9 files changed, 141 insertions(+), 121 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 13fac5c4..525c1496 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -170,6 +170,7 @@ typedef CairoBaseWidget CairoStandaloneWindow; // -------------------------------------------------------------------------------------------------------------------- typedef ImageBaseAboutWindow CairoImageAboutWindow; +typedef ImageBaseButton CairoImageButton; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 3fef7e63..6ae520db 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -18,6 +18,7 @@ #define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED #include "StandaloneWindow.hpp" +#include "SubWidget.hpp" START_NAMESPACE_DGL @@ -48,6 +49,39 @@ private: // -------------------------------------------------------------------------------------------------------------------- +template +class ImageBaseButton : public SubWidget +{ +public: + class Callback + { + public: + virtual ~Callback() {} + virtual void imageButtonClicked(ImageBaseButton* imageButton, int button) = 0; + }; + + explicit ImageBaseButton(Widget* parentWidget, const ImageType& image); + explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown); + explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown); + + ~ImageBaseButton() override; + + void setCallback(Callback* callback) noexcept; + +protected: + void onDisplay() override; + bool onMouse(const MouseEvent&) override; + bool onMotion(const MotionEvent&) override; + +private: + struct PrivateData; + PrivateData* const pData; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseButton) +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index 8a2fe040..3571d8da 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -31,38 +31,6 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -class ImageButton : public SubWidget -{ -public: - class Callback - { - public: - virtual ~Callback() {} - virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0; - }; - - explicit ImageButton(Widget* parentWidget, const Image& image); - explicit ImageButton(Widget* parentWidget, const Image& imageNormal, const Image& imageDown); - explicit ImageButton(Widget* parentWidget, const Image& imageNormal, const Image& imageHover, const Image& imageDown); - - ~ImageButton() override; - - void setCallback(Callback* callback) noexcept; - -protected: - void onDisplay() override; - bool onMouse(const MouseEvent&) override; - bool onMotion(const MotionEvent&) override; - -private: - struct PrivateData; - PrivateData* const pData; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageButton) -}; - -// ----------------------------------------------------------------------- - class ImageKnob : public SubWidget { public: diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index a9769355..e3fa37e1 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -281,10 +281,14 @@ private: // ----------------------------------------------------------------------- typedef ImageBaseAboutWindow OpenGLImageAboutWindow; +typedef ImageBaseButton OpenGLImageButton; DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") typedef OpenGLImageAboutWindow ImageAboutWindow; +DISTRHO_DEPRECATED_BY("OpenGLImageButton") +typedef OpenGLImageButton ImageButton; + // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 009da180..0671345e 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -16,6 +16,7 @@ #include "../Cairo.hpp" #include "../Color.hpp" +#include "../ImageBaseWidgets.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" @@ -468,6 +469,7 @@ void ImageBaseAboutWindow::onDisplay() } template class ImageBaseAboutWindow; +template class ImageBaseButton; // ----------------------------------------------------------------------- diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index 6e07c20a..5bbac85b 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -17,12 +17,13 @@ #ifndef DGL_COMMON_HPP_INCLUDED #define DGL_COMMON_HPP_INCLUDED -#include "../ImageWidgets.hpp" +#include "../ImageBaseWidgets.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- +template struct ButtonImpl { enum State { kStateNormal = 0, @@ -32,11 +33,11 @@ struct ButtonImpl { int button; int state; - SubWidget* self; + ImageBaseButton* const self; - ImageButton::Callback* callback_img; + typename ImageBaseButton::Callback* callback_img; - ButtonImpl(SubWidget* const s) noexcept + explicit ButtonImpl(ImageBaseButton* const s) noexcept : button(-1), state(kStateNormal), self(s), @@ -66,7 +67,7 @@ struct ButtonImpl { self->repaint(); if (callback_img != nullptr) - callback_img->imageButtonClicked((ImageButton*)self, button2); + callback_img->imageButtonClicked(self, button2); return true; } diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 45aca12f..1eb8bbd1 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -15,6 +15,7 @@ */ #include "../ImageBaseWidgets.hpp" +#include "Common.hpp" START_NAMESPACE_DGL @@ -88,4 +89,95 @@ void ImageBaseAboutWindow::onReshape(uint width, uint height) // -------------------------------------------------------------------------------------------------------------------- +template +struct ImageBaseButton::PrivateData { + ButtonImpl impl; + ImageType imageNormal; + ImageType imageHover; + ImageType imageDown; + + PrivateData(ImageBaseButton* const s, const ImageType& normal, const ImageType& hover, const ImageType& down) + : impl(s), + imageNormal(normal), + imageHover(hover), + imageDown(down) {} + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template +ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& image) + : SubWidget(parentWidget), + pData(new PrivateData(this, image, image, image)) +{ + setSize(image.getSize()); +} + +template +ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) + : SubWidget(parentWidget), + pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) +{ + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + + setSize(imageNormal.getSize()); +} + +template +ImageBaseButton::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) + : SubWidget(parentWidget), + pData(new PrivateData(this, imageNormal, imageHover, imageDown)) +{ + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); + + setSize(imageNormal.getSize()); +} + +template +ImageBaseButton::~ImageBaseButton() +{ + delete pData; +} + +template +void ImageBaseButton::setCallback(Callback* callback) noexcept +{ + pData->impl.callback_img = callback; +} + +template +void ImageBaseButton::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + + switch (pData->impl.state) + { + case ButtonImpl::kStateDown: + pData->imageDown.draw(context); + break; + case ButtonImpl::kStateHover: + pData->imageHover.draw(context); + break; + default: + pData->imageNormal.draw(context); + break; + } +} + +template +bool ImageBaseButton::onMouse(const MouseEvent& ev) +{ + return pData->impl.onMouse(ev); +} + +template +bool ImageBaseButton::onMotion(const MotionEvent& ev) +{ + return pData->impl.onMotion(ev); +} + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp index e71e1069..41d4f1a7 100644 --- a/dgl/src/ImageWidgets.cpp +++ b/dgl/src/ImageWidgets.cpp @@ -14,10 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "../Image.hpp" -#include "../ImageBaseWidgets.hpp" - -#include "Common.hpp" +#include "../ImageWidgets.hpp" #include "WidgetPrivateData.hpp" // TODO make this code more generic and move GL specific bits to OpenGL.cpp @@ -33,86 +30,6 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -struct ImageButton::PrivateData { - ButtonImpl impl; - Image imageNormal; - Image imageHover; - Image imageDown; - - PrivateData(SubWidget* const s, const Image& normal, const Image& hover, const Image& down) - : impl(s), - imageNormal(normal), - imageHover(hover), - imageDown(down) {} - - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) -}; - -// ----------------------------------------------------------------------- - -ImageButton::ImageButton(Widget* const parentWidget, const Image& image) - : SubWidget(parentWidget), - pData(new PrivateData(this, image, image, image)) -{ - setSize(image.getSize()); -} - -ImageButton::ImageButton(Widget* const parentWidget, const Image& imageNormal, const Image& imageDown) - : SubWidget(parentWidget), - pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) -{ - DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); - - setSize(imageNormal.getSize()); -} - -ImageButton::ImageButton(Widget* const parentWidget, const Image& imageNormal, const Image& imageHover, const Image& imageDown) - : SubWidget(parentWidget), - pData(new PrivateData(this, imageNormal, imageHover, imageDown)) -{ - DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); - - setSize(imageNormal.getSize()); -} - -ImageButton::~ImageButton() -{ - delete pData; -} - -void ImageButton::setCallback(Callback* callback) noexcept -{ - pData->impl.callback_img = callback; -} - -void ImageButton::onDisplay() -{ - switch (pData->impl.state) - { - case ButtonImpl::kStateDown: - pData->imageDown.draw(); - break; - case ButtonImpl::kStateHover: - pData->imageHover.draw(); - break; - default: - pData->imageNormal.draw(); - break; - } -} - -bool ImageButton::onMouse(const MouseEvent& ev) -{ - return pData->impl.onMouse(ev); -} - -bool ImageButton::onMotion(const MotionEvent& ev) -{ - return pData->impl.onMotion(ev); -} - -// ----------------------------------------------------------------------- - ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept : SubWidget(parentWidget), fImage(image), diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 43a0ded3..0d498321 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -444,6 +444,7 @@ void ImageBaseAboutWindow::onDisplay() } template class ImageBaseAboutWindow; +template class ImageBaseButton; // ----------------------------------------------------------------------- From 8f134ff179378f8fa464b17ffe26accd15d6d4cd Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 04:47:45 +0100 Subject: [PATCH 108/159] Allow to build plugins against vulkan --- Makefile.plugins.mk | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 41b05ce9..431dad38 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -103,6 +103,18 @@ HAVE_DGL = false endif endif +ifeq ($(UI_TYPE),vulkan) +ifeq ($(HAVE_VULKAN),true) +DGL_FLAGS += -DDGL_VULKAN +DGL_FLAGS += $(VULKAN_FLAGS) +DGL_LIBS += $(VULKAN_LIBS) +DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a +HAVE_DGL = true +else +HAVE_DGL = false +endif +endif + ifeq ($(UI_TYPE),external) DGL_FLAGS += -DDGL_EXTERNAL HAVE_DGL = true From a895877f5ee3722a840b973b76bec7022cc93f86 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 04:48:06 +0100 Subject: [PATCH 109/159] Make Parameters UI backend agnostic, test build against cairo --- examples/Parameters/ExampleUIParameters.cpp | 33 +++++++++++---------- examples/Parameters/Makefile | 1 + 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/examples/Parameters/ExampleUIParameters.cpp b/examples/Parameters/ExampleUIParameters.cpp index 8e69e2c4..3af11eac 100644 --- a/examples/Parameters/ExampleUIParameters.cpp +++ b/examples/Parameters/ExampleUIParameters.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -15,6 +15,7 @@ */ #include "DistrhoUI.hpp" +#include "Color.hpp" START_NAMESPACE_DISTRHO @@ -102,6 +103,8 @@ protected: */ void onDisplay() override { + const GraphicsContext& context(getGraphicsContext()); + const uint width = getWidth(); const uint height = getHeight(); const uint minwh = std::min(width, height); @@ -112,10 +115,10 @@ protected: // if host doesn't respect aspect-ratio but supports ui background, draw out-of-bounds color from it if (width != height && bgColor != 0) { - const GLubyte red = (bgColor >> 24) & 0xff; - const GLubyte green = (bgColor >> 16) & 0xff; - const GLubyte blue = (bgColor >> 8) & 0xff; - glColor3ub(red, green, blue); + const int red = (bgColor >> 24) & 0xff; + const int green = (bgColor >> 16) & 0xff; + const int blue = (bgColor >> 8) & 0xff; + Color(red, green, blue).setFor(context); if (width > height) { @@ -128,7 +131,7 @@ protected: r.setSize(width, height-width); } - r.draw(); + r.draw(context); } r.setWidth(minwh/3 - 6); @@ -143,31 +146,31 @@ protected: r.setY(3); if (fParamGrid[0+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // middle r.setY(3 + minwh/3); if (fParamGrid[3+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // bottom r.setY(3 + minwh*2/3); if (fParamGrid[6+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); } } diff --git a/examples/Parameters/Makefile b/examples/Parameters/Makefile index c8c7cafc..c3ae4189 100644 --- a/examples/Parameters/Makefile +++ b/examples/Parameters/Makefile @@ -21,6 +21,7 @@ FILES_UI = \ # -------------------------------------------------------------- # Do some magic +UI_TYPE = cairo include ../../Makefile.plugins.mk # -------------------------------------------------------------- From 6f218732774b8b49708d4b07137471d14d38614c Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 09:57:29 +0100 Subject: [PATCH 110/159] Build CairoUI by default now that DPF cairo APIs are going stable --- Makefile | 8 +++----- distrho/DistrhoUI.hpp | 3 +++ examples/CairoUI/CairoExampleUI.cpp | 8 ++++---- examples/CairoUI/DemoWidgetBanner.cpp | 18 ++++++++---------- examples/CairoUI/DemoWidgetBanner.hpp | 14 +++++++++----- examples/CairoUI/DemoWidgetClickable.cpp | 18 ++++++++---------- examples/CairoUI/DemoWidgetClickable.hpp | 14 +++++++++----- examples/CairoUI/DistrhoPluginInfo.h | 4 +++- examples/Parameters/Makefile | 1 - 9 files changed, 47 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index f57cc1e9..1bc07b82 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,9 @@ examples: dgl $(MAKE) all -C examples/MidiThrough $(MAKE) all -C examples/Parameters $(MAKE) all -C examples/States - -# ifeq ($(HAVE_CAIRO),true) -# $(MAKE) all -C examples/CairoUI -# endif - +ifeq ($(HAVE_CAIRO),true) + $(MAKE) all -C examples/CairoUI +endif ifneq ($(MACOS_OR_WINDOWS),true) # ExternalUI is WIP $(MAKE) all -C examples/ExternalUI diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 8cb4bec5..e802f663 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -27,6 +27,9 @@ typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget; #elif DISTRHO_UI_USE_CUSTOM # include DISTRHO_UI_CUSTOM_INCLUDE_PATH typedef DISTRHO_UI_CUSTOM_WIDGET_TYPE UIWidget; +#elif DISTRHO_UI_USE_CAIRO +# include "../dgl/Cairo.hpp" +typedef DGL_NAMESPACE::CairoTopLevelWidget UIWidget; #elif DISTRHO_UI_USE_NANOVG # include "../dgl/NanoVG.hpp" typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; diff --git a/examples/CairoUI/CairoExampleUI.cpp b/examples/CairoUI/CairoExampleUI.cpp index cd80d10e..9cf206f1 100644 --- a/examples/CairoUI/CairoExampleUI.cpp +++ b/examples/CairoUI/CairoExampleUI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -17,7 +17,6 @@ #include "DistrhoUI.hpp" #include "DemoWidgetBanner.hpp" #include "DemoWidgetClickable.hpp" -#include "Window.hpp" START_NAMESPACE_DISTRHO @@ -42,9 +41,10 @@ public: { } - void onDisplay() +protected: + void onCairoDisplay(const CairoGraphicsContext& context) { - cairo_t* cr = getParentWindow().getGraphicsContext().cairo; + cairo_t* cr = context.handle; cairo_set_source_rgb(cr, 1.0, 0.8, 0.5); cairo_paint(cr); diff --git a/examples/CairoUI/DemoWidgetBanner.cpp b/examples/CairoUI/DemoWidgetBanner.cpp index 200598e9..f1c7a516 100644 --- a/examples/CairoUI/DemoWidgetBanner.cpp +++ b/examples/CairoUI/DemoWidgetBanner.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -16,9 +16,6 @@ #include "DemoWidgetBanner.hpp" -#include "Cairo.hpp" -#include "Window.hpp" - START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -54,14 +51,15 @@ enum columns = 72, }; -DemoWidgetBanner::DemoWidgetBanner(Widget* group) - : Widget(group) -{ -} +DemoWidgetBanner::DemoWidgetBanner(SubWidget* parent) + : CairoSubWidget(parent) {} + +DemoWidgetBanner::DemoWidgetBanner(TopLevelWidget* parent) + : CairoSubWidget(parent) {} -void DemoWidgetBanner::onDisplay() +void DemoWidgetBanner::onCairoDisplay(const CairoGraphicsContext& context) { - cairo_t* cr = getParentWindow().getGraphicsContext().cairo; + cairo_t* cr = context.handle; Size sz = getSize(); int w = sz.getWidth(); diff --git a/examples/CairoUI/DemoWidgetBanner.hpp b/examples/CairoUI/DemoWidgetBanner.hpp index 8571b58e..029092c8 100644 --- a/examples/CairoUI/DemoWidgetBanner.hpp +++ b/examples/CairoUI/DemoWidgetBanner.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -14,17 +14,21 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "Widget.hpp" +#pragma once + +#include "Cairo.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -class DemoWidgetBanner : public Widget +class DemoWidgetBanner : public CairoSubWidget { public: - explicit DemoWidgetBanner(Widget* group); - void onDisplay() override; + explicit DemoWidgetBanner(SubWidget* parent); + explicit DemoWidgetBanner(TopLevelWidget* parent); +protected: + void onCairoDisplay(const CairoGraphicsContext& context) override; }; // ----------------------------------------------------------------------- diff --git a/examples/CairoUI/DemoWidgetClickable.cpp b/examples/CairoUI/DemoWidgetClickable.cpp index 23fec617..c2bfc5d4 100644 --- a/examples/CairoUI/DemoWidgetClickable.cpp +++ b/examples/CairoUI/DemoWidgetClickable.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -16,21 +16,19 @@ #include "DemoWidgetClickable.hpp" -#include "Cairo.hpp" -#include "Window.hpp" - START_NAMESPACE_DGL // ----------------------------------------------------------------------- -DemoWidgetClickable::DemoWidgetClickable(Widget* group) - : Widget(group) -{ -} +DemoWidgetClickable::DemoWidgetClickable(SubWidget* parent) + : CairoSubWidget(parent) {} + +DemoWidgetClickable::DemoWidgetClickable(TopLevelWidget* parent) + : CairoSubWidget(parent) {} -void DemoWidgetClickable::onDisplay() +void DemoWidgetClickable::onCairoDisplay(const CairoGraphicsContext& context) { - cairo_t* cr = getParentWindow().getGraphicsContext().cairo; + cairo_t* cr = context.handle; Size sz = getSize(); int w = sz.getWidth(); diff --git a/examples/CairoUI/DemoWidgetClickable.hpp b/examples/CairoUI/DemoWidgetClickable.hpp index a63c85fe..91d7b6c5 100644 --- a/examples/CairoUI/DemoWidgetClickable.hpp +++ b/examples/CairoUI/DemoWidgetClickable.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -14,17 +14,21 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "Widget.hpp" +#pragma once + +#include "Cairo.hpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- -class DemoWidgetClickable : public Widget +class DemoWidgetClickable : public CairoSubWidget { public: - explicit DemoWidgetClickable(Widget* group); - void onDisplay() override; + explicit DemoWidgetClickable(SubWidget* group); + explicit DemoWidgetClickable(TopLevelWidget* parent); +protected: + void onCairoDisplay(const CairoGraphicsContext& context) override; bool onMouse(const MouseEvent& event) override; private: diff --git a/examples/CairoUI/DistrhoPluginInfo.h b/examples/CairoUI/DistrhoPluginInfo.h index 8ab79fb4..ce9c04c0 100644 --- a/examples/CairoUI/DistrhoPluginInfo.h +++ b/examples/CairoUI/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -116,3 +116,5 @@ By default this is set to @ref DISTRHO_PLUGIN_URI with "#UI" as suffix. */ #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" + +#define DISTRHO_UI_USE_CAIRO 1 diff --git a/examples/Parameters/Makefile b/examples/Parameters/Makefile index c3ae4189..c8c7cafc 100644 --- a/examples/Parameters/Makefile +++ b/examples/Parameters/Makefile @@ -21,7 +21,6 @@ FILES_UI = \ # -------------------------------------------------------------- # Do some magic -UI_TYPE = cairo include ../../Makefile.plugins.mk # -------------------------------------------------------------- From d4f91993e9305a50593c27b31ea1cc3f9eb9afb4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 10:33:35 +0100 Subject: [PATCH 111/159] Build dgl-stub lib Signed-off-by: falkTX --- Makefile.base.mk | 9 +++++++++ dgl/Makefile | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index 5320c54f..83d64e65 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -317,6 +317,15 @@ HAVE_CAIRO_OR_OPENGL = true endif +# --------------------------------------------------------------------------------------------------------------------- +# Set Stub specific stuff + +ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +HAVE_STUB = true +else +HAVE_STUB = $(HAVE_X11) +endif + # --------------------------------------------------------------------------------------------------------------------- # Set Vulkan specific stuff diff --git a/dgl/Makefile b/dgl/Makefile index ff7fa1e7..35068125 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -67,6 +67,16 @@ endif # --------------------------------------------------------------------------------------------------------------------- +OBJS_stub = $(OBJS_common) + +ifeq ($(MACOS),true) +OBJS_stub += ../build/dgl/pugl.mm.o +else +OBJS_stub += ../build/dgl/pugl.cpp.o +endif + +# --------------------------------------------------------------------------------------------------------------------- + OBJS_vulkan = $(OBJS_common) \ ../build/dgl/Vulkan.cpp.vulkan.o @@ -88,6 +98,10 @@ TARGETS += ../build/libdgl-opengl.a TARGETS += ../build/libdgl.a endif +ifeq ($(HAVE_STUB),true) +TARGETS += ../build/libdgl-stub.a +endif + ifeq ($(HAVE_VULKAN),true) TARGETS += ../build/libdgl-vulkan.a endif @@ -110,6 +124,12 @@ all: $(TARGETS) $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ +../build/libdgl-stub.a: $(OBJS_stub) + -@mkdir -p ../build + @echo "Creating libdgl-stub.a" + $(SILENT)rm -f $@ + $(SILENT)$(AR) crs $@ $^ + ../build/libdgl-vulkan.a: $(OBJS_vulkan) -@mkdir -p ../build @echo "Creating libdgl-vulkan.a" @@ -187,6 +207,7 @@ debug: -include $(OBJS_common:%.o=%.d) -include $(OBJS_cairo:%.o=%.d) -include $(OBJS_opengl:%.o=%.d) +-include $(OBJS_stub:%.o=%.d) -include $(OBJS_vulkan:%.o=%.d) # --------------------------------------------------------------------------------------------------------------------- From 7fcfe3d9cc9fa14f7795ef2c1f69372631e7804d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 11:11:50 +0100 Subject: [PATCH 112/159] Make states example cairo compatible too Signed-off-by: falkTX --- examples/Parameters/ExampleUIParameters.cpp | 5 ++ examples/States/ExampleUIStates.cpp | 62 ++++++++++++++++----- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/examples/Parameters/ExampleUIParameters.cpp b/examples/Parameters/ExampleUIParameters.cpp index 3af11eac..7b5e2c49 100644 --- a/examples/Parameters/ExampleUIParameters.cpp +++ b/examples/Parameters/ExampleUIParameters.cpp @@ -19,6 +19,11 @@ START_NAMESPACE_DISTRHO +/** + We need the Color class from DGL. + */ +using DGL_NAMESPACE::Color; + /** We need the rectangle class from DGL. */ diff --git a/examples/States/ExampleUIStates.cpp b/examples/States/ExampleUIStates.cpp index 2e0ae031..4d271fc9 100644 --- a/examples/States/ExampleUIStates.cpp +++ b/examples/States/ExampleUIStates.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -15,9 +15,15 @@ */ #include "DistrhoUI.hpp" +#include "Color.hpp" START_NAMESPACE_DISTRHO +/** + We need the Color class from DGL. + */ +using DGL_NAMESPACE::Color; + /** We need the rectangle class from DGL. */ @@ -146,48 +152,74 @@ protected: */ void onDisplay() override { + const GraphicsContext& context(getGraphicsContext()); + const uint width = getWidth(); const uint height = getHeight(); + const uint minwh = std::min(width, height); + const uint bgColor = getBackgroundColor(); Rectangle r; - r.setWidth(width/3 - 6); - r.setHeight(height/3 - 6); + // if host doesn't respect aspect-ratio but supports ui background, draw out-of-bounds color from it + if (width != height && bgColor != 0) + { + const int red = (bgColor >> 24) & 0xff; + const int green = (bgColor >> 16) & 0xff; + const int blue = (bgColor >> 8) & 0xff; + Color(red, green, blue).setFor(context); + + if (width > height) + { + r.setPos(height, 0); + r.setSize(width-height, height); + } + else + { + r.setPos(0, width); + r.setSize(width, height-width); + } + + r.draw(context); + } + + r.setWidth(minwh/3 - 6); + r.setHeight(minwh/3 - 6); // draw left, center and right columns for (int i=0; i<3; ++i) { - r.setX(3 + i*width/3); + r.setX(3 + i*minwh/3); // top r.setY(3); if (fParamGrid[0+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // middle - r.setY(3 + height/3); + r.setY(3 + minwh/3); if (fParamGrid[3+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); // bottom - r.setY(3 + height*2/3); + r.setY(3 + minwh*2/3); if (fParamGrid[6+i]) - glColor3f(0.8f, 0.5f, 0.3f); + Color(0.8f, 0.5f, 0.3f).setFor(context); else - glColor3f(0.3f, 0.5f, 0.8f); + Color(0.3f, 0.5f, 0.8f).setFor(context); - r.draw(); + r.draw(context); } } From a8af6b72821ce5e3be05657f08f8ce7217a0ddc5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 11:20:07 +0100 Subject: [PATCH 113/159] Fix in-place processing in cairoui example; Cleanup Signed-off-by: falkTX --- distrho/DistrhoUI.hpp | 17 ++++++++++------- examples/CairoUI/CairoExamplePlugin.cpp | 8 +++++++- examples/ExternalUI/ExternalExamplePlugin.cpp | 2 +- examples/FileHandling/FileHandlingPlugin.cpp | 2 +- examples/Info/InfoExamplePlugin.cpp | 2 +- examples/Parameters/ExamplePluginParameters.cpp | 2 +- examples/States/ExamplePluginStates.cpp | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index e802f663..df042753 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -20,6 +20,16 @@ #include "extra/LeakDetector.hpp" #include "src/DistrhoPluginChecks.h" +#ifdef DGL_CAIRO +# include "Cairo.hpp" +#endif +#ifdef DGL_OPENGL +# include "OpenGL.hpp" +#endif +#ifdef DGL_VULKAN +# include "Vulkan.hpp" +#endif + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "../dgl/Base.hpp" # include "extra/ExternalWindow.hpp" @@ -38,13 +48,6 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; typedef DGL_NAMESPACE::TopLevelWidget UIWidget; #endif -#ifdef DGL_CAIRO -# include "Cairo.hpp" -#endif -#ifdef DGL_OPENGL -# include "OpenGL.hpp" -#endif - START_NAMESPACE_DISTRHO /* ------------------------------------------------------------------------------------------------------------ diff --git a/examples/CairoUI/CairoExamplePlugin.cpp b/examples/CairoUI/CairoExamplePlugin.cpp index b6df8799..161399e5 100644 --- a/examples/CairoUI/CairoExamplePlugin.cpp +++ b/examples/CairoUI/CairoExamplePlugin.cpp @@ -77,7 +77,13 @@ public: void run(const float** inputs, float** outputs, uint32_t frames) { - memcpy(outputs[0], inputs[0], frames * sizeof(float)); + /** + This plugin does nothing, it just demonstrates cairo UI usage. + So here we directly copy inputs over outputs, leaving the audio untouched. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. + */ + if (outputs[0] != inputs[0]) + std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); } }; diff --git a/examples/ExternalUI/ExternalExamplePlugin.cpp b/examples/ExternalUI/ExternalExamplePlugin.cpp index dbecdcc6..92b1b12e 100644 --- a/examples/ExternalUI/ExternalExamplePlugin.cpp +++ b/examples/ExternalUI/ExternalExamplePlugin.cpp @@ -157,7 +157,7 @@ protected: /** This plugin does nothing, it just demonstrates information usage. So here we directly copy inputs over outputs, leaving the audio untouched. - We need to be careful in case the host re-uses the same buffer for both ins and outs. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. */ if (outputs[0] != inputs[0]) std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp index 074e5ec4..db134ce3 100644 --- a/examples/FileHandling/FileHandlingPlugin.cpp +++ b/examples/FileHandling/FileHandlingPlugin.cpp @@ -227,7 +227,7 @@ protected: /** This plugin doesn't do audio, it just demonstrates file handling usage. So here we directly copy inputs over outputs, leaving the audio untouched. - We need to be careful in case the host re-uses the same buffer for both ins and outs. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. */ if (outputs[0] != inputs[0]) std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); diff --git a/examples/Info/InfoExamplePlugin.cpp b/examples/Info/InfoExamplePlugin.cpp index bd4829ea..15c4f41e 100644 --- a/examples/Info/InfoExamplePlugin.cpp +++ b/examples/Info/InfoExamplePlugin.cpp @@ -208,7 +208,7 @@ protected: /** This plugin does nothing, it just demonstrates information usage. So here we directly copy inputs over outputs, leaving the audio untouched. - We need to be careful in case the host re-uses the same buffer for both ins and outs. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. */ if (outputs[0] != inputs[0]) std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); diff --git a/examples/Parameters/ExamplePluginParameters.cpp b/examples/Parameters/ExamplePluginParameters.cpp index ea6ec6d1..3d995ed8 100644 --- a/examples/Parameters/ExamplePluginParameters.cpp +++ b/examples/Parameters/ExamplePluginParameters.cpp @@ -251,7 +251,7 @@ The plugin will be treated as an effect, but it will not change the host audio." /** This plugin does nothing, it just demonstrates parameter usage. So here we directly copy inputs over outputs, leaving the audio untouched. - We need to be careful in case the host re-uses the same buffer for both ins and outs. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. */ if (outputs[0] != inputs[0]) std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index b9f9ee57..66cba49d 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -279,7 +279,7 @@ The plugin will be treated as an effect, but it will not change the host audio." /** This plugin does nothing, it just demonstrates state usage. So here we directly copy inputs over outputs, leaving the audio untouched. - We need to be careful in case the host re-uses the same buffer for both ins and outs. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. */ if (outputs[0] != inputs[0]) std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); From 2369ad127d882359074ad1ad0cb0f6b2efacf0a2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 May 2021 11:26:20 +0100 Subject: [PATCH 114/159] Fix a few typos Signed-off-by: falkTX --- dgl/Geometry.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index 078b9c35..1bd02282 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -371,8 +371,8 @@ public: #ifndef DPF_TEST_POINT_CPP /** - Draw this line using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + Draw this line using the current OpenGL state.@n + DEPRECATED Please use draw(const GraphicsContext&) instead. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); @@ -489,15 +489,15 @@ public: #ifndef DPF_TEST_POINT_CPP /** - Draw this circle using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + Draw this circle using the current OpenGL state.@n + DEPRECATED Please use draw(const GraphicsContext&) instead. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** - Draw lines (outline of this circle) using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&,T) instead. + Draw lines (outline of this circle) using the current OpenGL state.@n + DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); @@ -582,15 +582,15 @@ public: #ifndef DPF_TEST_POINT_CPP /** - Draw this triangle using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + Draw this triangle using the current OpenGL state.@n + DEPRECATED Please use draw(const GraphicsContext&) instead. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); /** - Draw lines (outline of this triangle) using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&,T) instead. + Draw lines (outline of this triangle) using the current OpenGL state.@n + DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); @@ -801,15 +801,15 @@ public: bool operator!=(const Rectangle& size) const noexcept; /** - Draw this rectangle using the current OpenGL state. - DEPRECATED please use draw(const GraphicsContext&) instead. + Draw this rectangle using the current OpenGL state.@n + DEPRECATED Please use draw(const GraphicsContext&) instead. */ DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") void draw(); - /** DEPRECATED - Draw lines (outline of this rectangle) using the current OpenGL state. - DEPRECATED please use drawOutline(const GraphicsContext&,T) instead. + /** + Draw lines (outline of this rectangle) using the current OpenGL state.@n + DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. */ DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") void drawOutline(); From 4efdece239087ab0580e065acbb45652512901e3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 20 May 2021 19:56:28 +0100 Subject: [PATCH 115/159] Add NanoSubWidgets test Signed-off-by: falkTX --- tests/Circle.cpp | 2 + tests/Line.cpp | 2 + tests/Makefile | 6 +- tests/NanoSubWidgets.cpp | 135 +++++++++++++++++++++++++++++++++++++++ tests/Rectangle.cpp | 2 + tests/Triangle.cpp | 2 + 6 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 tests/NanoSubWidgets.cpp diff --git a/tests/Circle.cpp b/tests/Circle.cpp index 578688c6..e00d80be 100644 --- a/tests/Circle.cpp +++ b/tests/Circle.cpp @@ -14,6 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + // -------------------------------------------------------------------------------------------------------------------- int main() diff --git a/tests/Line.cpp b/tests/Line.cpp index 578688c6..e00d80be 100644 --- a/tests/Line.cpp +++ b/tests/Line.cpp @@ -14,6 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + // -------------------------------------------------------------------------------------------------------------------- int main() diff --git a/tests/Makefile b/tests/Makefile index dde5918d..1d79aca4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -17,7 +17,7 @@ BUILD_CXX_FLAGS += -Wno-extra -Wno-missing-field-initializers # --------------------------------------------------------------------------------------------------------------------- -TESTS = Application Color Point +TESTS = Application Color Point NanoSubWidgets ifeq ($(HAVE_CAIRO),true) TESTS += Demo.cairo WTESTS += Window.cairo @@ -128,6 +128,10 @@ clean: @echo "Linking Demo (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ +../build/tests/NanoSubWidgets$(APP_EXT): ../build/tests/NanoSubWidgets.cpp.o ../build/libdgl-opengl.a + @echo "Linking Demo (OpenGL)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS:%.o=%.d) diff --git a/tests/NanoSubWidgets.cpp b/tests/NanoSubWidgets.cpp new file mode 100644 index 00000000..e6608cbf --- /dev/null +++ b/tests/NanoSubWidgets.cpp @@ -0,0 +1,135 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "tests.hpp" + +#include "../dgl/NanoVG.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +class NanoRectangle : public NanoSubWidget +{ +public: + explicit NanoRectangle(Widget* const parent) + : NanoSubWidget(parent), + color() {} + + void setColor(const Color c) noexcept + { + color = c; + } + +protected: + void onNanoDisplay() override + { + beginPath(); + + fillColor(color); + rect(0, 0, getWidth(), getHeight()); + fill(); + + closePath(); + } + +private: + Color color; +}; + +// -------------------------------------------------------------------------------------------------------------------- + +class NanoRectanglesContainer : public NanoTopLevelWidget +{ +public: + explicit NanoRectanglesContainer(Window& parent) + : NanoTopLevelWidget(parent), + rect1(this), + rect2(this), + rect3(this) + { + rect1.setAbsolutePos(100, 100); + rect1.setSize(25, 25); + rect1.setColor(Color(255, 0, 0)); + + rect2.setAbsolutePos(200, 200); + rect2.setSize(25, 25); + rect2.setColor(Color(0, 255, 0)); + + rect3.setAbsolutePos(300, 300); + rect3.setSize(25, 25); + rect3.setColor(Color(0, 0, 255)); + } + +protected: + void onNanoDisplay() override + { + } + +private: + NanoRectangle rect1, rect2, rect3; +}; + +// -------------------------------------------------------------------------------------------------------------------- + +class NanoExampleWindow : public Window +{ +public: + explicit NanoExampleWindow(Application& app) + : Window(app), + container(*this) + { + const uint targetWidth = 1000; + const uint targetHeight = 600; + + setSize(targetWidth, targetHeight); + // container.setSize(width, height); + + setTitle("NanoVG SubWidgets test"); + } + + /* +protected: + void onReshape(uint width, uint height) override + { + container.setSize(width, height); + + Window::onReshape(width, height); + } + */ + +private: + NanoRectanglesContainer container; +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +int main() +{ + USE_NAMESPACE_DGL; + + Application app; + NanoExampleWindow win(app); + + win.show(); + app.exec(); + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Rectangle.cpp b/tests/Rectangle.cpp index 578688c6..e00d80be 100644 --- a/tests/Rectangle.cpp +++ b/tests/Rectangle.cpp @@ -14,6 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + // -------------------------------------------------------------------------------------------------------------------- int main() diff --git a/tests/Triangle.cpp b/tests/Triangle.cpp index 578688c6..e00d80be 100644 --- a/tests/Triangle.cpp +++ b/tests/Triangle.cpp @@ -14,6 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "tests.hpp" + // -------------------------------------------------------------------------------------------------------------------- int main() From 14faa195a6730c0ff531bbef978a5fc9b457dcdf Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 20 May 2021 20:06:35 +0100 Subject: [PATCH 116/159] Workaround wrong initial window size Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index e7ed92a1..5ed30ba6 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -207,7 +207,6 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r rect.width = width; rect.height = height; puglSetFrame(view, rect); - puglSetWindowSize(view, width, height); // FIXME this is bad puglRealize(view); @@ -245,6 +244,11 @@ void Window::PrivateData::show() isClosed = false; appData->oneWindowShown(); + // FIXME + PuglRect rect = puglGetFrame(view); + puglSetDefaultSize(view, rect.width, rect.height); + puglSetWindowSize(view, rect.width, rect.height); + #ifdef DISTRHO_OS_WINDOWS puglWin32ShowWindowCentered(view); #else From 5b98b66c0f68c2e835a5c437aa56f3e945aac28b Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 20 May 2021 20:06:52 +0100 Subject: [PATCH 117/159] NanoSubWidgets test: pain container grey to detect size issues Signed-off-by: falkTX --- tests/NanoSubWidgets.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/NanoSubWidgets.cpp b/tests/NanoSubWidgets.cpp index e6608cbf..1f0c3f7b 100644 --- a/tests/NanoSubWidgets.cpp +++ b/tests/NanoSubWidgets.cpp @@ -77,6 +77,13 @@ public: protected: void onNanoDisplay() override { + beginPath(); + + fillColor(Color(0.5f, 0.5f, 0.5f)); + rect(0, 0, getWidth(), getHeight()); + fill(); + + closePath(); } private: @@ -92,25 +99,13 @@ public: : Window(app), container(*this) { - const uint targetWidth = 1000; - const uint targetHeight = 600; + const uint targetWidth = 400; + const uint targetHeight = 400; setSize(targetWidth, targetHeight); - // container.setSize(width, height); - setTitle("NanoVG SubWidgets test"); } - /* -protected: - void onReshape(uint width, uint height) override - { - container.setSize(width, height); - - Window::onReshape(width, height); - } - */ - private: NanoRectanglesContainer container; }; From 647086c182afd1948111fd198b3b5f5ed0a0696d Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 20 May 2021 20:16:44 +0100 Subject: [PATCH 118/159] Add idle callback to NanoSubWidgets test, for hide/show widgets Signed-off-by: falkTX --- tests/NanoSubWidgets.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/NanoSubWidgets.cpp b/tests/NanoSubWidgets.cpp index 1f0c3f7b..267d5878 100644 --- a/tests/NanoSubWidgets.cpp +++ b/tests/NanoSubWidgets.cpp @@ -52,14 +52,16 @@ private: // -------------------------------------------------------------------------------------------------------------------- -class NanoRectanglesContainer : public NanoTopLevelWidget +class NanoRectanglesContainer : public NanoTopLevelWidget, + public IdleCallback { public: explicit NanoRectanglesContainer(Window& parent) : NanoTopLevelWidget(parent), rect1(this), rect2(this), - rect3(this) + rect3(this), + rectToHide(1) { rect1.setAbsolutePos(100, 100); rect1.setSize(25, 25); @@ -72,6 +74,9 @@ public: rect3.setAbsolutePos(300, 300); rect3.setSize(25, 25); rect3.setColor(Color(0, 0, 255)); + + idleCallback(); + addIdleCallback(this, 500); } protected: @@ -86,8 +91,34 @@ protected: closePath(); } + void idleCallback() override + { + switch (rectToHide) + { + case 1: + rect1.hide(); + rect2.show(); + rect3.show(); + rectToHide = 2; + break; + case 2: + rect1.show(); + rect2.hide(); + rect3.show(); + rectToHide = 3; + break; + case 3: + rect1.show(); + rect2.show(); + rect3.hide(); + rectToHide = 1; + break; + } + } + private: NanoRectangle rect1, rect2, rect3; + int rectToHide; }; // -------------------------------------------------------------------------------------------------------------------- From 0762a73bffee1e8df7f5095ee770d3eb3c764c0f Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 20 May 2021 20:41:46 +0100 Subject: [PATCH 119/159] Clarify tests, allow make -C test run Signed-off-by: falkTX --- tests/Color.cpp | 14 ++++++------ tests/Makefile | 56 +++++++++++++++++++++++++++--------------------- tests/README.txt | 42 +++++++++++++++++++++++++++++++++--- tests/Window.cpp | 4 ---- tests/tests.hpp | 3 +++ 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/tests/Color.cpp b/tests/Color.cpp index 0e572a40..412de3a7 100644 --- a/tests/Color.cpp +++ b/tests/Color.cpp @@ -46,10 +46,10 @@ int main() // constructor gives correct integer values normalized to float (arguments are r, g, b, a; in order) { Color c(51, 102, 153); - DISTRHO_ASSERT_EQUAL(c.red, 0.2f, "red value is 0.2 (integer 51)"); - DISTRHO_ASSERT_EQUAL(c.green, 0.4f, "green value is 0.4 (integer 102)"); - DISTRHO_ASSERT_EQUAL(c.blue, 0.6f, "blue value is 0.6 (integer 153)"); - DISTRHO_ASSERT_EQUAL(c.alpha, 1.0f, "alpha value is 1"); + DISTRHO_ASSERT_SAFE_EQUAL(c.red, 0.2f, "red value is 0.2 (integer 51)"); + DISTRHO_ASSERT_SAFE_EQUAL(c.green, 0.4f, "green value is 0.4 (integer 102)"); + DISTRHO_ASSERT_SAFE_EQUAL(c.blue, 0.6f, "blue value is 0.6 (integer 153)"); + DISTRHO_ASSERT_SAFE_EQUAL(c.alpha, 1.0f, "alpha value is 1"); Color white(255, 255, 255); DISTRHO_ASSERT_EQUAL(white.red, 1.0f, "white's red value is 1"); @@ -177,9 +177,9 @@ int main() // half point, round to 1 decimal point due to precision loss Color grey = Color::fromHTML("#7b7b7b"); - DISTRHO_ASSERT_EQUAL(std::round(grey.red*10)/10, 0.5f, "grey's rounded red value is 0.5"); - DISTRHO_ASSERT_EQUAL(std::round(grey.green*10)/10, 0.5f, "grey's rounded green value is 0.5"); - DISTRHO_ASSERT_EQUAL(std::round(grey.blue*10)/10, 0.5f, "grey's rounded blue value is 0.5"); + DISTRHO_ASSERT_SAFE_EQUAL(std::round(grey.red*10)/10, 0.5f, "grey's rounded red value is 0.5"); + DISTRHO_ASSERT_SAFE_EQUAL(std::round(grey.green*10)/10, 0.5f, "grey's rounded green value is 0.5"); + DISTRHO_ASSERT_SAFE_EQUAL(std::round(grey.blue*10)/10, 0.5f, "grey's rounded blue value is 0.5"); } // check bounds diff --git a/tests/Makefile b/tests/Makefile index 1d79aca4..7c6e2d30 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -17,40 +17,34 @@ BUILD_CXX_FLAGS += -Wno-extra -Wno-missing-field-initializers # --------------------------------------------------------------------------------------------------------------------- -TESTS = Application Color Point NanoSubWidgets +MANUAL_TESTS = +UNIT_TESTS = Application Color Point + ifeq ($(HAVE_CAIRO),true) -TESTS += Demo.cairo -WTESTS += Window.cairo +MANUAL_TESTS += Demo.cairo +UNIT_TESTS += Window.cairo endif ifeq ($(HAVE_OPENGL),true) -TESTS += Demo.opengl -WTESTS += Window.opengl +MANUAL_TESTS += Demo.opengl +MANUAL_TESTS += NanoSubWidgets +UNIT_TESTS += Window.opengl +endif +ifeq ($(HAVE_STUB),true) +UNIT_TESTS += Window.stub endif ifeq ($(HAVE_VULKAN),true) -TESTS += Demo.vulkan -WTESTS = Window.vulkan +UNIT_TESTS += Window.vulkan endif -TARGETS = $(TESTS:%=../build/tests/%$(APP_EXT)) -TARGETS += $(WTESTS:Window.%=../build/tests/Window.%$(APP_EXT)) +MANUAL_TARGETS = $(MANUAL_TESTS:%=../build/tests/%$(APP_EXT)) +UNIT_TARGET = $(UNIT_TESTS:%=../build/tests/%$(APP_EXT)) -OBJS = $(TESTS:%=../build/tests/%.cpp.o) -OBJS += $(WTESTS:Window.%=../build/tests/Window.cpp.%.o) +ALL_OBJS = $(MANUAL_TESTS:%=../build/tests/%.cpp.o) +ALL_OBJS += $(UNIT_TESTS:%=../build/tests/%.cpp.o) # --------------------------------------------------------------------------------------------------------------------- -ifeq ($(HAVE_CAIRO),true) -endif - -ifeq ($(HAVE_OPENGL),true) -endif - -ifeq ($(HAVE_VULKAN),true) -endif - -# --------------------------------------------------------------------------------------------------------------------- - -all: $(TARGETS) +all: $(MANUAL_TARGETS) $(UNIT_TARGET) # --------------------------------------------------------------------------------------------------------------------- @@ -61,8 +55,8 @@ endef # valgrind --leak-check=full $@ -run: $(TARGETS) - $(foreach TEST,$(TARGETS),$(call RUN_TEST,$(TEST))) +run: $(UNIT_TARGET) + $(foreach TEST,$^,$(call RUN_TEST,$(TEST))) # --------------------------------------------------------------------------------------------------------------------- @@ -92,6 +86,11 @@ clean: @echo "Compiling $< (OpenGL)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ +../build/tests/%.cpp.stub.o: %.cpp + -@mkdir -p ../build/tests + @echo "Compiling $< (Stub)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + ../build/tests/%.cpp.vulkan.o: %.cpp -@mkdir -p ../build/tests @echo "Compiling $< (Vulkan)" @@ -112,10 +111,17 @@ clean: @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ +../build/tests/%.stub$(APP_EXT): ../build/tests/%.cpp.stub.o + @echo "Linking $*" + $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) -o $@ + ../build/tests/%.vulkan$(APP_EXT): ../build/tests/%.cpp.vulkan.o @echo "Linking $*" $(SILENT)$(CXX) $< $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ +# --------------------------------------------------------------------------------------------------------------------- +# linking steps (special, links against DGL static lib) + ../build/tests/Demo.cairo$(APP_EXT): ../build/tests/Demo.cpp.cairo.o ../build/libdgl-cairo.a @echo "Linking Demo (Cairo)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(CAIRO_LIBS) -o $@ diff --git a/tests/README.txt b/tests/README.txt index 4d597ac6..953e9397 100644 --- a/tests/README.txt +++ b/tests/README.txt @@ -1,6 +1,42 @@ This directory contains several tests for DPF related things, from graphics to plugin stuff to utilities. Each *.cpp file is meant to be its own test. -In order to test DPF components individually, these tests do not link against DGL directly, -but will directly import the needed source code. -All files must be self-contained, in order to prevent surprises in regards global state and initialization stuff. +In order to test DPF components individually, some of these tests do not link against DGL but import/include its files. +All test files must be self-contained, in order to prevent surprises in regards global state and initialization stuff. + +The following tests are present: + + - Application + Verifies that creating an application instance and its event loop is working correctly. + This test should automatically close itself without errors after a few seconds + + - Circle + TODO + + - Color + Runs a few unit-tests on top of the Color class. Mostly complete but still WIP. + + - Demo + A full window with widgets to verify that contents are being drawn correctly, window can be resized and events work. + Can be used in both Cairo and OpenGL modes, the Vulkan variant does not work right now. + + - Line + TODO + + - NanoSubWidgets + Verifies that NanoVG subwidgets are being drawn properly, and that hide/show calls work as intended. + There should be a grey background with 3 squares on top, one of hiding every half second in a sequence. + + - Point + Runs a few unit-tests on top of the Point class. Mostly complete but still WIP. + + - Rectangle + TODO + + - Triangle + TODO + + - Window + Runs a few basic tests with Window showing, hiding and event loop. + Will try to create a window on screen. + Should automatically close after a few seconds. diff --git a/tests/Window.cpp b/tests/Window.cpp index 32724cdd..81dd37c5 100644 --- a/tests/Window.cpp +++ b/tests/Window.cpp @@ -14,10 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#if !(defined(DGL_CAIRO) || defined(DGL_OPENGL) || defined(DGL_VULKAN)) -# error test setup failed, must be for cairo, opengl or vulkan -#endif - #include "tests.hpp" #define DPF_TEST_POINT_CPP diff --git a/tests/tests.hpp b/tests/tests.hpp index 466141f7..c9b8b0cc 100644 --- a/tests/tests.hpp +++ b/tests/tests.hpp @@ -24,6 +24,9 @@ #define DISTRHO_ASSERT_NOT_EQUAL(v1, v2, msg) \ if (v1 == v2) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } +#define DISTRHO_ASSERT_SAFE_EQUAL(v1, v2, msg) \ + if (d_isNotEqual(v1, v2)) { d_stderr2("Test condition failed: %s; file:%s line:%i", msg, __FILE__, __LINE__); return 1; } + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- From c503ca081e07c55b48001de9ef0a61e3006f12f7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 09:50:32 +0100 Subject: [PATCH 120/159] Update copyright year Signed-off-by: falkTX --- LICENSE | 2 +- tests/Demo.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 6bd93e18..822c5576 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ DISTRHO Plugin Framework (DPF) -Copyright (C) 2012-2018 Filipe Coelho +Copyright (C) 2012-2021 Filipe Coelho 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 diff --git a/tests/Demo.cpp b/tests/Demo.cpp index f5998fef..751ce106 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -394,7 +394,7 @@ int main(int argc, char* argv[]) createAndShowExampleWidgetStandaloneWindow(app); #endif else - d_stderr2("Invalid demo mode, must be one of: color, rectangles, shapes"); + d_stderr2("Invalid demo mode, must be one of: color, images, rectangles or shapes"); } else { From 227e620d117832f73461b41704fa1eaee2e49277 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 09:51:56 +0100 Subject: [PATCH 121/159] Allow UI_TYPE=stub target Signed-off-by: falkTX --- Makefile.plugins.mk | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 431dad38..8e4e9fa3 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -120,6 +120,15 @@ DGL_FLAGS += -DDGL_EXTERNAL HAVE_DGL = true endif +ifeq ($(UI_TYPE),stub) +ifeq ($(HAVE_STUB),true) +DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a +HAVE_DGL = true +else +HAVE_DGL = false +endif +endif + DGL_LIBS += $(DGL_SYSTEM_LIBS) ifneq ($(HAVE_DGL),true) From e46dd357b642654240b5539b0e831178fd22e343 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 18:34:23 +0100 Subject: [PATCH 122/159] Reorganize the example plugin targets --- distrho/src/DistrhoPluginChecks.h | 11 ++++++----- examples/CairoUI/Makefile | 19 +++++++------------ examples/ImguiSimpleGain/Makefile | 19 +++++++++++-------- examples/Info/Makefile | 4 ++-- examples/Latency/Makefile | 4 ---- examples/Meters/Makefile | 19 +++++++------------ examples/MidiThrough/Makefile | 3 +-- examples/Parameters/Makefile | 12 ++++++------ examples/States/DistrhoPluginInfo.h | 1 - examples/States/ExamplePluginStates.cpp | 3 ++- examples/States/Makefile | 8 ++++++-- 11 files changed, 48 insertions(+), 55 deletions(-) diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index 7bc5dd67..d8a3b0f8 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -142,11 +142,12 @@ // ----------------------------------------------------------------------- // Enable full state if plugin exports presets -#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE -# warning Plugins with programs and state need to implement full state API -# undef DISTRHO_PLUGIN_WANT_FULL_STATE -# define DISTRHO_PLUGIN_WANT_FULL_STATE 1 -#endif +// FIXME +// #if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE +// # warning Plugins with programs and state need to implement full state API +// # undef DISTRHO_PLUGIN_WANT_FULL_STATE +// # define DISTRHO_PLUGIN_WANT_FULL_STATE 1 +// #endif // ----------------------------------------------------------------------- // Disable UI if DGL or External UI is not available diff --git a/examples/CairoUI/Makefile b/examples/CairoUI/Makefile index 828029e3..13c684b6 100644 --- a/examples/CairoUI/Makefile +++ b/examples/CairoUI/Makefile @@ -29,28 +29,23 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_CAIRO),true) + +ifeq ($(HAVE_JACK),true) TARGETS += jack -endif -endif +endif # HAVE_JACK ifneq ($(MACOS_OR_WINDOWS),true) -ifeq ($(HAVE_CAIRO),true) ifeq ($(HAVE_LIBLO),true) TARGETS += dssi -endif -endif -endif +endif # HAVE_LIBLO +endif # MACOS_OR_WINDOWS -ifeq ($(HAVE_CAIRO),true) TARGETS += lv2_sep -else -TARGETS += lv2_dsp -endif - TARGETS += vst +endif # HAVE_CAIRO + all: $(TARGETS) # -------------------------------------------------------------- diff --git a/examples/ImguiSimpleGain/Makefile b/examples/ImguiSimpleGain/Makefile index 0e07c251..f398a122 100644 --- a/examples/ImguiSimpleGain/Makefile +++ b/examples/ImguiSimpleGain/Makefile @@ -32,20 +32,23 @@ LINK_FLAGS += $(shell $(PKG_CONFIG) glew --libs) # -------------------------------------------------------------- # Enable all selected plugin types -ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_OPENGL),true) + +ifeq ($(HAVE_JACK),true) TARGETS += jack -endif -endif +endif # HAVE_JACK -ifeq ($(HAVE_OPENGL),true) -TARGETS += lv2_sep -else -TARGETS += lv2_dsp -endif +ifneq ($(MACOS_OR_WINDOWS),true) +ifeq ($(HAVE_LIBLO),true) +TARGETS += dssi +endif # HAVE_LIBLO +endif # MACOS_OR_WINDOWS +TARGETS += lv2_sep TARGETS += vst +endif # HAVE_OPENGL + all: $(TARGETS) # -------------------------------------------------------------- diff --git a/examples/Info/Makefile b/examples/Info/Makefile index 4fe4fa54..2ffdae11 100644 --- a/examples/Info/Makefile +++ b/examples/Info/Makefile @@ -29,8 +29,8 @@ include ../../Makefile.plugins.mk ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_OPENGL),true) TARGETS += jack -endif -endif +endif # HAVE_OPENGL +endif # HAVE_JACK ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep diff --git a/examples/Latency/Makefile b/examples/Latency/Makefile index c0520530..4b0067b9 100644 --- a/examples/Latency/Makefile +++ b/examples/Latency/Makefile @@ -23,11 +23,7 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -ifneq ($(MACOS_OR_WINDOWS),true) TARGETS += ladspa -TARGETS += dssi -endif - TARGETS += lv2_dsp TARGETS += vst diff --git a/examples/Meters/Makefile b/examples/Meters/Makefile index 9ac485a5..1fdbfa24 100644 --- a/examples/Meters/Makefile +++ b/examples/Meters/Makefile @@ -26,28 +26,23 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_OPENGL),true) + +ifeq ($(HAVE_JACK),true) TARGETS += jack -endif -endif +endif # HAVE_JACK ifneq ($(MACOS_OR_WINDOWS),true) ifeq ($(HAVE_LIBLO),true) -ifeq ($(HAVE_OPENGL),true) TARGETS += dssi -endif -endif -endif +endif # HAVE_LIBLO +endif # MACOS_OR_WINDOWS -ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep -else -TARGETS += lv2_dsp -endif - TARGETS += vst +endif # HAVE_OPENGL + all: $(TARGETS) # -------------------------------------------------------------- diff --git a/examples/MidiThrough/Makefile b/examples/MidiThrough/Makefile index 79c50beb..084fb2c7 100644 --- a/examples/MidiThrough/Makefile +++ b/examples/MidiThrough/Makefile @@ -23,10 +23,9 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -ifeq ($(LINUX),true) +ifeq ($(HAVE_JACK),true) TARGETS += jack endif - TARGETS += lv2_dsp TARGETS += vst diff --git a/examples/Parameters/Makefile b/examples/Parameters/Makefile index c8c7cafc..2d899211 100644 --- a/examples/Parameters/Makefile +++ b/examples/Parameters/Makefile @@ -29,17 +29,17 @@ include ../../Makefile.plugins.mk ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_OPENGL),true) TARGETS += jack -endif -endif +endif # HAVE_OPENGL +endif # HAVE_JACK ifneq ($(MACOS_OR_WINDOWS),true) +TARGETS += ladspa ifeq ($(HAVE_LIBLO),true) ifeq ($(HAVE_OPENGL),true) -TARGETS += ladspa TARGETS += dssi -endif -endif -endif +endif # HAVE_OPENGL +endif # HAVE_LIBLO +endif # MACOS_OR_WINDOWS ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep diff --git a/examples/States/DistrhoPluginInfo.h b/examples/States/DistrhoPluginInfo.h index 2b888245..52e13568 100644 --- a/examples/States/DistrhoPluginInfo.h +++ b/examples/States/DistrhoPluginInfo.h @@ -27,7 +27,6 @@ #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 #define DISTRHO_PLUGIN_WANT_STATE 1 -#define DISTRHO_PLUGIN_WANT_FULL_STATE 1 #define DISTRHO_UI_USER_RESIZABLE 1 #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index 66cba49d..8d1c6c94 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -208,6 +208,7 @@ The plugin will be treated as an effect, but it will not change the host audio." } } +#if DISTRHO_PLUGIN_WANT_FULL_STATE /* FIXME */ /** Get the value of an internal state. The host may call this function from any non-realtime context. @@ -239,6 +240,7 @@ The plugin will be treated as an effect, but it will not change the host audio." return sFalse; } +#endif /** Change an internal state. @@ -296,7 +298,6 @@ private: */ bool fParamGrid[9]; - /** Set our plugin class as non-copyable and add a leak detector just in case. */ diff --git a/examples/States/Makefile b/examples/States/Makefile index a9117e84..25489a34 100644 --- a/examples/States/Makefile +++ b/examples/States/Makefile @@ -29,8 +29,12 @@ include ../../Makefile.plugins.mk ifeq ($(HAVE_JACK),true) ifeq ($(HAVE_OPENGL),true) TARGETS += jack -endif -endif +endif # HAVE_OPENGL +endif # HAVE_JACK + +ifneq ($(MACOS_OR_WINDOWS),true) +TARGETS += dssi +endif # MACOS_OR_WINDOWS ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep From 7baa2c0458c2968d32bd37e34b0e3f54c1a368ff Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 19:14:07 +0100 Subject: [PATCH 123/159] Move ImageWidgets code to generic file, WIP --- dgl/Cairo.hpp | 8 +- dgl/ImageBaseWidgets.hpp | 199 ++++++++ dgl/ImageWidgets.hpp | 203 +------- dgl/Makefile | 1 - dgl/OpenGL.hpp | 9 +- dgl/src/ImageBaseWidgets.cpp | 853 ++++++++++++++++++++++++++++++++++ dgl/src/ImageWidgets.cpp | 876 ----------------------------------- 7 files changed, 1069 insertions(+), 1080 deletions(-) delete mode 100644 dgl/src/ImageWidgets.cpp diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 525c1496..e7dfaa93 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -19,7 +19,6 @@ #include "ImageBase.hpp" #include "ImageBaseWidgets.hpp" -#include "SubWidget.hpp" #include @@ -115,25 +114,21 @@ class CairoBaseWidget : public BaseWidget public: /** Constructor for a CairoSubWidget. - @see CreateFlags */ explicit CairoBaseWidget(Widget* const parentGroupWidget); /** Constructor for a CairoTopLevelWidget. - @see CreateFlags */ explicit CairoBaseWidget(Window& windowToMapTo); /** Constructor for a CairoStandaloneWindow without parent window. - @see CreateFlags */ explicit CairoBaseWidget(Application& app); /** Constructor for a CairoStandaloneWindow with parent window. - @see CreateFlags */ explicit CairoBaseWidget(Application& app, Window& parentWindow); @@ -171,6 +166,9 @@ typedef CairoBaseWidget CairoStandaloneWindow; typedef ImageBaseAboutWindow CairoImageAboutWindow; typedef ImageBaseButton CairoImageButton; +typedef ImageBaseKnob CairoImageKnob; +typedef ImageBaseSlider CairoImageSlider; +typedef ImageBaseSwitch CairoImageSwitch; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 6ae520db..33a72639 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -82,6 +82,205 @@ private: // -------------------------------------------------------------------------------------------------------------------- +template +class ImageBaseKnob : public SubWidget +{ +public: + enum Orientation { + Horizontal, + Vertical + }; + + class Callback + { + public: + virtual ~Callback() {} + virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0; + virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0; + virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0; + }; + + explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept; + explicit ImageBaseKnob(const ImageBaseKnob& imageKnob); + ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); + ~ImageBaseKnob() override; + + float getValue() const noexcept; + + void setDefault(float def) noexcept; + void setRange(float min, float max) noexcept; + void setStep(float step) noexcept; + void setValue(float value, bool sendCallback = false) noexcept; + void setUsingLogScale(bool yesNo) noexcept; + + void setCallback(Callback* callback) noexcept; + void setOrientation(Orientation orientation) noexcept; + void setRotationAngle(int angle); + + void setImageLayerCount(uint count) noexcept; + +protected: + void onDisplay() override; + bool onMouse(const MouseEvent&) override; + bool onMotion(const MotionEvent&) override; + bool onScroll(const ScrollEvent&) override; + +private: + struct PrivateData; + PrivateData* const pData; + + /* + Image fImage; + float fMinimum; + float fMaximum; + float fStep; + float fValue; + float fValueDef; + float fValueTmp; + bool fUsingDefault; + bool fUsingLog; + Orientation fOrientation; + + int fRotationAngle; + bool fDragging; + int fLastX; + int fLastY; + + Callback* fCallback; + + bool fIsImgVertical; + uint fImgLayerWidth; + uint fImgLayerHeight; + uint fImgLayerCount; + bool fIsReady; + GLuint fTextureId; + + float _logscale(float value) const; + float _invlogscale(float value) const; + */ + + DISTRHO_LEAK_DETECTOR(ImageBaseKnob) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +// note set range and step before setting the value + +template +class ImageBaseSlider : public SubWidget +{ +public: + class Callback + { + public: + virtual ~Callback() {} + virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0; + virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0; + virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0; + }; + + explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept; + + float getValue() const noexcept; + void setValue(float value, bool sendCallback = false) noexcept; + void setDefault(float def) noexcept; + + void setStartPos(const Point& startPos) noexcept; + void setStartPos(int x, int y) noexcept; + void setEndPos(const Point& endPos) noexcept; + void setEndPos(int x, int y) noexcept; + + void setInverted(bool inverted) noexcept; + void setRange(float min, float max) noexcept; + void setStep(float step) noexcept; + + void setCallback(Callback* callback) noexcept; + +protected: + void onDisplay() override; + bool onMouse(const MouseEvent&) override; + bool onMotion(const MotionEvent&) override; + +private: + struct PrivateData; + PrivateData* const pData; + + /* + Image fImage; + float fMinimum; + float fMaximum; + float fStep; + float fValue; + float fValueDef; + float fValueTmp; + bool fUsingDefault; + + bool fDragging; + bool fInverted; + bool fValueIsSet; + int fStartedX; + int fStartedY; + + Callback* fCallback; + + Point fStartPos; + Point fEndPos; + Rectangle fSliderArea; + + void _recheckArea() noexcept; + */ + + // these should not be used + void setAbsoluteX(int) const noexcept {} + void setAbsoluteY(int) const noexcept {} + void setAbsolutePos(int, int) const noexcept {} + void setAbsolutePos(const Point&) const noexcept {} + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template +class ImageBaseSwitch : public SubWidget +{ +public: + class Callback + { + public: + virtual ~Callback() {} + virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0; + }; + + explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept; + explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept; + ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept; + + bool isDown() const noexcept; + void setDown(bool down) noexcept; + + void setCallback(Callback* callback) noexcept; + +protected: + void onDisplay() override; + bool onMouse(const MouseEvent&) override; + +private: + struct PrivateData; + PrivateData* const pData; + + /* + Image fImageNormal; + Image fImageDown; + bool fIsDown; + Callback* fCallback; + */ + + DISTRHO_LEAK_DETECTOR(ImageBaseSwitch) +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index 3571d8da..aa85fe6f 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -17,206 +17,25 @@ #ifndef DGL_IMAGE_WIDGETS_HPP_INCLUDED #define DGL_IMAGE_WIDGETS_HPP_INCLUDED -#include "Image.hpp" -#include "ImageBaseWidgets.hpp" -#include "SubWidget.hpp" - -// TODO switch to use templated image type after merging widget-related PRs -#if defined(__GNUC__) && (__GNUC__ >= 6) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif +#include "OpenGL.hpp" START_NAMESPACE_DGL -// ----------------------------------------------------------------------- - -class ImageKnob : public SubWidget -{ -public: - enum Orientation { - Horizontal, - Vertical - }; - - class Callback - { - public: - virtual ~Callback() {} - virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0; - virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0; - virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0; - }; - - explicit ImageKnob(Widget* parentWidget, const Image& image, Orientation orientation = Vertical) noexcept; - explicit ImageKnob(const ImageKnob& imageKnob); - ImageKnob& operator=(const ImageKnob& imageKnob); - ~ImageKnob() override; - - float getValue() const noexcept; - - void setDefault(float def) noexcept; - void setRange(float min, float max) noexcept; - void setStep(float step) noexcept; - void setValue(float value, bool sendCallback = false) noexcept; - void setUsingLogScale(bool yesNo) noexcept; - - void setCallback(Callback* callback) noexcept; - void setOrientation(Orientation orientation) noexcept; - void setRotationAngle(int angle); - - void setImageLayerCount(uint count) noexcept; - -protected: - void onDisplay() override; - bool onMouse(const MouseEvent&) override; - bool onMotion(const MotionEvent&) override; - bool onScroll(const ScrollEvent&) override; - -private: - Image fImage; - float fMinimum; - float fMaximum; - float fStep; - float fValue; - float fValueDef; - float fValueTmp; - bool fUsingDefault; - bool fUsingLog; - Orientation fOrientation; - - int fRotationAngle; - bool fDragging; - int fLastX; - int fLastY; - - Callback* fCallback; - - bool fIsImgVertical; - uint fImgLayerWidth; - uint fImgLayerHeight; - uint fImgLayerCount; - bool fIsReady; - GLuint fTextureId; - - float _logscale(float value) const; - float _invlogscale(float value) const; - - DISTRHO_LEAK_DETECTOR(ImageKnob) -}; - -// ----------------------------------------------------------------------- - -// note set range and step before setting the value - -class ImageSlider : public SubWidget -{ -public: - class Callback - { - public: - virtual ~Callback() {} - virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0; - virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0; - virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0; - }; - - explicit ImageSlider(Widget* parentWidget, const Image& image) noexcept; +DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") +typedef OpenGLImageAboutWindow ImageAboutWindow; - float getValue() const noexcept; - void setValue(float value, bool sendCallback = false) noexcept; - void setDefault(float def) noexcept; +DISTRHO_DEPRECATED_BY("OpenGLImageButton") +typedef OpenGLImageButton ImageButton; - void setStartPos(const Point& startPos) noexcept; - void setStartPos(int x, int y) noexcept; - void setEndPos(const Point& endPos) noexcept; - void setEndPos(int x, int y) noexcept; +DISTRHO_DEPRECATED_BY("OpenGLImageKnob") +typedef OpenGLImageKnob ImageKnob; - void setInverted(bool inverted) noexcept; - void setRange(float min, float max) noexcept; - void setStep(float step) noexcept; +DISTRHO_DEPRECATED_BY("OpenGLImageSlider") +typedef OpenGLImageSlider ImageSlider; - void setCallback(Callback* callback) noexcept; - -protected: - void onDisplay() override; - bool onMouse(const MouseEvent&) override; - bool onMotion(const MotionEvent&) override; - -private: - Image fImage; - float fMinimum; - float fMaximum; - float fStep; - float fValue; - float fValueDef; - float fValueTmp; - bool fUsingDefault; - - bool fDragging; - bool fInverted; - bool fValueIsSet; - int fStartedX; - int fStartedY; - - Callback* fCallback; - - Point fStartPos; - Point fEndPos; - Rectangle fSliderArea; - - void _recheckArea() noexcept; - - // these should not be used - void setAbsoluteX(int) const noexcept {} - void setAbsoluteY(int) const noexcept {} - void setAbsolutePos(int, int) const noexcept {} - void setAbsolutePos(const Point&) const noexcept {} - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider) -}; - -// ----------------------------------------------------------------------- - -class ImageSwitch : public SubWidget -{ -public: - class Callback - { - public: - virtual ~Callback() {} - virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0; - }; - - explicit ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept; - explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept; - ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept; - - bool isDown() const noexcept; - void setDown(bool down) noexcept; - - void setCallback(Callback* callback) noexcept; - -protected: - void onDisplay() override; - bool onMouse(const MouseEvent&) override; - -private: - Image fImageNormal; - Image fImageDown; - bool fIsDown; - - Callback* fCallback; - - DISTRHO_LEAK_DETECTOR(ImageSwitch) -}; - -// ----------------------------------------------------------------------- +DISTRHO_DEPRECATED_BY("OpenGLImageSwitch") +typedef OpenGLImageSwitch ImageSwitch; END_NAMESPACE_DGL -#if defined(__GNUC__) && (__GNUC__ >= 6) -# pragma GCC diagnostic pop -#endif - #endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED diff --git a/dgl/Makefile b/dgl/Makefile index 35068125..041e1d03 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -56,7 +56,6 @@ endif OBJS_opengl = $(OBJS_common) \ ../build/dgl/OpenGL.cpp.opengl.o \ - ../build/dgl/ImageWidgets.cpp.o \ ../build/dgl/NanoVG.cpp.opengl.o ifeq ($(MACOS),true) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index e3fa37e1..f28f2277 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -282,12 +282,9 @@ private: typedef ImageBaseAboutWindow OpenGLImageAboutWindow; typedef ImageBaseButton OpenGLImageButton; - -DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") -typedef OpenGLImageAboutWindow ImageAboutWindow; - -DISTRHO_DEPRECATED_BY("OpenGLImageButton") -typedef OpenGLImageButton ImageButton; +typedef ImageBaseKnob OpenGLImageKnob; +typedef ImageBaseSlider OpenGLImageSlider; +typedef ImageBaseSwitch OpenGLImageSwitch; // ----------------------------------------------------------------------- diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 1eb8bbd1..b9e46896 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -180,4 +180,857 @@ bool ImageBaseButton::onMotion(const MotionEvent& ev) // -------------------------------------------------------------------------------------------------------------------- +#if 0 +ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept + : SubWidget(parentWidget), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fStep(0.0f), + fValue(0.5f), + fValueDef(fValue), + fValueTmp(fValue), + fUsingDefault(false), + fUsingLog(false), + fOrientation(orientation), + fRotationAngle(0), + fDragging(false), + fLastX(0), + fLastY(0), + fCallback(nullptr), + fIsImgVertical(image.getHeight() > image.getWidth()), + fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), + fImgLayerHeight(fImgLayerWidth), + fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), + fIsReady(false), + fTextureId(0) +{ + glGenTextures(1, &fTextureId); + setSize(fImgLayerWidth, fImgLayerHeight); +} + +ImageKnob::ImageKnob(const ImageKnob& imageKnob) + : SubWidget(imageKnob.getParentWidget()), + fImage(imageKnob.fImage), + fMinimum(imageKnob.fMinimum), + fMaximum(imageKnob.fMaximum), + fStep(imageKnob.fStep), + fValue(imageKnob.fValue), + fValueDef(imageKnob.fValueDef), + fValueTmp(fValue), + fUsingDefault(imageKnob.fUsingDefault), + fUsingLog(imageKnob.fUsingLog), + fOrientation(imageKnob.fOrientation), + fRotationAngle(imageKnob.fRotationAngle), + fDragging(false), + fLastX(0), + fLastY(0), + fCallback(imageKnob.fCallback), + fIsImgVertical(imageKnob.fIsImgVertical), + fImgLayerWidth(imageKnob.fImgLayerWidth), + fImgLayerHeight(imageKnob.fImgLayerHeight), + fImgLayerCount(imageKnob.fImgLayerCount), + fIsReady(false), + fTextureId(0) +{ + glGenTextures(1, &fTextureId); + setSize(fImgLayerWidth, fImgLayerHeight); +} + +ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) +{ + fImage = imageKnob.fImage; + fMinimum = imageKnob.fMinimum; + fMaximum = imageKnob.fMaximum; + fStep = imageKnob.fStep; + fValue = imageKnob.fValue; + fValueDef = imageKnob.fValueDef; + fValueTmp = fValue; + fUsingDefault = imageKnob.fUsingDefault; + fUsingLog = imageKnob.fUsingLog; + fOrientation = imageKnob.fOrientation; + fRotationAngle = imageKnob.fRotationAngle; + fDragging = false; + fLastX = 0; + fLastY = 0; + fCallback = imageKnob.fCallback; + fIsImgVertical = imageKnob.fIsImgVertical; + fImgLayerWidth = imageKnob.fImgLayerWidth; + fImgLayerHeight = imageKnob.fImgLayerHeight; + fImgLayerCount = imageKnob.fImgLayerCount; + fIsReady = false; + + if (fTextureId != 0) + { + glDeleteTextures(1, &fTextureId); + fTextureId = 0; + } + + glGenTextures(1, &fTextureId); + setSize(fImgLayerWidth, fImgLayerHeight); + + return *this; +} + +ImageKnob::~ImageKnob() +{ + if (fTextureId != 0) + { + glDeleteTextures(1, &fTextureId); + fTextureId = 0; + } +} + +float ImageKnob::getValue() const noexcept +{ + return fValue; +} + +// NOTE: value is assumed to be scaled if using log +void ImageKnob::setDefault(float value) noexcept +{ + fValueDef = value; + fUsingDefault = true; +} + +void ImageKnob::setRange(float min, float max) noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(max > min,); + + if (fValue < min) + { + fValue = min; + repaint(); + + if (fCallback != nullptr) + { + try { + fCallback->imageKnobValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min"); + } + } + else if (fValue > max) + { + fValue = max; + repaint(); + + if (fCallback != nullptr) + { + try { + fCallback->imageKnobValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max"); + } + } + + fMinimum = min; + fMaximum = max; +} + +void ImageKnob::setStep(float step) noexcept +{ + fStep = step; +} + +// NOTE: value is assumed to be scaled if using log +void ImageKnob::setValue(float value, bool sendCallback) noexcept +{ + if (d_isEqual(fValue, value)) + return; + + fValue = value; + + if (d_isZero(fStep)) + fValueTmp = value; + + if (fRotationAngle == 0) + fIsReady = false; + + repaint(); + + if (sendCallback && fCallback != nullptr) + { + try { + fCallback->imageKnobValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue"); + } +} + +void ImageKnob::setUsingLogScale(bool yesNo) noexcept +{ + fUsingLog = yesNo; +} + +void ImageKnob::setCallback(Callback* callback) noexcept +{ + fCallback = callback; +} + +void ImageKnob::setOrientation(Orientation orientation) noexcept +{ + if (fOrientation == orientation) + return; + + fOrientation = orientation; +} + +void ImageKnob::setRotationAngle(int angle) +{ + if (fRotationAngle == angle) + return; + + fRotationAngle = angle; + fIsReady = false; +} + +void ImageKnob::setImageLayerCount(uint count) noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(count > 1,); + + fImgLayerCount = count; + + if (fIsImgVertical) + fImgLayerHeight = fImage.getHeight()/count; + else + fImgLayerWidth = fImage.getWidth()/count; + + setSize(fImgLayerWidth, fImgLayerHeight); +} + +void ImageKnob::onDisplay() +{ + const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fTextureId); + + if (! fIsReady) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + uint imageDataOffset = 0; + + if (fRotationAngle == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,); + DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); + + const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); + const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); + + const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || + fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); + /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + static_cast(getWidth()), static_cast(getHeight()), 0, + asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); + + fIsReady = true; + } + + const int w = static_cast(getWidth()); + const int h = static_cast(getHeight()); + + if (fRotationAngle != 0) + { + glPushMatrix(); + + const int w2 = w/2; + const int h2 = h/2; + + glTranslatef(static_cast(w2), static_cast(h2), 0.0f); + glRotatef(normValue*static_cast(fRotationAngle), 0.0f, 0.0f, 1.0f); + + Rectangle(-w2, -h2, w, h).draw(); + + glPopMatrix(); + } + else + { + Rectangle(0, 0, w, h).draw(); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +bool ImageKnob::onMouse(const MouseEvent& ev) +{ + if (ev.button != 1) + return false; + + if (ev.press) + { + if (! contains(ev.pos)) + return false; + + if ((ev.mod & kModifierShift) != 0 && fUsingDefault) + { + setValue(fValueDef, true); + fValueTmp = fValue; + return true; + } + + fDragging = true; + fLastX = ev.pos.getX(); + fLastY = ev.pos.getY(); + + if (fCallback != nullptr) + fCallback->imageKnobDragStarted(this); + + return true; + } + else if (fDragging) + { + if (fCallback != nullptr) + fCallback->imageKnobDragFinished(this); + + fDragging = false; + return true; + } + + return false; +} + +bool ImageKnob::onMotion(const MotionEvent& ev) +{ + if (! fDragging) + return false; + + bool doVal = false; + float d, value = 0.0f; + + if (fOrientation == ImageKnob::Horizontal) + { + if (const int movX = ev.pos.getX() - fLastX) + { + d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX)); + doVal = true; + } + } + else if (fOrientation == ImageKnob::Vertical) + { + if (const int movY = fLastY - ev.pos.getY()) + { + d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY)); + doVal = true; + } + } + + if (! doVal) + return false; + + if (fUsingLog) + value = _logscale(value); + + if (value < fMinimum) + { + fValueTmp = value = fMinimum; + } + else if (value > fMaximum) + { + fValueTmp = value = fMaximum; + } + else if (d_isNotZero(fStep)) + { + fValueTmp = value; + const float rest = std::fmod(value, fStep); + value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + } + + setValue(value, true); + + fLastX = ev.pos.getX(); + fLastY = ev.pos.getY(); + + return true; +} + +bool ImageKnob::onScroll(const ScrollEvent& ev) +{ + if (! contains(ev.pos)) + return false; + + const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY()); + + if (fUsingLog) + value = _logscale(value); + + if (value < fMinimum) + { + fValueTmp = value = fMinimum; + } + else if (value > fMaximum) + { + fValueTmp = value = fMaximum; + } + else if (d_isNotZero(fStep)) + { + fValueTmp = value; + const float rest = std::fmod(value, fStep); + value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + } + + setValue(value, true); + return true; +} + +// ----------------------------------------------------------------------- + +float ImageKnob::_logscale(float value) const +{ + const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); + const float a = fMaximum/std::exp(fMaximum*b); + return a * std::exp(b*value); +} + +float ImageKnob::_invlogscale(float value) const +{ + const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); + const float a = fMaximum/std::exp(fMaximum*b); + return std::log(value/a)/b; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#if 0 +ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept + : SubWidget(parentWidget), + fImage(image), + fMinimum(0.0f), + fMaximum(1.0f), + fStep(0.0f), + fValue(0.5f), + fValueDef(fValue), + fValueTmp(fValue), + fUsingDefault(false), + fDragging(false), + fInverted(false), + fValueIsSet(false), + fStartedX(0), + fStartedY(0), + fCallback(nullptr), + fStartPos(), + fEndPos(), + fSliderArea() +{ + setNeedsFullViewportDrawing(); +} + +float ImageSlider::getValue() const noexcept +{ + return fValue; +} + +void ImageSlider::setValue(float value, bool sendCallback) noexcept +{ + if (! fValueIsSet) + fValueIsSet = true; + + if (d_isEqual(fValue, value)) + return; + + fValue = value; + + if (d_isZero(fStep)) + fValueTmp = value; + + repaint(); + + if (sendCallback && fCallback != nullptr) + { + try { + fCallback->imageSliderValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue"); + } +} + +void ImageSlider::setStartPos(const Point& startPos) noexcept +{ + fStartPos = startPos; + _recheckArea(); +} + +void ImageSlider::setStartPos(int x, int y) noexcept +{ + setStartPos(Point(x, y)); +} + +void ImageSlider::setEndPos(const Point& endPos) noexcept +{ + fEndPos = endPos; + _recheckArea(); +} + +void ImageSlider::setEndPos(int x, int y) noexcept +{ + setEndPos(Point(x, y)); +} + +void ImageSlider::setInverted(bool inverted) noexcept +{ + if (fInverted == inverted) + return; + + fInverted = inverted; + repaint(); +} + +void ImageSlider::setDefault(float value) noexcept +{ + fValueDef = value; + fUsingDefault = true; +} + +void ImageSlider::setRange(float min, float max) noexcept +{ + fMinimum = min; + fMaximum = max; + + if (fValue < min) + { + fValue = min; + repaint(); + + if (fCallback != nullptr && fValueIsSet) + { + try { + fCallback->imageSliderValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); + } + } + else if (fValue > max) + { + fValue = max; + repaint(); + + if (fCallback != nullptr && fValueIsSet) + { + try { + fCallback->imageSliderValueChanged(this, fValue); + } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); + } + } +} + +void ImageSlider::setStep(float step) noexcept +{ + fStep = step; +} + +void ImageSlider::setCallback(Callback* callback) noexcept +{ + fCallback = callback; +} + +void ImageSlider::onDisplay() +{ +#if 0 // DEBUG, paints slider area + glColor3f(0.4f, 0.5f, 0.1f); + glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +#endif + + const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); + + int x, y; + + if (fStartPos.getY() == fEndPos.getY()) + { + // horizontal + if (fInverted) + x = fEndPos.getX() - static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); + else + x = fStartPos.getX() + static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); + + y = fStartPos.getY(); + } + else + { + // vertical + x = fStartPos.getX(); + + if (fInverted) + y = fEndPos.getY() - static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); + else + y = fStartPos.getY() + static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); + } + + fImage.drawAt(x, y); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#if 0 +bool ImageSlider::onMouse(const MouseEvent& ev) +{ + if (ev.button != 1) + return false; + + if (ev.press) + { + if (! fSliderArea.contains(ev.pos)) + return false; + + if ((ev.mod & kModifierShift) != 0 && fUsingDefault) + { + setValue(fValueDef, true); + fValueTmp = fValue; + return true; + } + + float vper; + const int x = ev.pos.getX(); + const int y = ev.pos.getY(); + + if (fStartPos.getY() == fEndPos.getY()) + { + // horizontal + vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + } + else + { + // vertical + vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + } + + float value; + + if (fInverted) + value = fMaximum - vper * (fMaximum - fMinimum); + else + value = fMinimum + vper * (fMaximum - fMinimum); + + if (value < fMinimum) + { + fValueTmp = value = fMinimum; + } + else if (value > fMaximum) + { + fValueTmp = value = fMaximum; + } + else if (d_isNotZero(fStep)) + { + fValueTmp = value; + const float rest = std::fmod(value, fStep); + value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + } + + fDragging = true; + fStartedX = x; + fStartedY = y; + + if (fCallback != nullptr) + fCallback->imageSliderDragStarted(this); + + setValue(value, true); + + return true; + } + else if (fDragging) + { + if (fCallback != nullptr) + fCallback->imageSliderDragFinished(this); + + fDragging = false; + return true; + } + + return false; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#if 0 +bool ImageSlider::onMotion(const MotionEvent& ev) +{ + if (! fDragging) + return false; + + const bool horizontal = fStartPos.getY() == fEndPos.getY(); + const int x = ev.pos.getX(); + const int y = ev.pos.getY(); + + if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) + { + float vper; + + if (horizontal) + { + // horizontal + vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + } + else + { + // vertical + vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + } + + float value; + + if (fInverted) + value = fMaximum - vper * (fMaximum - fMinimum); + else + value = fMinimum + vper * (fMaximum - fMinimum); + + if (value < fMinimum) + { + fValueTmp = value = fMinimum; + } + else if (value > fMaximum) + { + fValueTmp = value = fMaximum; + } + else if (d_isNotZero(fStep)) + { + fValueTmp = value; + const float rest = std::fmod(value, fStep); + value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + } + + setValue(value, true); + } + else if (horizontal) + { + if (x < fSliderArea.getX()) + setValue(fInverted ? fMaximum : fMinimum, true); + else + setValue(fInverted ? fMinimum : fMaximum, true); + } + else + { + if (y < fSliderArea.getY()) + setValue(fInverted ? fMaximum : fMinimum, true); + else + setValue(fInverted ? fMinimum : fMaximum, true); + } + + return true; +} + +void ImageSlider::_recheckArea() noexcept +{ + if (fStartPos.getY() == fEndPos.getY()) + { + // horizontal + fSliderArea = Rectangle(fStartPos.getX(), + fStartPos.getY(), + fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), + static_cast(fImage.getHeight())); + } + else + { + // vertical + fSliderArea = Rectangle(fStartPos.getX(), + fStartPos.getY(), + static_cast(fImage.getWidth()), + fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); + } +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#if 0 +ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept + : SubWidget(parentWidget), + fImageNormal(imageNormal), + fImageDown(imageDown), + fIsDown(false), + fCallback(nullptr) +{ + DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); + + setSize(fImageNormal.getSize()); +} + +ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept + : SubWidget(imageSwitch.getParentWidget()), + fImageNormal(imageSwitch.fImageNormal), + fImageDown(imageSwitch.fImageDown), + fIsDown(imageSwitch.fIsDown), + fCallback(imageSwitch.fCallback) +{ + DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); + + setSize(fImageNormal.getSize()); +} + +ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept +{ + fImageNormal = imageSwitch.fImageNormal; + fImageDown = imageSwitch.fImageDown; + fIsDown = imageSwitch.fIsDown; + fCallback = imageSwitch.fCallback; + + DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); + + setSize(fImageNormal.getSize()); + + return *this; +} + +bool ImageSwitch::isDown() const noexcept +{ + return fIsDown; +} + +void ImageSwitch::setDown(bool down) noexcept +{ + if (fIsDown == down) + return; + + fIsDown = down; + repaint(); +} + +void ImageSwitch::setCallback(Callback* callback) noexcept +{ + fCallback = callback; +} + +void ImageSwitch::onDisplay() +{ + if (fIsDown) + fImageDown.draw(); + else + fImageNormal.draw(); +} + +bool ImageSwitch::onMouse(const MouseEvent& ev) +{ + if (ev.press && contains(ev.pos)) + { + fIsDown = !fIsDown; + + repaint(); + + if (fCallback != nullptr) + fCallback->imageSwitchClicked(this, fIsDown); + + return true; + } + + return false; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL diff --git a/dgl/src/ImageWidgets.cpp b/dgl/src/ImageWidgets.cpp deleted file mode 100644 index 41d4f1a7..00000000 --- a/dgl/src/ImageWidgets.cpp +++ /dev/null @@ -1,876 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho - * - * 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 "../ImageWidgets.hpp" -#include "WidgetPrivateData.hpp" - -// TODO make this code more generic and move GL specific bits to OpenGL.cpp -#include "../OpenGL.hpp" - -// TODO switch to use templated image type after merging widget-related PRs -#if defined(__GNUC__) && (__GNUC__ >= 6) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -START_NAMESPACE_DGL - -// ----------------------------------------------------------------------- - -ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept - : SubWidget(parentWidget), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fUsingLog(false), - fOrientation(orientation), - fRotationAngle(0), - fDragging(false), - fLastX(0), - fLastY(0), - fCallback(nullptr), - fIsImgVertical(image.getHeight() > image.getWidth()), - fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), - fImgLayerHeight(fImgLayerWidth), - fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), - fIsReady(false), - fTextureId(0) -{ - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); -} - -ImageKnob::ImageKnob(const ImageKnob& imageKnob) - : SubWidget(imageKnob.getParentWidget()), - fImage(imageKnob.fImage), - fMinimum(imageKnob.fMinimum), - fMaximum(imageKnob.fMaximum), - fStep(imageKnob.fStep), - fValue(imageKnob.fValue), - fValueDef(imageKnob.fValueDef), - fValueTmp(fValue), - fUsingDefault(imageKnob.fUsingDefault), - fUsingLog(imageKnob.fUsingLog), - fOrientation(imageKnob.fOrientation), - fRotationAngle(imageKnob.fRotationAngle), - fDragging(false), - fLastX(0), - fLastY(0), - fCallback(imageKnob.fCallback), - fIsImgVertical(imageKnob.fIsImgVertical), - fImgLayerWidth(imageKnob.fImgLayerWidth), - fImgLayerHeight(imageKnob.fImgLayerHeight), - fImgLayerCount(imageKnob.fImgLayerCount), - fIsReady(false), - fTextureId(0) -{ - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); -} - -ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) -{ - fImage = imageKnob.fImage; - fMinimum = imageKnob.fMinimum; - fMaximum = imageKnob.fMaximum; - fStep = imageKnob.fStep; - fValue = imageKnob.fValue; - fValueDef = imageKnob.fValueDef; - fValueTmp = fValue; - fUsingDefault = imageKnob.fUsingDefault; - fUsingLog = imageKnob.fUsingLog; - fOrientation = imageKnob.fOrientation; - fRotationAngle = imageKnob.fRotationAngle; - fDragging = false; - fLastX = 0; - fLastY = 0; - fCallback = imageKnob.fCallback; - fIsImgVertical = imageKnob.fIsImgVertical; - fImgLayerWidth = imageKnob.fImgLayerWidth; - fImgLayerHeight = imageKnob.fImgLayerHeight; - fImgLayerCount = imageKnob.fImgLayerCount; - fIsReady = false; - - if (fTextureId != 0) - { - glDeleteTextures(1, &fTextureId); - fTextureId = 0; - } - - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); - - return *this; -} - -ImageKnob::~ImageKnob() -{ - if (fTextureId != 0) - { - glDeleteTextures(1, &fTextureId); - fTextureId = 0; - } -} - -float ImageKnob::getValue() const noexcept -{ - return fValue; -} - -// NOTE: value is assumed to be scaled if using log -void ImageKnob::setDefault(float value) noexcept -{ - fValueDef = value; - fUsingDefault = true; -} - -void ImageKnob::setRange(float min, float max) noexcept -{ - DISTRHO_SAFE_ASSERT_RETURN(max > min,); - - if (fValue < min) - { - fValue = min; - repaint(); - - if (fCallback != nullptr) - { - try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min"); - } - } - else if (fValue > max) - { - fValue = max; - repaint(); - - if (fCallback != nullptr) - { - try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max"); - } - } - - fMinimum = min; - fMaximum = max; -} - -void ImageKnob::setStep(float step) noexcept -{ - fStep = step; -} - -// NOTE: value is assumed to be scaled if using log -void ImageKnob::setValue(float value, bool sendCallback) noexcept -{ - if (d_isEqual(fValue, value)) - return; - - fValue = value; - - if (d_isZero(fStep)) - fValueTmp = value; - - if (fRotationAngle == 0) - fIsReady = false; - - repaint(); - - if (sendCallback && fCallback != nullptr) - { - try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue"); - } -} - -void ImageKnob::setUsingLogScale(bool yesNo) noexcept -{ - fUsingLog = yesNo; -} - -void ImageKnob::setCallback(Callback* callback) noexcept -{ - fCallback = callback; -} - -void ImageKnob::setOrientation(Orientation orientation) noexcept -{ - if (fOrientation == orientation) - return; - - fOrientation = orientation; -} - -void ImageKnob::setRotationAngle(int angle) -{ - if (fRotationAngle == angle) - return; - - fRotationAngle = angle; - fIsReady = false; -} - -void ImageKnob::setImageLayerCount(uint count) noexcept -{ - DISTRHO_SAFE_ASSERT_RETURN(count > 1,); - - fImgLayerCount = count; - - if (fIsImgVertical) - fImgLayerHeight = fImage.getHeight()/count; - else - fImgLayerWidth = fImage.getWidth()/count; - - setSize(fImgLayerWidth, fImgLayerHeight); -} - -void ImageKnob::onDisplay() -{ - const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fTextureId); - - if (! fIsReady) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - uint imageDataOffset = 0; - - if (fRotationAngle == 0) - { - DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,); - DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); - - const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); - const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); - - const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || - fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); - /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); - } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(getWidth()), static_cast(getHeight()), 0, - asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); - - fIsReady = true; - } - - const int w = static_cast(getWidth()); - const int h = static_cast(getHeight()); - - if (fRotationAngle != 0) - { - glPushMatrix(); - - const int w2 = w/2; - const int h2 = h/2; - - glTranslatef(static_cast(w2), static_cast(h2), 0.0f); - glRotatef(normValue*static_cast(fRotationAngle), 0.0f, 0.0f, 1.0f); - - Rectangle(-w2, -h2, w, h).draw(); - - glPopMatrix(); - } - else - { - Rectangle(0, 0, w, h).draw(); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -bool ImageKnob::onMouse(const MouseEvent& ev) -{ - if (ev.button != 1) - return false; - - if (ev.press) - { - if (! contains(ev.pos)) - return false; - - if ((ev.mod & kModifierShift) != 0 && fUsingDefault) - { - setValue(fValueDef, true); - fValueTmp = fValue; - return true; - } - - fDragging = true; - fLastX = ev.pos.getX(); - fLastY = ev.pos.getY(); - - if (fCallback != nullptr) - fCallback->imageKnobDragStarted(this); - - return true; - } - else if (fDragging) - { - if (fCallback != nullptr) - fCallback->imageKnobDragFinished(this); - - fDragging = false; - return true; - } - - return false; -} - -bool ImageKnob::onMotion(const MotionEvent& ev) -{ - if (! fDragging) - return false; - - bool doVal = false; - float d, value = 0.0f; - - if (fOrientation == ImageKnob::Horizontal) - { - if (const int movX = ev.pos.getX() - fLastX) - { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX)); - doVal = true; - } - } - else if (fOrientation == ImageKnob::Vertical) - { - if (const int movY = fLastY - ev.pos.getY()) - { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY)); - doVal = true; - } - } - - if (! doVal) - return false; - - if (fUsingLog) - value = _logscale(value); - - if (value < fMinimum) - { - fValueTmp = value = fMinimum; - } - else if (value > fMaximum) - { - fValueTmp = value = fMaximum; - } - else if (d_isNotZero(fStep)) - { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); - } - - setValue(value, true); - - fLastX = ev.pos.getX(); - fLastY = ev.pos.getY(); - - return true; -} - -bool ImageKnob::onScroll(const ScrollEvent& ev) -{ - if (! contains(ev.pos)) - return false; - - const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY()); - - if (fUsingLog) - value = _logscale(value); - - if (value < fMinimum) - { - fValueTmp = value = fMinimum; - } - else if (value > fMaximum) - { - fValueTmp = value = fMaximum; - } - else if (d_isNotZero(fStep)) - { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); - } - - setValue(value, true); - return true; -} - -// ----------------------------------------------------------------------- - -float ImageKnob::_logscale(float value) const -{ - const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); - const float a = fMaximum/std::exp(fMaximum*b); - return a * std::exp(b*value); -} - -float ImageKnob::_invlogscale(float value) const -{ - const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); - const float a = fMaximum/std::exp(fMaximum*b); - return std::log(value/a)/b; -} - -// ----------------------------------------------------------------------- - -ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept - : SubWidget(parentWidget), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fDragging(false), - fInverted(false), - fValueIsSet(false), - fStartedX(0), - fStartedY(0), - fCallback(nullptr), - fStartPos(), - fEndPos(), - fSliderArea() -{ - setNeedsFullViewportDrawing(); -} - -float ImageSlider::getValue() const noexcept -{ - return fValue; -} - -void ImageSlider::setValue(float value, bool sendCallback) noexcept -{ - if (! fValueIsSet) - fValueIsSet = true; - - if (d_isEqual(fValue, value)) - return; - - fValue = value; - - if (d_isZero(fStep)) - fValueTmp = value; - - repaint(); - - if (sendCallback && fCallback != nullptr) - { - try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue"); - } -} - -void ImageSlider::setStartPos(const Point& startPos) noexcept -{ - fStartPos = startPos; - _recheckArea(); -} - -void ImageSlider::setStartPos(int x, int y) noexcept -{ - setStartPos(Point(x, y)); -} - -void ImageSlider::setEndPos(const Point& endPos) noexcept -{ - fEndPos = endPos; - _recheckArea(); -} - -void ImageSlider::setEndPos(int x, int y) noexcept -{ - setEndPos(Point(x, y)); -} - -void ImageSlider::setInverted(bool inverted) noexcept -{ - if (fInverted == inverted) - return; - - fInverted = inverted; - repaint(); -} - -void ImageSlider::setDefault(float value) noexcept -{ - fValueDef = value; - fUsingDefault = true; -} - -void ImageSlider::setRange(float min, float max) noexcept -{ - fMinimum = min; - fMaximum = max; - - if (fValue < min) - { - fValue = min; - repaint(); - - if (fCallback != nullptr && fValueIsSet) - { - try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); - } - } - else if (fValue > max) - { - fValue = max; - repaint(); - - if (fCallback != nullptr && fValueIsSet) - { - try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); - } - } -} - -void ImageSlider::setStep(float step) noexcept -{ - fStep = step; -} - -void ImageSlider::setCallback(Callback* callback) noexcept -{ - fCallback = callback; -} - -void ImageSlider::onDisplay() -{ -#if 0 // DEBUG, paints slider area - glColor3f(0.4f, 0.5f, 0.1f); - glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -#endif - - const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); - - int x, y; - - if (fStartPos.getY() == fEndPos.getY()) - { - // horizontal - if (fInverted) - x = fEndPos.getX() - static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); - else - x = fStartPos.getX() + static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); - - y = fStartPos.getY(); - } - else - { - // vertical - x = fStartPos.getX(); - - if (fInverted) - y = fEndPos.getY() - static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); - else - y = fStartPos.getY() + static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); - } - - fImage.drawAt(x, y); -} - -bool ImageSlider::onMouse(const MouseEvent& ev) -{ - if (ev.button != 1) - return false; - - if (ev.press) - { - if (! fSliderArea.contains(ev.pos)) - return false; - - if ((ev.mod & kModifierShift) != 0 && fUsingDefault) - { - setValue(fValueDef, true); - fValueTmp = fValue; - return true; - } - - float vper; - const int x = ev.pos.getX(); - const int y = ev.pos.getY(); - - if (fStartPos.getY() == fEndPos.getY()) - { - // horizontal - vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); - } - else - { - // vertical - vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); - } - - float value; - - if (fInverted) - value = fMaximum - vper * (fMaximum - fMinimum); - else - value = fMinimum + vper * (fMaximum - fMinimum); - - if (value < fMinimum) - { - fValueTmp = value = fMinimum; - } - else if (value > fMaximum) - { - fValueTmp = value = fMaximum; - } - else if (d_isNotZero(fStep)) - { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); - } - - fDragging = true; - fStartedX = x; - fStartedY = y; - - if (fCallback != nullptr) - fCallback->imageSliderDragStarted(this); - - setValue(value, true); - - return true; - } - else if (fDragging) - { - if (fCallback != nullptr) - fCallback->imageSliderDragFinished(this); - - fDragging = false; - return true; - } - - return false; -} - -bool ImageSlider::onMotion(const MotionEvent& ev) -{ - if (! fDragging) - return false; - - const bool horizontal = fStartPos.getY() == fEndPos.getY(); - const int x = ev.pos.getX(); - const int y = ev.pos.getY(); - - if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) - { - float vper; - - if (horizontal) - { - // horizontal - vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); - } - else - { - // vertical - vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); - } - - float value; - - if (fInverted) - value = fMaximum - vper * (fMaximum - fMinimum); - else - value = fMinimum + vper * (fMaximum - fMinimum); - - if (value < fMinimum) - { - fValueTmp = value = fMinimum; - } - else if (value > fMaximum) - { - fValueTmp = value = fMaximum; - } - else if (d_isNotZero(fStep)) - { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); - } - - setValue(value, true); - } - else if (horizontal) - { - if (x < fSliderArea.getX()) - setValue(fInverted ? fMaximum : fMinimum, true); - else - setValue(fInverted ? fMinimum : fMaximum, true); - } - else - { - if (y < fSliderArea.getY()) - setValue(fInverted ? fMaximum : fMinimum, true); - else - setValue(fInverted ? fMinimum : fMaximum, true); - } - - return true; -} - -void ImageSlider::_recheckArea() noexcept -{ - if (fStartPos.getY() == fEndPos.getY()) - { - // horizontal - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), - static_cast(fImage.getHeight())); - } - else - { - // vertical - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - static_cast(fImage.getWidth()), - fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); - } -} - -// ----------------------------------------------------------------------- - -ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept - : SubWidget(parentWidget), - fImageNormal(imageNormal), - fImageDown(imageDown), - fIsDown(false), - fCallback(nullptr) -{ - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); -} - -ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept - : SubWidget(imageSwitch.getParentWidget()), - fImageNormal(imageSwitch.fImageNormal), - fImageDown(imageSwitch.fImageDown), - fIsDown(imageSwitch.fIsDown), - fCallback(imageSwitch.fCallback) -{ - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); -} - -ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept -{ - fImageNormal = imageSwitch.fImageNormal; - fImageDown = imageSwitch.fImageDown; - fIsDown = imageSwitch.fIsDown; - fCallback = imageSwitch.fCallback; - - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); - - return *this; -} - -bool ImageSwitch::isDown() const noexcept -{ - return fIsDown; -} - -void ImageSwitch::setDown(bool down) noexcept -{ - if (fIsDown == down) - return; - - fIsDown = down; - repaint(); -} - -void ImageSwitch::setCallback(Callback* callback) noexcept -{ - fCallback = callback; -} - -void ImageSwitch::onDisplay() -{ - if (fIsDown) - fImageDown.draw(); - else - fImageNormal.draw(); -} - -bool ImageSwitch::onMouse(const MouseEvent& ev) -{ - if (ev.press && contains(ev.pos)) - { - fIsDown = !fIsDown; - - repaint(); - - if (fCallback != nullptr) - fCallback->imageSwitchClicked(this, fIsDown); - - return true; - } - - return false; -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DGL - -#if defined(__GNUC__) && (__GNUC__ >= 6) -# pragma GCC diagnostic pop -#endif From f0c52659b94d2bc1c4e49ca9009e434a19f4cd90 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 19:30:22 +0100 Subject: [PATCH 124/159] Generic ImageBaseSwitch --- dgl/ImageBaseWidgets.hpp | 8 +-- dgl/src/ImageBaseWidgets.cpp | 125 +++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 50 deletions(-) diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 33a72639..7cb25a90 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -255,6 +255,7 @@ public: explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept; explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept; ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept; + ~ImageBaseSwitch() override; bool isDown() const noexcept; void setDown(bool down) noexcept; @@ -269,13 +270,6 @@ private: struct PrivateData; PrivateData* const pData; - /* - Image fImageNormal; - Image fImageDown; - bool fIsDown; - Callback* fCallback; - */ - DISTRHO_LEAK_DETECTOR(ImageBaseSwitch) }; diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index b9e46896..f9652d4b 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -947,89 +947,128 @@ void ImageSlider::_recheckArea() noexcept // -------------------------------------------------------------------------------------------------------------------- -#if 0 -ImageSwitch::ImageSwitch(Widget* parentWidget, const Image& imageNormal, const Image& imageDown) noexcept +template +struct ImageBaseSwitch::PrivateData { + ImageBaseSwitch* const self; + ImageType imageNormal; + ImageType imageDown; + bool isDown; + Callback* callback; + + PrivateData(ImageBaseSwitch* const s, const ImageType& normal, const ImageType& down) + : self(s), + imageNormal(normal), + imageDown(down), + isDown(false), + callback(nullptr) + { + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + PrivateData(ImageBaseSwitch* const s, PrivateData* const other) + : self(s), + imageNormal(other->imageNormal), + imageDown(other->imageDown), + isDown(other->isDown), + callback(other->callback) + { + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + void assignFrom(PrivateData* const other) + { + imageNormal = other->imageNormal; + imageDown = other->imageDown; + isDown = other->isDown; + callback = other->callback; + DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// -------------------------------------------------------------------------------------------------------------------- + +template +ImageBaseSwitch::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept : SubWidget(parentWidget), - fImageNormal(imageNormal), - fImageDown(imageDown), - fIsDown(false), - fCallback(nullptr) + pData(new PrivateData(this, imageNormal, imageDown)) { - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); + setSize(imageNormal.getSize()); } -ImageSwitch::ImageSwitch(const ImageSwitch& imageSwitch) noexcept +template +ImageBaseSwitch::ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept : SubWidget(imageSwitch.getParentWidget()), - fImageNormal(imageSwitch.fImageNormal), - fImageDown(imageSwitch.fImageDown), - fIsDown(imageSwitch.fIsDown), - fCallback(imageSwitch.fCallback) + pData(new PrivateData(this, imageSwitch.pData)) { - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); + setSize(pData->imageNormal.getSize()); } -ImageSwitch& ImageSwitch::operator=(const ImageSwitch& imageSwitch) noexcept +template +ImageBaseSwitch& ImageBaseSwitch::operator=(const ImageBaseSwitch& imageSwitch) noexcept { - fImageNormal = imageSwitch.fImageNormal; - fImageDown = imageSwitch.fImageDown; - fIsDown = imageSwitch.fIsDown; - fCallback = imageSwitch.fCallback; - - DISTRHO_SAFE_ASSERT(fImageNormal.getSize() == fImageDown.getSize()); - - setSize(fImageNormal.getSize()); - + pData->assignFrom(imageSwitch.pData); + setSize(pData->imageNormal.getSize()); return *this; } -bool ImageSwitch::isDown() const noexcept +template +ImageBaseSwitch::~ImageBaseSwitch() { - return fIsDown; + delete pData; } -void ImageSwitch::setDown(bool down) noexcept +template +bool ImageBaseSwitch::isDown() const noexcept { - if (fIsDown == down) + return pData->isDown; +} + +template +void ImageBaseSwitch::setDown(const bool down) noexcept +{ + if (pData->isDown == down) return; - fIsDown = down; + pData->isDown = down; repaint(); } -void ImageSwitch::setCallback(Callback* callback) noexcept +template +void ImageBaseSwitch::setCallback(Callback* const callback) noexcept { - fCallback = callback; + pData->callback = callback; } -void ImageSwitch::onDisplay() +template +void ImageBaseSwitch::onDisplay() { - if (fIsDown) - fImageDown.draw(); + const GraphicsContext& context(getGraphicsContext()); + + if (pData->isDown) + pData->imageDown.draw(context); else - fImageNormal.draw(); + pData->imageNormal.draw(context); } -bool ImageSwitch::onMouse(const MouseEvent& ev) +template +bool ImageBaseSwitch::onMouse(const MouseEvent& ev) { if (ev.press && contains(ev.pos)) { - fIsDown = !fIsDown; + pData->isDown = !pData->isDown; repaint(); - if (fCallback != nullptr) - fCallback->imageSwitchClicked(this, fIsDown); + if (pData->callback != nullptr) + pData->callback->imageSwitchClicked(this, pData->isDown); return true; } return false; } -#endif // -------------------------------------------------------------------------------------------------------------------- From 5f2e1f37075924176acc65561793c9329373ea93 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 21 May 2021 20:35:02 +0100 Subject: [PATCH 125/159] Quick and rough conversion of Image widgets fMember to pData --- dgl/ImageBaseWidgets.hpp | 56 +-- dgl/src/Cairo.cpp | 42 +- dgl/src/Common.hpp | 58 +++ dgl/src/ImageBaseWidgets.cpp | 845 +++++++++++++++++------------------ dgl/src/OpenGL.cpp | 109 +++++ 5 files changed, 627 insertions(+), 483 deletions(-) diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 7cb25a90..20682aec 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -129,36 +129,6 @@ private: struct PrivateData; PrivateData* const pData; - /* - Image fImage; - float fMinimum; - float fMaximum; - float fStep; - float fValue; - float fValueDef; - float fValueTmp; - bool fUsingDefault; - bool fUsingLog; - Orientation fOrientation; - - int fRotationAngle; - bool fDragging; - int fLastX; - int fLastY; - - Callback* fCallback; - - bool fIsImgVertical; - uint fImgLayerWidth; - uint fImgLayerHeight; - uint fImgLayerCount; - bool fIsReady; - GLuint fTextureId; - - float _logscale(float value) const; - float _invlogscale(float value) const; - */ - DISTRHO_LEAK_DETECTOR(ImageBaseKnob) }; @@ -180,6 +150,7 @@ public: }; explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept; + ~ImageBaseSlider() override; float getValue() const noexcept; void setValue(float value, bool sendCallback = false) noexcept; @@ -205,31 +176,6 @@ private: struct PrivateData; PrivateData* const pData; - /* - Image fImage; - float fMinimum; - float fMaximum; - float fStep; - float fValue; - float fValueDef; - float fValueTmp; - bool fUsingDefault; - - bool fDragging; - bool fInverted; - bool fValueIsSet; - int fStartedX; - int fStartedY; - - Callback* fCallback; - - Point fStartPos; - Point fEndPos; - Rectangle fSliderArea; - - void _recheckArea() noexcept; - */ - // these should not be used void setAbsoluteX(int) const noexcept {} void setAbsoluteY(int) const noexcept {} diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 0671345e..f7df8d8d 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -18,6 +18,7 @@ #include "../Color.hpp" #include "../ImageBaseWidgets.hpp" +#include "Common.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" @@ -216,7 +217,6 @@ template class Triangle; template class Triangle; template class Triangle; - // ----------------------------------------------------------------------- // Rectangle @@ -461,16 +461,56 @@ CairoBaseWidget::CairoBaseWidget(Application& app, Window& par template class CairoBaseWidget; // ----------------------------------------------------------------------- +// ImageBaseAboutWindow +#if 0 template <> void ImageBaseAboutWindow::onDisplay() { img.draw(getGraphicsContext()); } +#endif template class ImageBaseAboutWindow; + +// ----------------------------------------------------------------------- +// ImageBaseButton + template class ImageBaseButton; +// ----------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + notImplemented("ImageBaseKnob::PrivateData::init"); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + notImplemented("ImageBaseKnob::PrivateData::cleanup"); +} + +template <> +void ImageBaseKnob::onDisplay() +{ + notImplemented("ImageBaseKnob::onDisplay"); +} + +template class ImageBaseKnob; + +// ----------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// ----------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index 5bbac85b..e34d22be 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -120,6 +120,64 @@ struct ButtonImpl { // ----------------------------------------------------------------------- +template +struct ImageBaseKnob::PrivateData { + ImageType image; + float minimum; + float maximum; + float step; + float value; + float valueDef; + float valueTmp; + bool usingDefault; + bool usingLog; + Orientation orientation; + + int rotationAngle; + bool dragging; + int lastX; + int lastY; + + Callback* callback; + + bool isImgVertical; + uint imgLayerWidth; + uint imgLayerHeight; + uint imgLayerCount; + bool isReady; + /*GL*/uint textureId; + + explicit PrivateData(const ImageType& img, const Orientation o); + explicit PrivateData(PrivateData* const other); + void assignFrom(PrivateData* const other); + + ~PrivateData() + { + cleanup(); + } + + void init(); + void cleanup(); + + inline float logscale(float value) const + { + const float b = std::log(maximum/minimum)/(maximum-minimum); + const float a = maximum/std::exp(maximum*b); + return a * std::exp(b*value); + } + + inline float invlogscale(float value) const + { + const float b = std::log(maximum/minimum)/(maximum-minimum); + const float a = maximum/std::exp(maximum*b); + return std::log(value/a)/b; + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; + +// ----------------------------------------------------------------------- + END_NAMESPACE_DGL #endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index f9652d4b..147b797b 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -15,6 +15,7 @@ */ #include "../ImageBaseWidgets.hpp" +#include "../Color.hpp" #include "Common.hpp" START_NAMESPACE_DGL @@ -55,6 +56,12 @@ void ImageBaseAboutWindow::setImage(const ImageType& image) setSize(image.getSize()); } +template +void ImageBaseAboutWindow::onDisplay() +{ + img.draw(getGraphicsContext()); +} + template bool ImageBaseAboutWindow::onKeyboard(const KeyboardEvent& ev) { @@ -180,291 +187,246 @@ bool ImageBaseButton::onMotion(const MotionEvent& ev) // -------------------------------------------------------------------------------------------------------------------- -#if 0 -ImageKnob::ImageKnob(Widget* const parentWidget, const Image& image, Orientation orientation) noexcept - : SubWidget(parentWidget), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fUsingLog(false), - fOrientation(orientation), - fRotationAngle(0), - fDragging(false), - fLastX(0), - fLastY(0), - fCallback(nullptr), - fIsImgVertical(image.getHeight() > image.getWidth()), - fImgLayerWidth(fIsImgVertical ? image.getWidth() : image.getHeight()), - fImgLayerHeight(fImgLayerWidth), - fImgLayerCount(fIsImgVertical ? image.getHeight()/fImgLayerHeight : image.getWidth()/fImgLayerWidth), - fIsReady(false), - fTextureId(0) +template +ImageBaseKnob::PrivateData::PrivateData(const ImageType& img, const Orientation o) + : image(img), + minimum(0.0f), + maximum(1.0f), + step(0.0f), + value(0.5f), + valueDef(value), + valueTmp(value), + usingDefault(false), + usingLog(false), + orientation(o), + rotationAngle(0), + dragging(false), + lastX(0), + lastY(0), + callback(nullptr), + isImgVertical(img.getHeight() > img.getWidth()), + imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), + imgLayerHeight(imgLayerWidth), + imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), + isReady(false), + textureId(0) { - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); + init(); } -ImageKnob::ImageKnob(const ImageKnob& imageKnob) - : SubWidget(imageKnob.getParentWidget()), - fImage(imageKnob.fImage), - fMinimum(imageKnob.fMinimum), - fMaximum(imageKnob.fMaximum), - fStep(imageKnob.fStep), - fValue(imageKnob.fValue), - fValueDef(imageKnob.fValueDef), - fValueTmp(fValue), - fUsingDefault(imageKnob.fUsingDefault), - fUsingLog(imageKnob.fUsingLog), - fOrientation(imageKnob.fOrientation), - fRotationAngle(imageKnob.fRotationAngle), - fDragging(false), - fLastX(0), - fLastY(0), - fCallback(imageKnob.fCallback), - fIsImgVertical(imageKnob.fIsImgVertical), - fImgLayerWidth(imageKnob.fImgLayerWidth), - fImgLayerHeight(imageKnob.fImgLayerHeight), - fImgLayerCount(imageKnob.fImgLayerCount), - fIsReady(false), - fTextureId(0) +template +ImageBaseKnob::PrivateData::PrivateData(PrivateData* const other) + : image(other->image), + minimum(other->minimum), + maximum(other->maximum), + step(other->step), + value(other->value), + valueDef(other->valueDef), + valueTmp(value), + usingDefault(other->usingDefault), + usingLog(other->usingLog), + orientation(other->orientation), + rotationAngle(other->rotationAngle), + dragging(false), + lastX(0), + lastY(0), + callback(other->callback), + isImgVertical(other->isImgVertical), + imgLayerWidth(other->imgLayerWidth), + imgLayerHeight(other->imgLayerHeight), + imgLayerCount(other->imgLayerCount), + isReady(false), + textureId(0) { - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); + init(); } -ImageKnob& ImageKnob::operator=(const ImageKnob& imageKnob) +template +void ImageBaseKnob::PrivateData::assignFrom(PrivateData* const other) { - fImage = imageKnob.fImage; - fMinimum = imageKnob.fMinimum; - fMaximum = imageKnob.fMaximum; - fStep = imageKnob.fStep; - fValue = imageKnob.fValue; - fValueDef = imageKnob.fValueDef; - fValueTmp = fValue; - fUsingDefault = imageKnob.fUsingDefault; - fUsingLog = imageKnob.fUsingLog; - fOrientation = imageKnob.fOrientation; - fRotationAngle = imageKnob.fRotationAngle; - fDragging = false; - fLastX = 0; - fLastY = 0; - fCallback = imageKnob.fCallback; - fIsImgVertical = imageKnob.fIsImgVertical; - fImgLayerWidth = imageKnob.fImgLayerWidth; - fImgLayerHeight = imageKnob.fImgLayerHeight; - fImgLayerCount = imageKnob.fImgLayerCount; - fIsReady = false; - - if (fTextureId != 0) - { - glDeleteTextures(1, &fTextureId); - fTextureId = 0; - } + cleanup(); + image = other->image; + minimum = other->minimum; + maximum = other->maximum; + step = other->step; + value = other->value; + valueDef = other->valueDef; + valueTmp = value; + usingDefault = other->usingDefault; + usingLog = other->usingLog; + orientation = other->orientation; + rotationAngle = other->rotationAngle; + dragging = false; + lastX = 0; + lastY = 0; + callback = other->callback; + isImgVertical = other->isImgVertical; + imgLayerWidth = other->imgLayerWidth; + imgLayerHeight = other->imgLayerHeight; + imgLayerCount = other->imgLayerCount; + isReady = false; + init(); +} + +// -------------------------------------------------------------------------------------------------------------------- - glGenTextures(1, &fTextureId); - setSize(fImgLayerWidth, fImgLayerHeight); +template +ImageBaseKnob::ImageBaseKnob(Widget* const parentWidget, const ImageType& image, const Orientation orientation) noexcept + : SubWidget(parentWidget), + pData(new PrivateData(image, orientation)) +{ + setSize(pData->imgLayerWidth, pData->imgLayerHeight); +} +template +ImageBaseKnob::ImageBaseKnob(const ImageBaseKnob& imageKnob) + : SubWidget(imageKnob.getParentWidget()), + pData(new PrivateData(imageKnob.pData)) +{ + setSize(pData->imgLayerWidth, pData->imgLayerHeight); +} + +template +ImageBaseKnob& ImageBaseKnob::operator=(const ImageBaseKnob& imageKnob) +{ + pData->assignFrom(imageKnob.pData); + setSize(pData->imgLayerWidth, pData->imgLayerHeight); return *this; } -ImageKnob::~ImageKnob() +template +ImageBaseKnob::~ImageBaseKnob() { - if (fTextureId != 0) - { - glDeleteTextures(1, &fTextureId); - fTextureId = 0; - } + delete pData; } -float ImageKnob::getValue() const noexcept +template +float ImageBaseKnob::getValue() const noexcept { - return fValue; + return pData->value; } // NOTE: value is assumed to be scaled if using log -void ImageKnob::setDefault(float value) noexcept +template +void ImageBaseKnob::setDefault(float value) noexcept { - fValueDef = value; - fUsingDefault = true; + pData->valueDef = value; + pData->usingDefault = true; } -void ImageKnob::setRange(float min, float max) noexcept +template +void ImageBaseKnob::setRange(float min, float max) noexcept { DISTRHO_SAFE_ASSERT_RETURN(max > min,); - if (fValue < min) + if (pData->value < min) { - fValue = min; + pData->value = min; repaint(); - if (fCallback != nullptr) + if (pData->callback != nullptr) { try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange < min"); + pData->callback->imageKnobValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setRange < min"); } } - else if (fValue > max) + else if (pData->value > max) { - fValue = max; + pData->value = max; repaint(); - if (fCallback != nullptr) + if (pData->callback != nullptr) { try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setRange > max"); + pData->callback->imageKnobValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setRange > max"); } } - fMinimum = min; - fMaximum = max; + pData->minimum = min; + pData->maximum = max; } -void ImageKnob::setStep(float step) noexcept +template +void ImageBaseKnob::setStep(float step) noexcept { - fStep = step; + pData->step = step; } // NOTE: value is assumed to be scaled if using log -void ImageKnob::setValue(float value, bool sendCallback) noexcept +template +void ImageBaseKnob::setValue(float value, bool sendCallback) noexcept { - if (d_isEqual(fValue, value)) + if (d_isEqual(pData->value, value)) return; - fValue = value; + pData->value = value; - if (d_isZero(fStep)) - fValueTmp = value; + if (d_isZero(pData->step)) + pData->valueTmp = value; - if (fRotationAngle == 0) - fIsReady = false; + if (pData->rotationAngle == 0) + pData->isReady = false; repaint(); - if (sendCallback && fCallback != nullptr) + if (sendCallback && pData->callback != nullptr) { try { - fCallback->imageKnobValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageKnob::setValue"); + pData->callback->imageKnobValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseKnob::setValue"); } } -void ImageKnob::setUsingLogScale(bool yesNo) noexcept +template +void ImageBaseKnob::setUsingLogScale(bool yesNo) noexcept { - fUsingLog = yesNo; + pData->usingLog = yesNo; } -void ImageKnob::setCallback(Callback* callback) noexcept +template +void ImageBaseKnob::setCallback(Callback* callback) noexcept { - fCallback = callback; + pData->callback = callback; } -void ImageKnob::setOrientation(Orientation orientation) noexcept +template +void ImageBaseKnob::setOrientation(Orientation orientation) noexcept { - if (fOrientation == orientation) + if (pData->orientation == orientation) return; - fOrientation = orientation; + pData->orientation = orientation; } -void ImageKnob::setRotationAngle(int angle) +template +void ImageBaseKnob::setRotationAngle(int angle) { - if (fRotationAngle == angle) + if (pData->rotationAngle == angle) return; - fRotationAngle = angle; - fIsReady = false; + pData->rotationAngle = angle; + pData->isReady = false; } -void ImageKnob::setImageLayerCount(uint count) noexcept +template +void ImageBaseKnob::setImageLayerCount(uint count) noexcept { DISTRHO_SAFE_ASSERT_RETURN(count > 1,); - fImgLayerCount = count; + pData->imgLayerCount = count; - if (fIsImgVertical) - fImgLayerHeight = fImage.getHeight()/count; + if (pData->isImgVertical) + pData->imgLayerHeight = pData->image.getHeight()/count; else - fImgLayerWidth = fImage.getWidth()/count; - - setSize(fImgLayerWidth, fImgLayerHeight); -} - -void ImageKnob::onDisplay() -{ - const float normValue = ((fUsingLog ? _invlogscale(fValue) : fValue) - fMinimum) / (fMaximum - fMinimum); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, fTextureId); - - if (! fIsReady) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - uint imageDataOffset = 0; - - if (fRotationAngle == 0) - { - DISTRHO_SAFE_ASSERT_RETURN(fImgLayerCount > 0,); - DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); - - const uint& v1(fIsImgVertical ? fImgLayerWidth : fImgLayerHeight); - const uint& v2(fIsImgVertical ? fImgLayerHeight : fImgLayerWidth); - - const uint layerDataSize = v1 * v2 * ((fImage.getFormat() == kImageFormatBGRA || - fImage.getFormat() == kImageFormatRGBA) ? 4 : 3); - /* */ imageDataOffset = layerDataSize * uint(normValue * float(fImgLayerCount-1)); - } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - static_cast(getWidth()), static_cast(getHeight()), 0, - asOpenGLImageFormat(fImage.getFormat()), GL_UNSIGNED_BYTE, fImage.getRawData() + imageDataOffset); - - fIsReady = true; - } - - const int w = static_cast(getWidth()); - const int h = static_cast(getHeight()); - - if (fRotationAngle != 0) - { - glPushMatrix(); - - const int w2 = w/2; - const int h2 = h/2; - - glTranslatef(static_cast(w2), static_cast(h2), 0.0f); - glRotatef(normValue*static_cast(fRotationAngle), 0.0f, 0.0f, 1.0f); - - Rectangle(-w2, -h2, w, h).draw(); - - glPopMatrix(); - } - else - { - Rectangle(0, 0, w, h).draw(); - } + pData->imgLayerWidth = pData->image.getWidth()/count; - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + setSize(pData->imgLayerWidth, pData->imgLayerHeight); } -bool ImageKnob::onMouse(const MouseEvent& ev) +template +bool ImageBaseKnob::onMouse(const MouseEvent& ev) { if (ev.button != 1) return false; @@ -474,57 +436,58 @@ bool ImageKnob::onMouse(const MouseEvent& ev) if (! contains(ev.pos)) return false; - if ((ev.mod & kModifierShift) != 0 && fUsingDefault) + if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) { - setValue(fValueDef, true); - fValueTmp = fValue; + setValue(pData->valueDef, true); + pData->valueTmp = pData->value; return true; } - fDragging = true; - fLastX = ev.pos.getX(); - fLastY = ev.pos.getY(); + pData->dragging = true; + pData->lastX = ev.pos.getX(); + pData->lastY = ev.pos.getY(); - if (fCallback != nullptr) - fCallback->imageKnobDragStarted(this); + if (pData->callback != nullptr) + pData->callback->imageKnobDragStarted(this); return true; } - else if (fDragging) + else if (pData->dragging) { - if (fCallback != nullptr) - fCallback->imageKnobDragFinished(this); + if (pData->callback != nullptr) + pData->callback->imageKnobDragFinished(this); - fDragging = false; + pData->dragging = false; return true; } return false; } -bool ImageKnob::onMotion(const MotionEvent& ev) +template +bool ImageBaseKnob::onMotion(const MotionEvent& ev) { - if (! fDragging) + if (! pData->dragging) return false; bool doVal = false; float d, value = 0.0f; - if (fOrientation == ImageKnob::Horizontal) + if (pData->orientation == ImageBaseKnob::Horizontal) { - if (const int movX = ev.pos.getX() - fLastX) + if (const int movX = ev.pos.getX() - pData->lastX) { d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movX)); + value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movX)); doVal = true; } } - else if (fOrientation == ImageKnob::Vertical) + else if (pData->orientation == ImageBaseKnob::Vertical) { - if (const int movY = fLastY - ev.pos.getY()) + if (const int movY = pData->lastY - ev.pos.getY()) { d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * float(movY)); + value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movY)); doVal = true; } } @@ -532,265 +495,320 @@ bool ImageKnob::onMotion(const MotionEvent& ev) if (! doVal) return false; - if (fUsingLog) - value = _logscale(value); + if (pData->usingLog) + value = pData->logscale(value); - if (value < fMinimum) + if (value < pData->minimum) { - fValueTmp = value = fMinimum; + pData->valueTmp = value = pData->minimum; } - else if (value > fMaximum) + else if (value > pData->maximum) { - fValueTmp = value = fMaximum; + pData->valueTmp = value = pData->maximum; } - else if (d_isNotZero(fStep)) + else if (d_isNotZero(pData->step)) { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); } setValue(value, true); - fLastX = ev.pos.getX(); - fLastY = ev.pos.getY(); + pData->lastX = ev.pos.getX(); + pData->lastY = ev.pos.getY(); return true; } -bool ImageKnob::onScroll(const ScrollEvent& ev) +template +bool ImageBaseKnob::onScroll(const ScrollEvent& ev) { if (! contains(ev.pos)) return false; const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - float value = (fUsingLog ? _invlogscale(fValueTmp) : fValueTmp) + (float(fMaximum - fMinimum) / d * 10.f * ev.delta.getY()); + float value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * 10.f * ev.delta.getY()); - if (fUsingLog) - value = _logscale(value); + if (pData->usingLog) + value = pData->logscale(value); - if (value < fMinimum) + if (value < pData->minimum) { - fValueTmp = value = fMinimum; + pData->valueTmp = value = pData->minimum; } - else if (value > fMaximum) + else if (value > pData->maximum) { - fValueTmp = value = fMaximum; + pData->valueTmp = value = pData->maximum; } - else if (d_isNotZero(fStep)) + else if (d_isNotZero(pData->step)) { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); } setValue(value, true); return true; } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- -float ImageKnob::_logscale(float value) const -{ - const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); - const float a = fMaximum/std::exp(fMaximum*b); - return a * std::exp(b*value); -} +template +struct ImageBaseSlider::PrivateData { + ImageType image; + float minimum; + float maximum; + float step; + float value; + float valueDef; + float valueTmp; + bool usingDefault; + + bool dragging; + bool inverted; + bool valueIsSet; + int startedX; + int startedY; -float ImageKnob::_invlogscale(float value) const -{ - const float b = std::log(fMaximum/fMinimum)/(fMaximum-fMinimum); - const float a = fMaximum/std::exp(fMaximum*b); - return std::log(value/a)/b; -} -#endif + Callback* callback; + + Point startPos; + Point endPos; + Rectangle sliderArea; + + PrivateData(const ImageType& img) + : image(img), + minimum(0.0f), + maximum(1.0f), + step(0.0f), + value(0.5f), + valueDef(value), + valueTmp(value), + usingDefault(false), + dragging(false), + inverted(false), + valueIsSet(false), + startedX(0), + startedY(0), + callback(nullptr), + startPos(), + endPos(), + sliderArea() {} + + void recheckArea() noexcept + { + if (startPos.getY() == endPos.getY()) + { + // horizontal + sliderArea = Rectangle(startPos.getX(), + startPos.getY(), + endPos.getX() + static_cast(image.getWidth()) - startPos.getX(), + static_cast(image.getHeight())); + } + else + { + // vertical + sliderArea = Rectangle(startPos.getX(), + startPos.getY(), + static_cast(image.getWidth()), + endPos.getY() + static_cast(image.getHeight()) - startPos.getY()); + } + } + + DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) +}; // -------------------------------------------------------------------------------------------------------------------- -#if 0 -ImageSlider::ImageSlider(Widget* const parentWidget, const Image& image) noexcept +template +ImageBaseSlider::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept : SubWidget(parentWidget), - fImage(image), - fMinimum(0.0f), - fMaximum(1.0f), - fStep(0.0f), - fValue(0.5f), - fValueDef(fValue), - fValueTmp(fValue), - fUsingDefault(false), - fDragging(false), - fInverted(false), - fValueIsSet(false), - fStartedX(0), - fStartedY(0), - fCallback(nullptr), - fStartPos(), - fEndPos(), - fSliderArea() + pData(new PrivateData(image)) { setNeedsFullViewportDrawing(); } -float ImageSlider::getValue() const noexcept +template +ImageBaseSlider::~ImageBaseSlider() +{ + delete pData; +} + +template +float ImageBaseSlider::getValue() const noexcept { - return fValue; + return pData->value; } -void ImageSlider::setValue(float value, bool sendCallback) noexcept +template +void ImageBaseSlider::setValue(float value, bool sendCallback) noexcept { - if (! fValueIsSet) - fValueIsSet = true; + if (! pData->valueIsSet) + pData->valueIsSet = true; - if (d_isEqual(fValue, value)) + if (d_isEqual(pData->value, value)) return; - fValue = value; + pData->value = value; - if (d_isZero(fStep)) - fValueTmp = value; + if (d_isZero(pData->step)) + pData->valueTmp = value; repaint(); - if (sendCallback && fCallback != nullptr) + if (sendCallback && pData->callback != nullptr) { try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setValue"); + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue"); } } -void ImageSlider::setStartPos(const Point& startPos) noexcept +template +void ImageBaseSlider::setStartPos(const Point& startPos) noexcept { - fStartPos = startPos; - _recheckArea(); + pData->startPos = startPos; + pData->recheckArea(); } -void ImageSlider::setStartPos(int x, int y) noexcept +template +void ImageBaseSlider::setStartPos(int x, int y) noexcept { setStartPos(Point(x, y)); } -void ImageSlider::setEndPos(const Point& endPos) noexcept +template +void ImageBaseSlider::setEndPos(const Point& endPos) noexcept { - fEndPos = endPos; - _recheckArea(); + pData->endPos = endPos; + pData->recheckArea(); } -void ImageSlider::setEndPos(int x, int y) noexcept +template +void ImageBaseSlider::setEndPos(int x, int y) noexcept { setEndPos(Point(x, y)); } -void ImageSlider::setInverted(bool inverted) noexcept +template +void ImageBaseSlider::setInverted(bool inverted) noexcept { - if (fInverted == inverted) + if (pData->inverted == inverted) return; - fInverted = inverted; + pData->inverted = inverted; repaint(); } -void ImageSlider::setDefault(float value) noexcept +template +void ImageBaseSlider::setDefault(float value) noexcept { - fValueDef = value; - fUsingDefault = true; + pData->valueDef = value; + pData->usingDefault = true; } -void ImageSlider::setRange(float min, float max) noexcept +template +void ImageBaseSlider::setRange(float min, float max) noexcept { - fMinimum = min; - fMaximum = max; + pData->minimum = min; + pData->maximum = max; - if (fValue < min) + if (pData->value < min) { - fValue = min; + pData->value = min; repaint(); - if (fCallback != nullptr && fValueIsSet) + if (pData->callback != nullptr && pData->valueIsSet) { try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange < min"); + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min"); } } - else if (fValue > max) + else if (pData->value > max) { - fValue = max; + pData->value = max; repaint(); - if (fCallback != nullptr && fValueIsSet) + if (pData->callback != nullptr && pData->valueIsSet) { try { - fCallback->imageSliderValueChanged(this, fValue); - } DISTRHO_SAFE_EXCEPTION("ImageSlider::setRange > max"); + pData->callback->imageSliderValueChanged(this, pData->value); + } DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max"); } } } -void ImageSlider::setStep(float step) noexcept +template +void ImageBaseSlider::setStep(float step) noexcept { - fStep = step; + pData->step = step; } -void ImageSlider::setCallback(Callback* callback) noexcept +template +void ImageBaseSlider::setCallback(Callback* callback) noexcept { - fCallback = callback; + pData->callback = callback; } -void ImageSlider::onDisplay() +template +void ImageBaseSlider::onDisplay() { -#if 0 // DEBUG, paints slider area - glColor3f(0.4f, 0.5f, 0.1f); - glRecti(fSliderArea.getX(), fSliderArea.getY(), fSliderArea.getX()+fSliderArea.getWidth(), fSliderArea.getY()+fSliderArea.getHeight()); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + const GraphicsContext& context(getGraphicsContext()); + +#if 1 // DEBUG, paints slider area + Color(0.4f, 0.5f, 0.1f).setFor(context); + Rectangle(pData->sliderArea.getX(), + pData->sliderArea.getY(), + pData->sliderArea.getX()+pData->sliderArea.getWidth(), + pData->sliderArea.getY()+pData->sliderArea.getHeight()).draw(context); + Color(1.0f, 1.0f, 1.0f, 1.0f).setFor(context, true); #endif - const float normValue = (fValue - fMinimum) / (fMaximum - fMinimum); + const float normValue = (pData->value - pData->minimum) / (pData->maximum - pData->minimum); int x, y; - if (fStartPos.getY() == fEndPos.getY()) + if (pData->startPos.getY() == pData->endPos.getY()) { // horizontal - if (fInverted) - x = fEndPos.getX() - static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); + if (pData->inverted) + x = pData->endPos.getX() - static_cast(normValue*static_cast(pData->endPos.getX()-pData->startPos.getX())); else - x = fStartPos.getX() + static_cast(normValue*static_cast(fEndPos.getX()-fStartPos.getX())); + x = pData->startPos.getX() + static_cast(normValue*static_cast(pData->endPos.getX()-pData->startPos.getX())); - y = fStartPos.getY(); + y = pData->startPos.getY(); } else { // vertical - x = fStartPos.getX(); + x = pData->startPos.getX(); - if (fInverted) - y = fEndPos.getY() - static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); + if (pData->inverted) + y = pData->endPos.getY() - static_cast(normValue*static_cast(pData->endPos.getY()-pData->startPos.getY())); else - y = fStartPos.getY() + static_cast(normValue*static_cast(fEndPos.getY()-fStartPos.getY())); + y = pData->startPos.getY() + static_cast(normValue*static_cast(pData->endPos.getY()-pData->startPos.getY())); } - fImage.drawAt(x, y); + pData->image.drawAt(context, x, y); } -#endif -// -------------------------------------------------------------------------------------------------------------------- - -#if 0 -bool ImageSlider::onMouse(const MouseEvent& ev) +template +bool ImageBaseSlider::onMouse(const MouseEvent& ev) { if (ev.button != 1) return false; if (ev.press) { - if (! fSliderArea.contains(ev.pos)) + if (! pData->sliderArea.contains(ev.pos)) return false; - if ((ev.mod & kModifierShift) != 0 && fUsingDefault) + if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) { - setValue(fValueDef, true); - fValueTmp = fValue; + setValue(pData->valueDef, true); + pData->valueTmp = pData->value; return true; } @@ -798,166 +816,140 @@ bool ImageSlider::onMouse(const MouseEvent& ev) const int x = ev.pos.getX(); const int y = ev.pos.getY(); - if (fStartPos.getY() == fEndPos.getY()) + if (pData->startPos.getY() == pData->endPos.getY()) { // horizontal - vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); } else { // vertical - vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); } float value; - if (fInverted) - value = fMaximum - vper * (fMaximum - fMinimum); + if (pData->inverted) + value = pData->maximum - vper * (pData->maximum - pData->minimum); else - value = fMinimum + vper * (fMaximum - fMinimum); + value = pData->minimum + vper * (pData->maximum - pData->minimum); - if (value < fMinimum) + if (value < pData->minimum) { - fValueTmp = value = fMinimum; + pData->valueTmp = value = pData->minimum; } - else if (value > fMaximum) + else if (value > pData->maximum) { - fValueTmp = value = fMaximum; + pData->valueTmp = value = pData->maximum; } - else if (d_isNotZero(fStep)) + else if (d_isNotZero(pData->step)) { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); } - fDragging = true; - fStartedX = x; - fStartedY = y; + pData->dragging = true; + pData->startedX = x; + pData->startedY = y; - if (fCallback != nullptr) - fCallback->imageSliderDragStarted(this); + if (pData->callback != nullptr) + pData->callback->imageSliderDragStarted(this); setValue(value, true); return true; } - else if (fDragging) + else if (pData->dragging) { - if (fCallback != nullptr) - fCallback->imageSliderDragFinished(this); + if (pData->callback != nullptr) + pData->callback->imageSliderDragFinished(this); - fDragging = false; + pData->dragging = false; return true; } return false; } -#endif -// -------------------------------------------------------------------------------------------------------------------- - -#if 0 -bool ImageSlider::onMotion(const MotionEvent& ev) +template +bool ImageBaseSlider::onMotion(const MotionEvent& ev) { - if (! fDragging) + if (! pData->dragging) return false; - const bool horizontal = fStartPos.getY() == fEndPos.getY(); + const bool horizontal = pData->startPos.getY() == pData->endPos.getY(); const int x = ev.pos.getX(); const int y = ev.pos.getY(); - if ((horizontal && fSliderArea.containsX(x)) || (fSliderArea.containsY(y) && ! horizontal)) + if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal)) { float vper; if (horizontal) { // horizontal - vper = float(x - fSliderArea.getX()) / float(fSliderArea.getWidth()); + vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); } else { // vertical - vper = float(y - fSliderArea.getY()) / float(fSliderArea.getHeight()); + vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); } float value; - if (fInverted) - value = fMaximum - vper * (fMaximum - fMinimum); + if (pData->inverted) + value = pData->maximum - vper * (pData->maximum - pData->minimum); else - value = fMinimum + vper * (fMaximum - fMinimum); + value = pData->minimum + vper * (pData->maximum - pData->minimum); - if (value < fMinimum) + if (value < pData->minimum) { - fValueTmp = value = fMinimum; + pData->valueTmp = value = pData->minimum; } - else if (value > fMaximum) + else if (value > pData->maximum) { - fValueTmp = value = fMaximum; + pData->valueTmp = value = pData->maximum; } - else if (d_isNotZero(fStep)) + else if (d_isNotZero(pData->step)) { - fValueTmp = value; - const float rest = std::fmod(value, fStep); - value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); + pData->valueTmp = value; + const float rest = std::fmod(value, pData->step); + value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); } setValue(value, true); } else if (horizontal) { - if (x < fSliderArea.getX()) - setValue(fInverted ? fMaximum : fMinimum, true); + if (x < pData->sliderArea.getX()) + setValue(pData->inverted ? pData->maximum : pData->minimum, true); else - setValue(fInverted ? fMinimum : fMaximum, true); + setValue(pData->inverted ? pData->minimum : pData->maximum, true); } else { - if (y < fSliderArea.getY()) - setValue(fInverted ? fMaximum : fMinimum, true); + if (y < pData->sliderArea.getY()) + setValue(pData->inverted ? pData->maximum : pData->minimum, true); else - setValue(fInverted ? fMinimum : fMaximum, true); + setValue(pData->inverted ? pData->minimum : pData->maximum, true); } return true; } -void ImageSlider::_recheckArea() noexcept -{ - if (fStartPos.getY() == fEndPos.getY()) - { - // horizontal - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - fEndPos.getX() + static_cast(fImage.getWidth()) - fStartPos.getX(), - static_cast(fImage.getHeight())); - } - else - { - // vertical - fSliderArea = Rectangle(fStartPos.getX(), - fStartPos.getY(), - static_cast(fImage.getWidth()), - fEndPos.getY() + static_cast(fImage.getHeight()) - fStartPos.getY()); - } -} -#endif - // -------------------------------------------------------------------------------------------------------------------- template struct ImageBaseSwitch::PrivateData { - ImageBaseSwitch* const self; ImageType imageNormal; ImageType imageDown; bool isDown; Callback* callback; - PrivateData(ImageBaseSwitch* const s, const ImageType& normal, const ImageType& down) - : self(s), - imageNormal(normal), + PrivateData(const ImageType& normal, const ImageType& down) + : imageNormal(normal), imageDown(down), isDown(false), callback(nullptr) @@ -965,9 +957,8 @@ struct ImageBaseSwitch::PrivateData { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); } - PrivateData(ImageBaseSwitch* const s, PrivateData* const other) - : self(s), - imageNormal(other->imageNormal), + PrivateData(PrivateData* const other) + : imageNormal(other->imageNormal), imageDown(other->imageDown), isDown(other->isDown), callback(other->callback) @@ -992,7 +983,7 @@ struct ImageBaseSwitch::PrivateData { template ImageBaseSwitch::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept : SubWidget(parentWidget), - pData(new PrivateData(this, imageNormal, imageDown)) + pData(new PrivateData(imageNormal, imageDown)) { setSize(imageNormal.getSize()); } @@ -1000,7 +991,7 @@ ImageBaseSwitch::ImageBaseSwitch(Widget* const parentWidget, const Im template ImageBaseSwitch::ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept : SubWidget(imageSwitch.getParentWidget()), - pData(new PrivateData(this, imageSwitch.pData)) + pData(new PrivateData(imageSwitch.pData)) { setSize(pData->imageNormal.getSize()); } diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 0d498321..a2c8c487 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -18,6 +18,7 @@ #include "../Color.hpp" #include "../ImageWidgets.hpp" +#include "Common.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" @@ -435,17 +436,125 @@ void OpenGLImage::drawAt(const Point& pos) } // ----------------------------------------------------------------------- +// ImageBaseAboutWindow +#if 0 template <> void ImageBaseAboutWindow::onDisplay() { const GraphicsContext& context(getGraphicsContext()); img.draw(context); } +#endif template class ImageBaseAboutWindow; + +// ----------------------------------------------------------------------- +// ImageBaseButton + template class ImageBaseButton; +// ----------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + glGenTextures(1, &textureId); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + if (textureId != 0) + { + glDeleteTextures(1, &textureId); + textureId = 0; + } +} + +template <> +void ImageBaseKnob::onDisplay() +{ + const GraphicsContext& context(getGraphicsContext()); + const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) / (pData->maximum - pData->minimum); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, pData->textureId); + + if (! pData->isReady) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + uint imageDataOffset = 0; + + if (pData->rotationAngle == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); + DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); + + const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); + const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); + + const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || + pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); + /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + static_cast(getWidth()), static_cast(getHeight()), 0, + asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); + + pData->isReady = true; + } + + const int w = static_cast(getWidth()); + const int h = static_cast(getHeight()); + + if (pData->rotationAngle != 0) + { + glPushMatrix(); + + const int w2 = w/2; + const int h2 = h/2; + + glTranslatef(static_cast(w2), static_cast(h2), 0.0f); + glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); + + Rectangle(-w2, -h2, w, h).draw(context); + + glPopMatrix(); + } + else + { + Rectangle(0, 0, w, h).draw(context); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +template class ImageBaseKnob; + +// ----------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// ----------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + // ----------------------------------------------------------------------- void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) From a4b06f3b8834a13806dee0eefb8616d8ae29c318 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 01:10:09 +0100 Subject: [PATCH 126/159] Incorporate the rest of cairo changes, CairoImageKnob works now --- dgl/Cairo.hpp | 18 +- dgl/src/Cairo.cpp | 183 +- dgl/src/Common.hpp | 7 +- dgl/src/ImageBaseWidgets.cpp | 6 +- dgl/src/OpenGL.cpp | 19 +- examples/CairoUI/Artwork.cpp | 2600 ++++++++++++++++++++++ examples/CairoUI/Artwork.hpp | 19 + examples/CairoUI/CairoExamplePlugin.cpp | 1 + examples/CairoUI/CairoExampleUI.cpp | 27 +- examples/CairoUI/DemoWidgetBanner.cpp | 1 + examples/CairoUI/DemoWidgetBanner.hpp | 1 + examples/CairoUI/DemoWidgetClickable.cpp | 1 + examples/CairoUI/DemoWidgetClickable.hpp | 1 + examples/CairoUI/Makefile | 1 + examples/CairoUI/artwork/buttonOff.png | Bin 0 -> 970 bytes examples/CairoUI/artwork/buttonOn.png | Bin 0 -> 983 bytes examples/CairoUI/artwork/knob.png | Bin 0 -> 52344 bytes 17 files changed, 2862 insertions(+), 23 deletions(-) create mode 100644 examples/CairoUI/Artwork.cpp create mode 100644 examples/CairoUI/Artwork.hpp create mode 100644 examples/CairoUI/artwork/buttonOff.png create mode 100644 examples/CairoUI/artwork/buttonOn.png create mode 100644 examples/CairoUI/artwork/knob.png diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index e7dfaa93..8857e126 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -72,18 +72,34 @@ public: ~CairoImage() override; /** - Load image data from memory. + Load raw image data from memory. @note @a rawData must remain valid for the lifetime of this Image. */ void loadFromMemory(const char* rawData, const Size& size, ImageFormat format = kImageFormatBGRA) noexcept override; + /** + Load PNG image from memory. + Image size is read from PNG contents. + @note @a pngData must remain valid for the lifetime of this Image. + */ + void loadFromPNG(const char* pngData, uint dataSize) noexcept; + /** Draw this image at position @a pos using the graphics context @a context. */ void drawAt(const GraphicsContext& context, const Point& pos) override; + /** + Get the cairo surface currently associated with this image. + FIXME might be removed + */ + inline cairo_surface_t* getSurface() const noexcept + { + return surface; + } + /** TODO document this. */ diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index f7df8d8d..64021fb9 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 @@ -275,7 +276,7 @@ template class Rectangle; // ----------------------------------------------------------------------- // CairoImage -static cairo_format_t asCairoImageFormat(const ImageFormat format) +static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept { switch (format) { @@ -294,6 +295,31 @@ static cairo_format_t asCairoImageFormat(const ImageFormat format) return CAIRO_FORMAT_INVALID; } +/* +static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept +{ + switch (format) + { + case CAIRO_FORMAT_INVALID: + break; + case CAIRO_FORMAT_ARGB32: + break; + case CAIRO_FORMAT_RGB24: + break; + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_A1: + break; + case CAIRO_FORMAT_RGB16_565: + break; + case CAIRO_FORMAT_RGB30: + break; + } + + return kImageFormatNull; +} +*/ + CairoImage::CairoImage() : ImageBase(), surface(nullptr), @@ -403,6 +429,51 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co ImageBase::loadFromMemory(rdata, s, fmt); } +// const GraphicsContext& context +void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept +{ + struct PngReaderData + { + const char* dataPtr; + uint sizeLeft; + + static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept + { + PngReaderData& readerData = *reinterpret_cast(closure); + + if (readerData.sizeLeft < length) + return CAIRO_STATUS_READ_ERROR; + + std::memcpy(data, readerData.dataPtr, length); + readerData.dataPtr += length; + readerData.sizeLeft -= length; + return CAIRO_STATUS_SUCCESS; + } + }; + + PngReaderData readerData; + readerData.dataPtr = pngData; + readerData.sizeLeft = pngSize; + + cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData); + DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + + cairo_surface_destroy(surface); + + if (datarefcount != nullptr && --(*datarefcount) == 0) + std::free(surfacedata); + else + datarefcount = (int*)malloc(sizeof(*datarefcount)); + + surface = newsurface; + surfacedata = nullptr; // cairo_image_surface_get_data(newsurface); + *datarefcount = 1; + + rawData = nullptr; + format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface)); + size = Size(cairo_image_surface_get_width(newsurface), cairo_image_surface_get_height(newsurface)); +} + void CairoImage::drawAt(const GraphicsContext& context, const Point& pos) { if (surface == nullptr) @@ -418,14 +489,23 @@ CairoImage& CairoImage::operator=(const CairoImage& image) noexcept { cairo_surface_t* newsurface = cairo_surface_reference(image.surface); cairo_surface_destroy(surface); + + if (datarefcount != nullptr && --(*datarefcount) == 0) + { + std::free(surfacedata); + std::free(datarefcount); + } + surface = newsurface; rawData = image.rawData; size = image.size; format = image.format; surfacedata = image.surfacedata; datarefcount = image.datarefcount; + if (datarefcount != nullptr) ++(*datarefcount); + return *this; } @@ -484,19 +564,114 @@ template class ImageBaseButton; template <> void ImageBaseKnob::PrivateData::init() { - notImplemented("ImageBaseKnob::PrivateData::init"); + // new (&cairoDisplayImage) CairoImage(); + cairoSurface = nullptr; } template <> void ImageBaseKnob::PrivateData::cleanup() { - notImplemented("ImageBaseKnob::PrivateData::cleanup"); + // cairoDisplayImage.~CairoImage(); + cairo_surface_destroy((cairo_surface_t*)cairoSurface); + cairoSurface = nullptr; +} + +/** + Get the pixel size in bytes. + @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes. +*/ +static uint getBytesPerPixel(cairo_format_t format) noexcept +{ + switch (format) + { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: + return 4; + case CAIRO_FORMAT_RGB16_565: + return 2; + case CAIRO_FORMAT_A8: + return 1; + case CAIRO_FORMAT_A1: + return 0; + default: + DISTRHO_SAFE_ASSERT(false); + return 0; + } +} + +static cairo_surface_t* getRegion(cairo_surface_t* origsurface, uint x, uint y, uint width, uint height) noexcept +{ + const cairo_format_t format = cairo_image_surface_get_format(origsurface); + const uint bpp = getBytesPerPixel(format); + + if (bpp == 0) + return nullptr; + + const uint fullWidth = cairo_image_surface_get_width(origsurface); + const uint fullHeight = cairo_image_surface_get_height(origsurface); + const uint stride = cairo_image_surface_get_stride(origsurface); + uchar* const fullData = cairo_image_surface_get_data(origsurface); + + x = (x < fullWidth) ? x : fullWidth; + y = (y < fullHeight) ? y : fullHeight; + width = (x + width < fullWidth) ? width : (fullWidth - x); + height = (x + height < fullHeight) ? height : (fullHeight - x); + + uchar* const data = fullData + x * bpp + y * stride; + return cairo_image_surface_create_for_data(data, format, width, height, stride); } template <> void ImageBaseKnob::onDisplay() { - notImplemented("ImageBaseKnob::onDisplay"); + const GraphicsContext& context(getGraphicsContext()); + cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) + / (pData->maximum - pData->minimum); + + cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; + + if (! pData->isReady) + { + const uint layerW = pData->imgLayerWidth; + const uint layerH = pData->imgLayerHeight; + uint layerNum = 0; + + if (pData->rotationAngle == 0) + layerNum = uint(normValue * float(pData->imgLayerCount-1)); + + const uint layerX = pData->isImgVertical ? 0 : layerNum * layerW; + const uint layerY = !pData->isImgVertical ? 0 : layerNum * layerH; + + cairo_surface_t* const newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); + DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + + if (pData->rotationAngle != 0) + { + // TODO + /* + CairoImage rotated(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH), false); + cairo_t* cr = cairo_create(rotated.getSurface()); + cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); + cairo_rotate(cr, normValue * angle * (float)(M_PI / 180)); + cairo_set_source_surface(cr, displayImage.getSurface(), -0.5f * layerW, -0.5f * layerH); + cairo_paint(cr); + cairo_destroy(cr); + pData->cairoDisplayImage = rotated; + */ + } + + cairo_surface_destroy(surface); + pData->cairoSurface = surface = newsurface; + pData->isReady = true; + } + + if (surface != nullptr) + { + cairo_set_source_surface(handle, surface, 0, 0); + cairo_paint(handle); + } } template class ImageBaseKnob; diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index e34d22be..87934a21 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -145,7 +145,12 @@ struct ImageBaseKnob::PrivateData { uint imgLayerHeight; uint imgLayerCount; bool isReady; - /*GL*/uint textureId; + + union { + uint glTextureId; + // ImageType cairoDisplayImage; + void* cairoSurface; + }; explicit PrivateData(const ImageType& img, const Orientation o); explicit PrivateData(PrivateData* const other); diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 147b797b..78bbffc3 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -208,8 +208,7 @@ ImageBaseKnob::PrivateData::PrivateData(const ImageType& img, const O imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), imgLayerHeight(imgLayerWidth), imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), - isReady(false), - textureId(0) + isReady(false) { init(); } @@ -235,8 +234,7 @@ ImageBaseKnob::PrivateData::PrivateData(PrivateData* const other) imgLayerWidth(other->imgLayerWidth), imgLayerHeight(other->imgLayerHeight), imgLayerCount(other->imgLayerCount), - isReady(false), - textureId(0) + isReady(false) { init(); } diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index a2c8c487..925b5abc 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -460,27 +460,29 @@ template class ImageBaseButton; template <> void ImageBaseKnob::PrivateData::init() { - glGenTextures(1, &textureId); + glTextureId = 0; + glGenTextures(1, &glTextureId); } template <> void ImageBaseKnob::PrivateData::cleanup() { - if (textureId != 0) - { - glDeleteTextures(1, &textureId); - textureId = 0; - } + if (glTextureId == 0) + return; + + glDeleteTextures(1, &glTextureId); + glTextureId = 0; } template <> void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); - const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) / (pData->maximum - pData->minimum); + const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) + / (pData->maximum - pData->minimum); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, pData->textureId); + glBindTexture(GL_TEXTURE_2D, pData->glTextureId); if (! pData->isReady) { @@ -505,6 +507,7 @@ void ImageBaseKnob::onDisplay() const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); + // TODO kImageFormatGreyscale const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); diff --git a/examples/CairoUI/Artwork.cpp b/examples/CairoUI/Artwork.cpp new file mode 100644 index 00000000..bfce3185 --- /dev/null +++ b/examples/CairoUI/Artwork.cpp @@ -0,0 +1,2600 @@ +/* (Auto-generated binary data file). */ + +#include "Artwork.hpp" + +static const unsigned char temp_buttonOff_1[] = { + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 50, 0, + 0, 0, 30, 8, 3, 0, 0, 0, 89, 143, 189, 206, 0, 0, 1, 242, 80, 76, 84, 69, 0, + 0, 0, 40, 65, 89, 126, 151, 175, 163, 187, 211, 253, 253, 253, 40, 65, 89, 103, 128, 152, 184, + 208, 233, 253, 253, 253, 40, 65, 89, 146, 170, 194, 253, 253, 253, 2, 3, 3, 5, 6, 6, 8, + 9, 11, 9, 10, 11, 15, 17, 19, 16, 18, 21, 19, 21, 24, 21, 24, 28, 22, 25, 28, 25, + 28, 32, 27, 31, 35, 28, 32, 36, 36, 41, 46, 38, 43, 49, 38, 44, 50, 40, 65, 89, 42, + 48, 54, 42, 48, 55, 43, 49, 56, 44, 50, 57, 44, 51, 57, 46, 52, 59, 46, 53, 60, 50, + 57, 64, 50, 74, 99, 52, 60, 68, 53, 60, 68, 55, 63, 71, 57, 65, 74, 57, 66, 74, 60, + 69, 78, 61, 70, 79, 63, 72, 81, 64, 73, 83, 65, 74, 84, 65, 75, 85, 67, 77, 87, 71, + 81, 91, 72, 82, 93, 73, 83, 94, 75, 100, 124, 77, 88, 100, 78, 102, 126, 80, 91, 103, 81, + 92, 104, 81, 93, 105, 84, 96, 108, 84, 97, 109, 90, 103, 116, 92, 106, 119, 93, 17, 19, 94, + 107, 121, 96, 109, 124, 96, 110, 125, 98, 112, 126, 105, 120, 136, 106, 121, 137, 108, 124, 140, 108, + 133, 157, 111, 127, 143, 111, 135, 159, 112, 128, 144, 112, 136, 160, 113, 130, 147, 114, 91, 103, 114, + 131, 148, 114, 138, 163, 115, 132, 149, 116, 133, 150, 117, 134, 152, 118, 135, 153, 119, 137, 154, 120, + 145, 169, 121, 139, 157, 122, 140, 158, 123, 140, 159, 124, 142, 160, 125, 143, 161, 126, 144, 163, 129, + 147, 166, 135, 155, 175, 136, 155, 176, 137, 156, 177, 138, 158, 178, 139, 158, 179, 140, 160, 181, 141, + 149, 168, 142, 162, 183, 144, 137, 154, 144, 164, 186, 144, 165, 187, 146, 167, 188, 146, 167, 189, 148, + 169, 191, 148, 170, 192, 149, 137, 154, 150, 42, 45, 151, 91, 103, 151, 173, 195, 152, 173, 196, 153, + 175, 198, 154, 176, 199, 157, 182, 206, 159, 175, 198, 160, 179, 202, 160, 182, 206, 160, 183, 207, 161, + 184, 208, 162, 185, 209, 162, 185, 210, 163, 20, 20, 164, 175, 198, 164, 187, 211, 165, 17, 19, 165, + 188, 213, 166, 42, 45, 166, 190, 215, 168, 179, 202, 168, 192, 217, 171, 181, 204, 172, 149, 168, 173, + 189, 212, 175, 182, 205, 175, 186, 209, 176, 152, 172, 187, 183, 202, 191, 216, 240, 192, 166, 184, 192, + 171, 188, 195, 40, 43, 200, 221, 234, 203, 20, 20, 203, 31, 31, 203, 220, 233, 206, 219, 234, 208, + 221, 234, 209, 234, 254, 213, 127, 139, 213, 164, 176, 218, 31, 31, 218, 32, 32, 222, 59, 61, 223, + 32, 32, 224, 33, 33, 224, 48, 49, 228, 82, 86, 228, 98, 102, 230, 237, 244, 234, 74, 74, 235, + 58, 60, 237, 254, 254, 243, 129, 131, 248, 254, 254, 253, 253, 253, 111, 147, 172, 98, 0, 0, 0, + 12, 116, 82, 78, 83, 0, 5, 5, 5, 5, 86, 86, 86, 86, 147, 147, 147, 101, 64, 116, 119, + 0, 0, 1, 123, 73, 68, 65, 84, 56, 203, 99, 96, 96, 225, 88, 186, 116, 9, 145, 96, 209, + 20, 118, 102, 6, 6, 22, 238, 165, 243, 39, 79, 36, 18, 244, 117, 53, 113, 49, 49, 112, 44, + 157, 212, 68, 2, 40, 10, 97, 99, 88, 58, 191, 137, 36, 224, 97, 198, 176, 116, 50, 105, 90, + 252, 84, 24, 150, 76, 36, 77, 139, 151, 244, 80, 208, 50, 109, 222, 204, 185, 83, 17, 114, 149, + 65, 170, 2, 124, 98, 166, 137, 64, 102, 190, 185, 128, 81, 60, 16, 23, 162, 106, 233, 92, 48, + 11, 4, 102, 119, 64, 117, 148, 219, 136, 234, 135, 199, 184, 200, 74, 6, 55, 53, 5, 42, 219, + 151, 5, 0, 113, 3, 138, 150, 214, 57, 179, 32, 96, 122, 35, 68, 139, 183, 132, 85, 5, 144, + 138, 83, 211, 74, 109, 242, 212, 244, 6, 99, 84, 135, 45, 158, 5, 3, 181, 96, 126, 141, 133, + 98, 52, 152, 225, 36, 227, 233, 44, 194, 195, 43, 44, 196, 195, 107, 92, 128, 172, 165, 125, 22, + 2, 84, 131, 4, 178, 244, 116, 50, 192, 90, 2, 228, 29, 154, 220, 53, 188, 193, 24, 197, 150, + 30, 36, 45, 217, 32, 129, 52, 109, 195, 66, 176, 146, 8, 37, 27, 236, 90, 186, 145, 180, 164, + 128, 4, 242, 12, 180, 211, 193, 74, 252, 21, 28, 177, 107, 105, 67, 210, 82, 12, 18, 168, 179, + 86, 140, 130, 248, 69, 193, 23, 187, 150, 166, 133, 112, 29, 118, 144, 16, 11, 148, 178, 44, 5, + 82, 9, 234, 186, 153, 56, 180, 52, 207, 128, 234, 152, 80, 2, 141, 73, 91, 81, 157, 176, 88, + 87, 5, 185, 200, 38, 28, 90, 154, 90, 250, 193, 58, 170, 146, 96, 177, 95, 19, 170, 46, 200, + 47, 110, 158, 220, 132, 83, 75, 83, 83, 110, 125, 111, 142, 207, 48, 73, 252, 68, 107, 89, 212, + 71, 154, 22, 55, 105, 134, 41, 93, 164, 105, 49, 145, 102, 96, 111, 42, 34, 205, 93, 172, 12, + 204, 92, 33, 30, 126, 94, 68, 2, 55, 19, 105, 78, 70, 6, 6, 38, 54, 51, 21, 105, 162, + 1, 43, 35, 3, 0, 57, 197, 173, 141, 128, 129, 207, 13, 0, 0, 0, 0, 73, 69, 78, 68, + 174, 66, 96, 130,}; +const char* Artwork::buttonOffData = (const char*)temp_buttonOff_1; + +static const unsigned char temp_buttonOn_2[] = { + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 50, 0, + 0, 0, 30, 8, 3, 0, 0, 0, 89, 143, 189, 206, 0, 0, 1, 254, 80, 76, 84, 69, 0, + 0, 0, 40, 65, 89, 126, 151, 175, 163, 187, 211, 253, 253, 253, 40, 65, 89, 103, 128, 152, 184, + 208, 233, 253, 253, 253, 40, 65, 89, 146, 170, 194, 253, 253, 253, 8, 9, 11, 9, 10, 11, 11, + 12, 14, 15, 17, 19, 15, 94, 19, 15, 166, 19, 16, 18, 21, 17, 19, 22, 20, 163, 20, 20, + 203, 20, 25, 28, 32, 27, 31, 35, 28, 32, 36, 30, 35, 39, 30, 203, 31, 30, 218, 31, 31, + 219, 32, 32, 223, 32, 33, 224, 33, 36, 41, 46, 38, 43, 49, 38, 44, 50, 38, 153, 45, 38, + 169, 45, 38, 197, 43, 40, 65, 89, 42, 48, 54, 42, 48, 55, 43, 49, 56, 44, 50, 57, 44, + 51, 57, 46, 52, 59, 46, 53, 60, 48, 225, 49, 49, 56, 63, 50, 57, 65, 50, 74, 99, 52, + 60, 68, 53, 60, 68, 55, 63, 71, 56, 64, 72, 56, 64, 73, 56, 236, 60, 57, 65, 74, 57, + 224, 61, 58, 67, 75, 60, 69, 78, 61, 70, 79, 63, 72, 81, 64, 73, 83, 65, 74, 84, 65, + 75, 85, 67, 77, 87, 69, 79, 90, 71, 81, 91, 73, 84, 95, 74, 234, 74, 75, 100, 124, 77, + 88, 100, 78, 102, 126, 79, 231, 86, 80, 91, 103, 80, 126, 103, 80, 162, 103, 81, 92, 104, 81, + 93, 105, 83, 95, 108, 84, 96, 108, 84, 97, 109, 86, 98, 111, 87, 100, 113, 90, 103, 116, 92, + 106, 119, 95, 231, 102, 96, 109, 124, 96, 110, 125, 98, 112, 126, 106, 121, 137, 108, 124, 140, 108, + 133, 157, 111, 135, 159, 112, 128, 144, 112, 136, 160, 113, 130, 147, 114, 131, 148, 114, 138, 163, 115, + 132, 149, 115, 224, 139, 116, 133, 150, 117, 134, 152, 118, 135, 153, 119, 137, 154, 119, 161, 154, 119, + 166, 154, 120, 145, 169, 121, 139, 157, 122, 140, 158, 123, 140, 159, 123, 141, 159, 124, 142, 160, 125, + 143, 161, 126, 144, 163, 127, 145, 164, 127, 245, 131, 129, 147, 166, 129, 148, 167, 130, 160, 168, 130, + 191, 168, 134, 195, 172, 135, 155, 175, 136, 155, 176, 137, 156, 177, 137, 157, 177, 139, 158, 179, 139, + 159, 180, 140, 160, 181, 140, 161, 182, 142, 162, 183, 144, 164, 186, 144, 165, 187, 146, 167, 188, 146, + 167, 189, 148, 169, 191, 148, 170, 192, 149, 210, 184, 151, 173, 195, 153, 175, 198, 153, 181, 198, 153, + 186, 198, 153, 210, 188, 153, 224, 176, 154, 176, 199, 156, 182, 202, 156, 190, 202, 157, 182, 206, 159, + 194, 204, 160, 182, 206, 160, 197, 205, 161, 184, 208, 162, 185, 209, 162, 185, 210, 164, 187, 211, 164, + 206, 202, 165, 188, 213, 165, 197, 209, 167, 191, 216, 167, 196, 212, 168, 192, 217, 191, 216, 240, 200, + 221, 234, 203, 220, 233, 206, 219, 234, 208, 221, 234, 209, 234, 254, 230, 237, 244, 237, 254, 254, 248, + 254, 254, 253, 253, 253, 121, 47, 149, 249, 0, 0, 0, 12, 116, 82, 78, 83, 0, 5, 5, 5, + 5, 86, 86, 86, 86, 147, 147, 147, 101, 64, 116, 119, 0, 0, 1, 124, 73, 68, 65, 84, 56, + 203, 99, 96, 96, 225, 88, 185, 114, 5, 145, 96, 249, 82, 118, 102, 6, 6, 22, 238, 149, 203, + 150, 44, 34, 18, 44, 92, 48, 159, 139, 137, 129, 99, 229, 226, 249, 36, 128, 73, 89, 108, 12, + 43, 151, 205, 39, 9, 196, 184, 51, 172, 92, 66, 154, 150, 68, 3, 134, 21, 139, 72, 211, 18, + 167, 58, 20, 180, 244, 133, 90, 120, 36, 35, 228, 102, 164, 25, 242, 243, 136, 187, 213, 227, 214, + 50, 203, 69, 22, 4, 228, 42, 160, 82, 211, 130, 197, 236, 115, 43, 35, 213, 21, 210, 113, 105, + 153, 167, 43, 11, 1, 210, 19, 33, 82, 241, 242, 254, 211, 129, 84, 181, 145, 85, 19, 14, 45, + 197, 178, 48, 32, 8, 230, 207, 244, 209, 46, 1, 51, 34, 212, 98, 231, 7, 90, 250, 73, 242, + 233, 23, 161, 106, 153, 35, 139, 0, 61, 32, 129, 118, 59, 155, 86, 176, 146, 20, 205, 176, 249, + 65, 194, 206, 13, 165, 122, 142, 93, 40, 90, 122, 145, 180, 100, 130, 4, 154, 173, 157, 250, 193, + 90, 242, 117, 130, 231, 7, 105, 228, 205, 159, 27, 96, 94, 135, 162, 165, 3, 73, 75, 6, 72, + 160, 211, 193, 186, 5, 172, 37, 73, 43, 124, 126, 144, 105, 245, 252, 249, 65, 38, 85, 40, 90, + 166, 34, 105, 233, 6, 9, 204, 14, 208, 46, 132, 248, 69, 43, 97, 126, 144, 121, 13, 166, 150, + 249, 102, 112, 29, 2, 144, 144, 73, 85, 244, 157, 2, 164, 106, 141, 109, 219, 112, 104, 153, 44, + 3, 213, 33, 53, 1, 26, 147, 33, 98, 54, 57, 85, 81, 90, 26, 5, 243, 113, 104, 153, 95, + 46, 10, 214, 33, 82, 6, 139, 128, 153, 217, 198, 66, 188, 18, 158, 141, 243, 113, 106, 153, 63, + 223, 91, 89, 69, 201, 107, 152, 36, 126, 162, 181, 44, 95, 72, 154, 150, 104, 85, 134, 165, 11, + 72, 211, 226, 170, 202, 192, 62, 127, 18, 105, 238, 98, 101, 96, 230, 202, 138, 73, 140, 35, 18, + 68, 187, 170, 114, 50, 50, 48, 48, 177, 185, 27, 168, 18, 13, 88, 25, 25, 0, 137, 242, 6, + 61, 121, 125, 59, 85, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,}; +const char* Artwork::buttonOnData = (const char*)temp_buttonOn_2; + +static const unsigned char temp_knob_3[] = { + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 64, 0, + 0, 16, 0, 8, 6, 0, 0, 0, 194, 239, 231, 124, 0, 0, 204, 63, 73, 68, 65, 84, 120, + 218, 236, 157, 7, 124, 27, 85, 242, 199, 77, 232, 28, 252, 129, 227, 40, 71, 63, 32, 192, 209, + 107, 122, 1, 210, 123, 181, 12, 7, 7, 119, 128, 34, 41, 157, 36, 132, 244, 80, 66, 56, 90, + 66, 13, 164, 82, 82, 44, 59, 78, 33, 182, 83, 156, 222, 27, 233, 133, 20, 72, 111, 54, 9, + 9, 41, 164, 162, 247, 159, 89, 207, 202, 79, 235, 93, 73, 150, 118, 181, 107, 123, 222, 231, 51, + 89, 91, 86, 164, 55, 179, 175, 237, 238, 251, 125, 39, 41, 41, 65, 229, 201, 209, 203, 46, 4, + 123, 0, 172, 1, 216, 43, 96, 157, 193, 122, 146, 117, 166, 215, 26, 208, 123, 46, 76, 42, 238, + 5, 156, 184, 2, 172, 5, 216, 231, 96, 43, 192, 68, 245, 180, 31, 69, 253, 241, 171, 68, 243, + 31, 214, 136, 148, 172, 181, 226, 95, 217, 235, 20, 195, 159, 241, 53, 252, 27, 190, 7, 223, 75, + 255, 231, 115, 250, 140, 43, 138, 147, 227, 88, 225, 140, 114, 224, 68, 157, 140, 149, 226, 165, 41, + 235, 197, 235, 115, 183, 136, 15, 150, 109, 23, 195, 215, 238, 17, 25, 155, 15, 136, 41, 219, 126, + 21, 179, 119, 30, 18, 243, 118, 255, 166, 24, 254, 140, 175, 225, 223, 240, 61, 248, 94, 252, 63, + 248, 127, 241, 51, 202, 229, 7, 36, 3, 63, 219, 169, 78, 95, 72, 205, 120, 219, 211, 233, 43, + 196, 171, 57, 27, 197, 135, 203, 118, 136, 113, 224, 208, 146, 189, 135, 197, 206, 35, 127, 136, 63, + 206, 156, 21, 127, 254, 249, 167, 174, 157, 59, 119, 46, 228, 103, 124, 47, 254, 31, 252, 191, 248, + 25, 248, 89, 248, 153, 248, 217, 248, 29, 244, 93, 23, 58, 197, 249, 54, 96, 251, 107, 142, 93, + 41, 186, 194, 89, 27, 189, 113, 159, 88, 113, 224, 119, 241, 219, 31, 167, 163, 114, 56, 26, 195, + 207, 194, 207, 196, 207, 198, 239, 192, 239, 194, 239, 196, 239, 182, 211, 241, 202, 96, 11, 171, 250, + 127, 84, 42, 53, 113, 107, 174, 248, 249, 183, 227, 97, 207, 116, 36, 139, 20, 24, 252, 108, 252, + 14, 252, 46, 252, 78, 252, 110, 172, 3, 214, 37, 209, 206, 247, 44, 63, 102, 153, 120, 97, 242, + 122, 229, 172, 96, 165, 78, 21, 209, 241, 19, 167, 207, 138, 35, 39, 79, 43, 134, 63, 23, 229, + 255, 158, 162, 64, 224, 119, 99, 29, 176, 46, 88, 167, 68, 141, 236, 202, 0, 247, 230, 162, 95, + 196, 250, 95, 143, 138, 163, 167, 206, 132, 173, 236, 153, 179, 231, 196, 174, 223, 255, 16, 179, 96, + 160, 251, 118, 253, 94, 241, 222, 210, 237, 162, 243, 156, 45, 162, 205, 204, 77, 194, 59, 253, 39, + 197, 240, 103, 124, 13, 255, 134, 239, 193, 247, 226, 255, 193, 255, 27, 238, 179, 241, 187, 177, 14, + 88, 23, 105, 160, 188, 194, 42, 231, 239, 194, 169, 169, 138, 127, 185, 50, 82, 239, 59, 122, 82, + 105, 178, 70, 205, 118, 207, 209, 63, 148, 166, 218, 99, 254, 86, 229, 44, 53, 153, 184, 90, 212, + 130, 17, 189, 10, 52, 91, 170, 108, 136, 225, 107, 248, 55, 124, 15, 190, 23, 255, 15, 254, 95, + 252, 12, 252, 172, 112, 221, 5, 235, 130, 117, 194, 186, 209, 244, 121, 151, 217, 206, 227, 226, 100, + 27, 206, 211, 147, 127, 249, 85, 252, 126, 242, 140, 97, 191, 197, 209, 251, 171, 213, 187, 131, 83, + 88, 165, 212, 229, 170, 147, 216, 87, 7, 130, 189, 12, 86, 29, 236, 94, 176, 155, 201, 238, 165, + 215, 94, 166, 247, 224, 123, 149, 255, 171, 78, 165, 248, 153, 248, 217, 70, 99, 7, 214, 9, 235, + 134, 117, 164, 153, 226, 1, 51, 207, 252, 182, 198, 112, 86, 22, 238, 249, 205, 112, 144, 203, 59, + 113, 74, 124, 15, 205, 247, 57, 88, 216, 84, 75, 11, 158, 229, 28, 114, 234, 170, 24, 190, 247, + 42, 250, 191, 57, 248, 89, 248, 153, 248, 217, 248, 29, 248, 93, 70, 131, 36, 214, 17, 235, 74, + 65, 184, 203, 140, 62, 191, 2, 163, 138, 31, 124, 18, 190, 64, 123, 198, 79, 67, 63, 93, 155, + 119, 84, 116, 152, 181, 89, 109, 130, 104, 163, 193, 158, 48, 177, 5, 62, 65, 159, 169, 124, 7, + 126, 23, 126, 231, 105, 205, 24, 129, 117, 59, 73, 65, 160, 150, 176, 34, 174, 49, 1, 7, 21, + 252, 66, 108, 90, 127, 232, 56, 143, 163, 55, 246, 209, 58, 227, 86, 202, 205, 188, 186, 133, 131, + 112, 117, 181, 123, 224, 119, 226, 119, 107, 103, 16, 117, 33, 133, 117, 166, 19, 146, 17, 243, 84, + 135, 77, 15, 7, 23, 236, 95, 218, 213, 218, 241, 211, 103, 196, 80, 248, 155, 52, 144, 245, 75, + 224, 52, 220, 79, 253, 94, 172, 3, 214, 69, 173, 159, 122, 196, 58, 99, 221, 169, 43, 246, 44, + 242, 34, 7, 231, 86, 156, 94, 112, 132, 85, 63, 88, 253, 112, 252, 194, 65, 171, 118, 169, 142, + 159, 6, 75, 177, 97, 33, 150, 66, 223, 173, 212, 5, 235, 164, 29, 19, 176, 238, 232, 3, 173, + 19, 42, 23, 229, 195, 23, 226, 52, 132, 115, 172, 182, 121, 97, 147, 147, 206, 252, 97, 43, 155, + 124, 148, 93, 226, 176, 218, 18, 244, 22, 84, 232, 3, 250, 130, 62, 69, 189, 182, 199, 37, 38, + 174, 178, 112, 161, 33, 55, 125, 28, 116, 176, 223, 73, 103, 222, 54, 231, 53, 65, 80, 90, 2, + 214, 77, 30, 24, 177, 238, 232, 3, 250, 66, 203, 230, 54, 209, 92, 213, 237, 199, 117, 54, 46, + 53, 181, 103, 31, 71, 94, 105, 192, 75, 73, 114, 72, 161, 238, 160, 212, 13, 235, 168, 109, 5, + 232, 11, 250, 68, 23, 80, 23, 134, 251, 160, 206, 120, 165, 133, 145, 84, 215, 246, 106, 223, 199, + 185, 23, 167, 159, 68, 15, 120, 69, 29, 24, 177, 142, 88, 87, 185, 229, 162, 47, 232, 19, 93, + 69, 118, 14, 247, 33, 219, 244, 206, 62, 26, 46, 64, 104, 90, 89, 152, 228, 208, 130, 117, 195, + 58, 98, 93, 181, 83, 182, 212, 10, 182, 25, 222, 201, 193, 27, 14, 216, 95, 180, 171, 61, 92, + 130, 226, 42, 140, 206, 126, 117, 7, 7, 0, 199, 3, 165, 174, 218, 101, 51, 250, 132, 190, 209, + 77, 149, 22, 186, 139, 30, 188, 235, 130, 55, 30, 180, 103, 31, 215, 225, 213, 242, 239, 213, 141, + 46, 6, 183, 228, 70, 99, 93, 177, 206, 90, 63, 208, 55, 244, 177, 208, 226, 8, 151, 139, 184, + 96, 192, 91, 79, 218, 59, 57, 120, 37, 134, 23, 35, 180, 160, 120, 162, 24, 4, 224, 9, 172, + 43, 214, 89, 123, 21, 137, 190, 161, 143, 228, 203, 21, 33, 205, 31, 175, 186, 240, 254, 155, 54, + 106, 202, 82, 55, 67, 25, 60, 114, 138, 209, 141, 217, 28, 172, 51, 214, 93, 123, 197, 138, 62, + 146, 63, 45, 228, 255, 240, 57, 70, 12, 111, 66, 106, 111, 102, 224, 53, 57, 93, 210, 190, 92, + 140, 2, 240, 50, 214, 25, 235, 174, 189, 169, 130, 62, 162, 175, 232, 179, 252, 31, 86, 224, 109, + 104, 237, 192, 129, 119, 101, 104, 21, 37, 98, 185, 164, 149, 75, 138, 199, 159, 200, 0, 224, 165, + 180, 82, 119, 244, 65, 59, 160, 191, 158, 63, 27, 172, 144, 23, 63, 202, 189, 120, 237, 232, 143, + 183, 166, 154, 228, 95, 91, 199, 61, 245, 65, 0, 90, 130, 221, 153, 200, 41, 17, 235, 142, 62, + 104, 103, 3, 244, 149, 78, 234, 133, 202, 221, 30, 124, 26, 131, 87, 78, 218, 254, 143, 247, 231, + 106, 229, 247, 151, 129, 113, 58, 127, 31, 216, 72, 176, 223, 93, 173, 82, 27, 37, 40, 0, 3, + 177, 238, 232, 131, 214, 47, 244, 149, 158, 64, 61, 128, 111, 108, 128, 55, 15, 50, 116, 6, 64, + 188, 73, 89, 37, 127, 13, 253, 114, 28, 206, 95, 224, 242, 164, 165, 192, 81, 128, 5, 232, 216, + 54, 17, 227, 0, 214, 29, 125, 208, 250, 133, 190, 210, 13, 147, 6, 248, 198, 87, 240, 185, 28, + 62, 154, 210, 190, 17, 239, 212, 150, 139, 115, 241, 3, 206, 222, 2, 182, 14, 236, 79, 114, 94, + 181, 143, 172, 94, 20, 97, 221, 209, 7, 173, 95, 232, 43, 250, 140, 190, 43, 235, 127, 124, 56, + 57, 91, 211, 87, 240, 210, 18, 111, 87, 83, 95, 185, 55, 70, 231, 203, 128, 245, 6, 219, 163, + 113, 94, 109, 9, 99, 193, 46, 182, 40, 0, 120, 163, 85, 241, 65, 123, 153, 140, 190, 162, 207, + 202, 117, 1, 222, 45, 193, 39, 180, 248, 144, 82, 126, 19, 62, 176, 192, 123, 246, 20, 128, 155, + 99, 12, 192, 147, 208, 252, 39, 160, 179, 112, 20, 154, 32, 168, 182, 12, 236, 110, 11, 2, 128, + 119, 155, 21, 31, 208, 23, 217, 55, 244, 245, 95, 249, 75, 251, 158, 150, 5, 0, 167, 189, 20, + 183, 223, 27, 193, 121, 213, 242, 192, 106, 219, 21, 128, 96, 23, 144, 239, 171, 197, 219, 5, 192, + 161, 123, 192, 14, 134, 113, 58, 160, 243, 154, 219, 142, 46, 16, 50, 8, 202, 203, 198, 88, 7, + 65, 112, 228, 18, 28, 228, 224, 204, 231, 169, 206, 193, 244, 23, 234, 188, 91, 227, 124, 193, 239, + 239, 90, 53, 8, 170, 247, 54, 180, 131, 160, 233, 211, 32, 56, 81, 29, 44, 39, 194, 217, 54, + 108, 13, 16, 172, 81, 241, 174, 28, 139, 50, 13, 154, 186, 16, 130, 138, 255, 5, 236, 13, 157, + 51, 171, 107, 6, 227, 67, 26, 216, 51, 86, 44, 132, 176, 5, 104, 23, 66, 166, 46, 133, 161, + 226, 143, 225, 138, 207, 232, 204, 195, 217, 13, 215, 26, 142, 131, 125, 1, 65, 123, 50, 97, 75, + 97, 189, 139, 33, 117, 28, 40, 234, 197, 16, 84, 254, 255, 192, 134, 129, 3, 71, 12, 250, 190, + 177, 185, 253, 171, 192, 222, 131, 159, 47, 5, 59, 63, 97, 23, 67, 102, 94, 14, 67, 197, 27, + 128, 45, 140, 212, 236, 165, 191, 171, 131, 97, 150, 201, 51, 64, 145, 47, 135, 227, 190, 33, 2, + 14, 92, 1, 253, 185, 127, 17, 6, 59, 252, 249, 24, 88, 6, 216, 195, 96, 151, 89, 117, 67, + 68, 54, 163, 27, 34, 113, 223, 18, 163, 145, 95, 68, 28, 252, 10, 94, 95, 3, 246, 37, 45, + 151, 203, 216, 122, 75, 44, 222, 155, 162, 112, 230, 175, 5, 39, 198, 129, 157, 8, 183, 234, 147, + 198, 131, 249, 96, 173, 192, 254, 150, 136, 155, 162, 234, 152, 102, 120, 83, 52, 158, 219, 226, 224, + 196, 121, 96, 41, 112, 102, 87, 22, 26, 244, 220, 249, 211, 156, 244, 218, 111, 96, 115, 224, 245, + 123, 192, 254, 234, 168, 219, 226, 177, 62, 24, 1, 135, 174, 3, 39, 191, 48, 152, 211, 229, 62, + 191, 25, 236, 27, 176, 91, 227, 29, 229, 45, 121, 48, 98, 244, 104, 76, 222, 2, 163, 247, 104, + 12, 156, 105, 4, 118, 182, 144, 195, 238, 144, 197, 206, 38, 28, 229, 173, 188, 37, 166, 125, 52, + 166, 221, 86, 23, 237, 163, 177, 66, 15, 71, 229, 72, 106, 31, 142, 210, 217, 156, 13, 118, 90, + 109, 1, 154, 121, 255, 16, 216, 79, 16, 140, 114, 86, 244, 119, 211, 31, 142, 210, 135, 133, 60, + 30, 215, 238, 7, 10, 62, 30, 31, 185, 244, 76, 163, 55, 126, 248, 72, 113, 80, 127, 186, 219, + 11, 246, 45, 24, 58, 127, 158, 197, 143, 195, 116, 31, 143, 171, 123, 9, 163, 126, 60, 46, 247, + 165, 72, 27, 36, 42, 14, 95, 36, 234, 247, 202, 58, 163, 211, 247, 207, 210, 82, 184, 13, 216, + 131, 9, 120, 22, 168, 187, 65, 66, 189, 242, 43, 242, 6, 9, 250, 224, 66, 91, 100, 100, 59, + 118, 234, 180, 232, 11, 163, 169, 206, 153, 63, 10, 182, 21, 172, 9, 216, 95, 45, 118, 190, 190, + 101, 91, 100, 232, 11, 66, 54, 73, 201, 145, 221, 13, 203, 201, 214, 189, 179, 160, 191, 251, 69, + 50, 158, 253, 252, 1, 239, 36, 180, 4, 28, 229, 235, 38, 232, 182, 119, 121, 176, 51, 88, 199, + 185, 187, 14, 153, 187, 73, 74, 94, 28, 105, 183, 201, 157, 132, 15, 254, 106, 220, 42, 209, 162, + 93, 6, 141, 240, 193, 245, 252, 27, 48, 0, 62, 158, 224, 71, 96, 174, 39, 105, 139, 237, 138, + 253, 71, 204, 221, 38, 167, 46, 145, 181, 27, 37, 103, 194, 28, 219, 180, 87, 166, 234, 60, 222, + 234, 94, 75, 171, 186, 191, 216, 244, 28, 240, 117, 12, 194, 51, 208, 37, 215, 228, 254, 110, 238, + 70, 73, 250, 130, 224, 86, 217, 180, 77, 251, 69, 163, 1, 179, 84, 231, 113, 202, 195, 167, 61, + 174, 68, 63, 251, 211, 169, 227, 151, 24, 132, 218, 112, 145, 131, 117, 52, 109, 171, 172, 244, 5, + 202, 102, 233, 170, 67, 230, 139, 230, 237, 51, 68, 75, 143, 255, 28, 244, 119, 188, 118, 119, 204, + 126, 1, 168, 95, 22, 6, 129, 6, 60, 243, 54, 75, 171, 165, 234, 151, 115, 31, 170, 251, 246, + 228, 253, 141, 187, 78, 60, 214, 178, 117, 250, 59, 248, 64, 195, 202, 249, 61, 198, 155, 32, 235, + 45, 217, 46, 175, 150, 22, 109, 199, 54, 105, 246, 218, 184, 46, 73, 14, 45, 224, 248, 237, 96, + 151, 91, 250, 37, 102, 94, 187, 115, 225, 194, 133, 11, 23, 46, 165, 164, 48, 63, 128, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 69, 186, + 197, 197, 252, 0, 170, 7, 243, 3, 168, 62, 204, 15, 160, 186, 49, 63, 128, 249, 1, 73, 204, + 15, 144, 131, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 32, 63, 152, 31, 192, 252, + 0, 230, 7, 56, 147, 31, 80, 38, 81, 59, 74, 28, 199, 15, 80, 84, 164, 30, 127, 51, 176, + 255, 185, 60, 105, 23, 37, 106, 74, 116, 18, 63, 224, 6, 176, 65, 180, 155, 116, 6, 10, 163, + 18, 16, 0, 103, 240, 3, 40, 0, 30, 141, 100, 14, 119, 152, 95, 97, 245, 56, 224, 20, 126, + 64, 13, 176, 141, 58, 154, 2, 148, 205, 92, 101, 97, 0, 28, 193, 15, 184, 3, 236, 99, 146, + 202, 232, 233, 8, 23, 89, 165, 35, 176, 157, 31, 128, 232, 140, 20, 183, 114, 246, 127, 145, 28, + 15, 104, 132, 211, 1, 226, 7, 92, 111, 65, 0, 108, 231, 7, 92, 9, 182, 212, 80, 61, 30, + 122, 92, 1, 118, 147, 93, 1, 48, 157, 31, 64, 187, 72, 7, 128, 29, 214, 19, 75, 203, 2, + 11, 233, 103, 212, 17, 222, 106, 71, 23, 48, 149, 31, 64, 11, 158, 166, 224, 216, 2, 93, 1, + 117, 120, 89, 237, 122, 28, 55, 172, 26, 4, 45, 231, 7, 208, 130, 231, 90, 112, 242, 107, 93, + 103, 163, 147, 210, 163, 194, 172, 108, 34, 167, 65, 179, 249, 1, 109, 8, 153, 19, 80, 133, 147, + 133, 156, 119, 23, 10, 70, 64, 51, 86, 160, 216, 234, 74, 43, 22, 66, 150, 242, 3, 160, 210, + 181, 148, 190, 236, 246, 159, 147, 157, 211, 136, 170, 2, 210, 25, 151, 7, 67, 245, 255, 124, 65, + 42, 244, 196, 44, 133, 245, 46, 134, 98, 225, 7, 208, 156, 255, 33, 216, 17, 3, 193, 116, 64, + 23, 165, 145, 255, 183, 99, 164, 60, 105, 7, 65, 249, 43, 188, 118, 94, 156, 206, 39, 150, 31, + 128, 87, 121, 208, 116, 107, 64, 197, 119, 26, 73, 103, 13, 68, 213, 248, 247, 131, 240, 127, 63, + 195, 179, 174, 142, 35, 118, 92, 14, 199, 117, 67, 4, 245, 195, 10, 1, 66, 223, 193, 128, 238, + 56, 128, 218, 66, 183, 127, 63, 81, 230, 238, 177, 242, 134, 136, 165, 252, 0, 168, 252, 229, 52, + 231, 31, 52, 24, 233, 245, 244, 196, 56, 221, 141, 130, 192, 60, 96, 246, 61, 130, 132, 242, 3, + 148, 229, 174, 199, 223, 24, 108, 65, 52, 211, 28, 13, 116, 136, 215, 249, 183, 153, 139, 158, 72, + 55, 69, 45, 225, 7, 208, 156, 127, 13, 216, 16, 3, 199, 181, 11, 160, 173, 202, 123, 97, 221, + 111, 213, 165, 112, 194, 249, 1, 208, 132, 59, 128, 51, 103, 34, 14, 120, 110, 255, 88, 120, 173, + 181, 89, 131, 156, 37, 15, 70, 140, 30, 141, 25, 241, 3, 192, 145, 58, 116, 17, 115, 78, 239, + 204, 195, 200, 126, 146, 130, 131, 56, 189, 39, 147, 44, 46, 9, 229, 7, 84, 28, 182, 80, 52, + 233, 50, 97, 18, 138, 165, 181, 156, 32, 58, 251, 7, 225, 56, 13, 206, 252, 115, 86, 209, 34, + 44, 121, 56, 170, 247, 120, 92, 187, 31, 104, 60, 76, 35, 213, 190, 152, 35, 90, 180, 27, 171, + 215, 223, 177, 53, 236, 2, 235, 10, 86, 209, 234, 38, 31, 233, 241, 184, 37, 252, 128, 111, 102, + 252, 42, 92, 29, 50, 67, 251, 122, 254, 32, 184, 151, 110, 110, 84, 194, 155, 160, 9, 124, 22, + 152, 56, 126, 192, 23, 83, 14, 139, 74, 222, 5, 162, 185, 103, 92, 240, 194, 5, 53, 196, 45, + 219, 164, 35, 69, 226, 61, 248, 253, 246, 4, 62, 252, 72, 44, 63, 160, 223, 248, 35, 226, 159, + 222, 13, 162, 174, 103, 114, 16, 143, 147, 236, 75, 19, 13, 122, 102, 6, 234, 247, 201, 106, 99, + 37, 35, 196, 160, 126, 137, 227, 7, 116, 155, 152, 43, 110, 111, 11, 139, 33, 239, 156, 96, 191, + 111, 218, 121, 162, 168, 253, 238, 84, 81, 254, 219, 197, 255, 130, 223, 47, 74, 178, 161, 36, 140, + 31, 80, 115, 204, 26, 209, 168, 199, 143, 34, 217, 147, 174, 52, 251, 134, 111, 77, 17, 79, 125, + 50, 11, 63, 252, 117, 59, 101, 243, 84, 71, 235, 249, 1, 213, 63, 157, 125, 160, 249, 27, 153, + 194, 213, 118, 130, 168, 247, 209, 108, 101, 26, 68, 205, 190, 131, 30, 134, 90, 203, 15, 104, 220, + 117, 98, 151, 122, 125, 178, 143, 86, 255, 124, 142, 186, 16, 202, 114, 224, 19, 97, 107, 249, 1, + 208, 223, 159, 166, 38, 181, 62, 222, 39, 197, 22, 62, 17, 182, 150, 31, 128, 218, 124, 212, 232, + 39, 57, 180, 36, 132, 31, 192, 133, 11, 23, 46, 92, 184, 112, 137, 101, 142, 102, 126, 0, 243, + 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 162, + 191, 197, 197, 252, 0, 170, 7, 243, 3, 168, 62, 204, 15, 160, 186, 49, 63, 128, 249, 1, 73, + 204, 15, 144, 131, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 32, 63, 152, 31, 192, + 252, 0, 230, 7, 56, 144, 31, 128, 5, 179, 78, 37, 40, 0, 142, 227, 7, 156, 79, 59, 72, + 199, 131, 245, 75, 80, 16, 28, 197, 15, 192, 84, 188, 159, 75, 123, 139, 219, 38, 32, 0, 206, + 225, 7, 184, 60, 105, 111, 130, 211, 127, 104, 36, 116, 45, 172, 30, 7, 108, 231, 7, 144, 140, + 246, 37, 176, 149, 90, 33, 21, 241, 4, 42, 91, 24, 0, 123, 249, 1, 228, 252, 195, 148, 69, + 90, 17, 73, 234, 72, 231, 118, 155, 165, 21, 214, 9, 128, 189, 252, 0, 10, 194, 231, 81, 164, + 223, 93, 102, 102, 202, 93, 41, 0, 182, 243, 3, 80, 42, 115, 66, 39, 215, 112, 64, 71, 47, + 60, 201, 206, 0, 152, 202, 15, 0, 135, 46, 196, 252, 99, 152, 129, 218, 40, 9, 171, 246, 117, + 74, 213, 57, 216, 174, 46, 96, 26, 63, 64, 233, 247, 110, 255, 63, 224, 56, 58, 76, 194, 101, + 35, 1, 53, 218, 91, 86, 14, 130, 150, 243, 3, 104, 202, 251, 44, 66, 198, 233, 2, 153, 188, + 91, 55, 57, 179, 55, 209, 211, 160, 41, 11, 33, 69, 64, 237, 73, 235, 6, 199, 3, 81, 136, + 166, 67, 147, 175, 187, 11, 113, 70, 170, 90, 181, 16, 178, 132, 31, 128, 120, 44, 48, 204, 63, + 60, 43, 82, 214, 121, 165, 201, 235, 235, 139, 85, 253, 33, 166, 237, 124, 57, 222, 156, 102, 9, + 227, 7, 144, 132, 246, 175, 96, 35, 117, 156, 213, 37, 70, 104, 181, 134, 164, 56, 219, 2, 134, + 242, 155, 187, 19, 122, 49, 100, 198, 229, 48, 205, 247, 129, 40, 19, 175, 107, 207, 252, 84, 176, + 183, 81, 66, 111, 86, 38, 187, 132, 241, 3, 80, 44, 5, 214, 73, 133, 37, 73, 164, 8, 17, + 1, 164, 112, 132, 168, 17, 184, 86, 40, 143, 18, 124, 43, 111, 136, 88, 194, 15, 192, 249, 30, + 206, 234, 211, 96, 83, 117, 207, 176, 113, 48, 86, 17, 36, 165, 134, 218, 133, 108, 191, 37, 22, + 203, 77, 81, 148, 192, 131, 211, 223, 71, 224, 5, 168, 103, 253, 52, 157, 241, 17, 96, 245, 33, + 56, 183, 36, 242, 166, 168, 233, 252, 0, 26, 248, 62, 193, 100, 203, 212, 247, 141, 154, 125, 128, + 180, 196, 83, 105, 100, 191, 194, 74, 141, 97, 66, 248, 1, 229, 190, 95, 178, 4, 156, 104, 77, + 24, 12, 17, 225, 98, 103, 6, 180, 18, 116, 252, 81, 199, 63, 24, 49, 122, 52, 38, 111, 129, + 105, 63, 99, 147, 168, 252, 245, 124, 209, 180, 211, 248, 173, 97, 156, 63, 74, 87, 120, 239, 16, + 80, 237, 202, 4, 57, 111, 61, 63, 96, 193, 246, 195, 162, 73, 159, 201, 161, 115, 122, 254, 168, + 174, 44, 100, 224, 108, 111, 130, 191, 245, 69, 184, 130, 21, 3, 156, 229, 15, 71, 245, 30, 143, + 171, 31, 50, 125, 237, 9, 81, 183, 227, 66, 112, 120, 172, 222, 10, 111, 43, 216, 24, 8, 198, + 189, 112, 188, 41, 41, 129, 37, 33, 252, 128, 79, 178, 143, 136, 135, 188, 171, 69, 125, 119, 86, + 16, 135, 135, 178, 249, 228, 214, 105, 162, 121, 135, 140, 44, 8, 194, 127, 213, 171, 65, 27, 156, + 183, 142, 31, 208, 103, 193, 47, 162, 221, 200, 67, 226, 54, 223, 118, 81, 195, 51, 61, 120, 198, + 155, 181, 202, 16, 41, 157, 178, 68, 245, 207, 102, 139, 74, 67, 22, 156, 65, 45, 191, 13, 15, + 63, 172, 231, 7, 60, 57, 106, 153, 112, 13, 221, 43, 106, 180, 158, 173, 156, 113, 116, 30, 3, + 81, 206, 187, 88, 212, 253, 96, 175, 120, 114, 228, 82, 252, 96, 12, 64, 121, 27, 2, 96, 61, + 63, 160, 252, 119, 139, 39, 214, 237, 55, 77, 36, 183, 157, 168, 80, 35, 30, 243, 254, 40, 202, + 250, 54, 139, 167, 223, 203, 19, 149, 70, 175, 80, 215, 5, 46, 27, 31, 129, 89, 203, 15, 128, + 254, 93, 171, 94, 159, 236, 188, 38, 125, 166, 136, 186, 253, 182, 139, 91, 219, 238, 21, 213, 223, + 205, 19, 213, 71, 7, 183, 201, 188, 238, 128, 231, 128, 214, 242, 3, 26, 118, 159, 244, 47, 104, + 9, 59, 106, 143, 89, 39, 90, 124, 117, 64, 60, 61, 102, 181, 234, 124, 233, 225, 7, 168, 155, + 165, 165, 93, 222, 165, 143, 31, 160, 110, 151, 103, 126, 0, 243, 3, 184, 112, 225, 194, 133, 11, + 23, 46, 69, 159, 163, 153, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, + 0, 243, 3, 152, 31, 192, 252, 128, 232, 111, 113, 49, 63, 128, 234, 193, 252, 0, 170, 15, 243, + 3, 168, 110, 204, 15, 96, 126, 64, 18, 243, 3, 228, 32, 48, 63, 128, 249, 1, 204, 15, 96, + 126, 0, 243, 3, 200, 15, 230, 7, 48, 63, 128, 249, 1, 14, 228, 7, 164, 184, 149, 29, 230, + 245, 193, 106, 38, 32, 0, 142, 227, 7, 148, 113, 121, 210, 238, 2, 27, 70, 251, 138, 43, 36, + 98, 74, 116, 12, 63, 192, 213, 42, 245, 60, 104, 1, 195, 165, 253, 197, 171, 49, 89, 163, 197, + 1, 112, 6, 63, 0, 156, 191, 21, 156, 29, 172, 147, 134, 123, 152, 213, 227, 128, 19, 248, 1, + 55, 193, 153, 127, 77, 217, 70, 239, 209, 213, 16, 121, 45, 12, 128, 189, 252, 0, 10, 64, 51, + 232, 247, 219, 34, 104, 138, 30, 183, 40, 0, 246, 242, 3, 224, 204, 63, 27, 76, 193, 173, 127, + 246, 81, 105, 134, 175, 253, 104, 81, 0, 236, 225, 7, 16, 57, 162, 25, 216, 28, 176, 83, 97, + 196, 84, 242, 107, 95, 217, 25, 0, 211, 248, 1, 148, 134, 247, 62, 56, 251, 163, 116, 206, 116, + 64, 87, 88, 229, 14, 74, 111, 94, 177, 171, 11, 152, 201, 15, 184, 20, 44, 53, 10, 213, 184, + 54, 0, 42, 84, 229, 97, 43, 7, 65, 75, 249, 1, 80, 249, 187, 72, 64, 125, 76, 39, 9, + 171, 110, 0, 116, 254, 182, 216, 142, 105, 48, 238, 133, 16, 156, 197, 219, 161, 242, 61, 52, 26, + 226, 240, 137, 152, 141, 3, 244, 185, 149, 11, 33, 211, 249, 1, 52, 232, 189, 0, 142, 236, 213, + 29, 233, 117, 120, 33, 218, 215, 37, 221, 49, 254, 127, 76, 231, 251, 207, 132, 45, 133, 245, 46, + 134, 138, 200, 15, 248, 175, 214, 113, 35, 169, 124, 72, 48, 10, 248, 2, 56, 85, 238, 131, 159, + 49, 127, 57, 230, 41, 174, 16, 175, 222, 48, 33, 252, 0, 162, 197, 96, 133, 23, 7, 211, 111, + 71, 78, 190, 174, 125, 15, 42, 80, 135, 147, 20, 23, 63, 243, 18, 187, 46, 135, 139, 116, 67, + 132, 156, 127, 20, 206, 90, 70, 52, 125, 61, 63, 61, 111, 240, 236, 31, 34, 130, 212, 215, 116, + 182, 239, 177, 250, 134, 136, 233, 252, 0, 112, 232, 42, 168, 248, 196, 136, 112, 148, 130, 223, 85, + 62, 200, 116, 176, 15, 192, 106, 171, 235, 6, 71, 220, 18, 43, 202, 77, 81, 168, 244, 253, 200, + 5, 132, 32, 252, 33, 45, 114, 242, 251, 52, 245, 253, 144, 49, 192, 237, 223, 14, 246, 51, 53, + 243, 71, 145, 55, 146, 232, 155, 162, 166, 241, 3, 48, 219, 108, 211, 78, 227, 71, 66, 115, 222, + 21, 210, 167, 245, 81, 56, 39, 8, 152, 128, 136, 141, 91, 19, 148, 132, 221, 58, 126, 192, 183, + 107, 118, 139, 218, 31, 207, 16, 45, 124, 105, 103, 245, 48, 56, 212, 18, 112, 237, 191, 1, 108, + 2, 178, 128, 192, 238, 77, 74, 96, 177, 140, 31, 112, 236, 143, 115, 194, 247, 241, 154, 160, 124, + 86, 179, 132, 253, 147, 250, 248, 10, 176, 119, 193, 146, 105, 68, 63, 207, 206, 71, 99, 166, 241, + 3, 210, 23, 29, 21, 213, 59, 175, 17, 245, 61, 89, 210, 34, 38, 132, 7, 136, 80, 148, 183, + 192, 254, 1, 65, 185, 177, 88, 63, 28, 149, 31, 143, 143, 88, 189, 79, 180, 251, 238, 144, 184, + 203, 183, 69, 212, 240, 228, 20, 76, 105, 222, 124, 249, 124, 131, 158, 153, 127, 54, 126, 125, 226, + 23, 42, 41, 54, 209, 103, 60, 154, 199, 227, 113, 241, 3, 154, 248, 127, 18, 213, 222, 218, 45, + 106, 146, 243, 120, 214, 27, 120, 50, 69, 93, 239, 84, 225, 250, 96, 173, 168, 56, 124, 33, 242, + 68, 142, 56, 117, 131, 68, 220, 252, 128, 42, 131, 231, 139, 148, 55, 166, 136, 150, 158, 177, 162, + 153, 103, 156, 168, 224, 93, 36, 30, 244, 174, 17, 55, 250, 246, 136, 219, 58, 28, 16, 41, 195, + 247, 202, 27, 37, 74, 22, 63, 0, 201, 78, 208, 196, 103, 130, 9, 87, 175, 185, 226, 159, 29, + 119, 136, 155, 125, 187, 196, 117, 173, 115, 243, 205, 183, 95, 212, 253, 56, 79, 32, 95, 160, 196, + 242, 3, 32, 8, 143, 61, 53, 112, 214, 188, 74, 163, 127, 20, 77, 62, 203, 21, 183, 180, 219, + 47, 174, 111, 125, 64, 9, 64, 169, 225, 7, 168, 27, 37, 107, 142, 89, 35, 106, 127, 152, 39, + 110, 110, 151, 91, 250, 248, 1, 234, 86, 89, 230, 7, 48, 63, 128, 249, 1, 204, 15, 224, 194, + 133, 11, 23, 46, 92, 184, 196, 58, 71, 51, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, + 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 209, 223, 226, 98, 126, 0, 213, 131, 249, + 1, 84, 31, 230, 7, 80, 221, 152, 31, 192, 252, 128, 36, 230, 7, 200, 65, 96, 126, 0, 243, + 3, 152, 31, 192, 252, 0, 230, 7, 144, 31, 204, 15, 96, 126, 128, 147, 249, 1, 86, 239, 22, + 39, 127, 28, 200, 15, 200, 215, 22, 149, 35, 161, 196, 237, 22, 7, 192, 89, 252, 0, 10, 192, + 213, 148, 154, 19, 55, 86, 143, 75, 196, 148, 232, 24, 126, 0, 137, 44, 126, 208, 40, 200, 94, + 180, 56, 0, 246, 243, 3, 104, 219, 124, 85, 69, 77, 234, 246, 31, 212, 104, 12, 246, 129, 253, + 159, 149, 227, 128, 19, 248, 1, 183, 186, 90, 165, 126, 9, 199, 227, 6, 162, 233, 65, 86, 46, + 138, 108, 229, 7, 128, 115, 119, 130, 101, 193, 153, 15, 24, 72, 234, 212, 96, 212, 182, 40, 0, + 246, 241, 3, 192, 169, 167, 192, 166, 104, 117, 196, 133, 90, 64, 126, 80, 86, 90, 20, 128, 196, + 243, 3, 72, 79, 248, 16, 12, 118, 67, 192, 185, 35, 69, 16, 81, 246, 177, 51, 0, 230, 241, + 3, 220, 254, 155, 192, 153, 140, 40, 69, 210, 90, 123, 216, 174, 46, 96, 10, 63, 0, 28, 124, + 6, 156, 152, 15, 199, 147, 33, 14, 71, 175, 36, 159, 98, 245, 32, 104, 9, 63, 0, 51, 207, + 67, 229, 43, 195, 241, 123, 112, 246, 183, 66, 34, 234, 104, 244, 196, 5, 93, 194, 99, 199, 52, + 24, 243, 66, 136, 230, 121, 76, 173, 235, 143, 134, 21, 64, 250, 194, 112, 65, 64, 214, 208, 245, + 86, 46, 132, 76, 229, 7, 224, 104, 15, 103, 126, 105, 84, 168, 140, 200, 32, 21, 85, 99, 220, + 53, 161, 75, 97, 189, 139, 161, 72, 252, 0, 168, 228, 197, 96, 181, 148, 1, 15, 71, 123, 183, + 33, 51, 192, 104, 48, 148, 157, 255, 13, 254, 63, 42, 202, 179, 17, 172, 4, 214, 40, 222, 236, + 181, 150, 242, 3, 168, 217, 223, 2, 54, 38, 196, 41, 119, 33, 71, 3, 70, 253, 159, 222, 255, + 135, 34, 194, 246, 164, 125, 10, 199, 6, 40, 212, 50, 107, 105, 108, 41, 63, 64, 25, 240, 90, + 165, 46, 137, 122, 96, 11, 149, 212, 239, 0, 135, 87, 147, 168, 186, 26, 252, 142, 106, 242, 11, + 45, 88, 3, 88, 192, 15, 24, 185, 84, 52, 236, 49, 9, 149, 224, 211, 104, 109, 31, 60, 243, + 58, 115, 188, 76, 143, 58, 74, 77, 252, 83, 106, 226, 229, 213, 251, 3, 142, 185, 37, 22, 233, + 166, 232, 160, 149, 187, 196, 51, 195, 23, 138, 198, 93, 39, 238, 36, 177, 116, 104, 147, 119, 235, + 246, 115, 60, 211, 99, 193, 122, 147, 124, 254, 134, 68, 200, 107, 45, 225, 7, 140, 152, 182, 75, + 36, 119, 153, 20, 110, 101, 167, 130, 144, 182, 194, 235, 27, 136, 25, 128, 11, 163, 187, 139, 205, + 109, 113, 189, 7, 35, 107, 119, 158, 18, 47, 126, 176, 73, 212, 243, 100, 195, 40, 175, 187, 148, + 61, 169, 0, 84, 220, 254, 89, 112, 124, 7, 236, 37, 26, 204, 18, 154, 131, 220, 148, 7, 35, + 242, 163, 177, 49, 235, 115, 69, 215, 209, 135, 242, 179, 76, 123, 102, 138, 100, 73, 54, 175, 152, + 87, 57, 219, 115, 104, 221, 239, 162, 89, 225, 250, 36, 27, 139, 169, 252, 128, 148, 209, 219, 68, + 229, 30, 191, 136, 90, 158, 105, 65, 199, 81, 64, 221, 210, 55, 78, 52, 237, 145, 45, 158, 30, + 56, 75, 212, 126, 119, 106, 63, 104, 238, 142, 216, 174, 106, 58, 63, 160, 230, 39, 115, 132, 171, + 221, 68, 56, 243, 233, 162, 185, 39, 67, 60, 229, 157, 37, 202, 123, 23, 139, 127, 250, 126, 18, + 77, 63, 223, 143, 178, 249, 98, 241, 120, 60, 38, 126, 0, 156, 241, 7, 234, 247, 201, 218, 215, + 242, 141, 44, 81, 191, 243, 66, 113, 135, 239, 103, 113, 187, 111, 91, 80, 57, 126, 75, 251, 253, + 194, 53, 108, 159, 227, 55, 72, 196, 197, 15, 104, 208, 43, 243, 141, 202, 131, 231, 139, 103, 71, + 238, 20, 15, 118, 203, 45, 144, 205, 147, 221, 214, 49, 183, 100, 243, 3, 232, 11, 122, 34, 31, + 192, 53, 116, 175, 184, 163, 83, 94, 104, 16, 74, 3, 63, 64, 93, 28, 49, 63, 128, 249, 1, + 204, 15, 96, 126, 128, 212, 18, 152, 31, 192, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 12, + 115, 52, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, + 243, 3, 152, 31, 16, 253, 45, 46, 230, 7, 80, 61, 152, 31, 64, 245, 97, 126, 0, 213, 141, + 249, 1, 204, 15, 72, 98, 126, 128, 28, 4, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, + 0, 249, 193, 252, 0, 230, 7, 56, 156, 31, 128, 59, 199, 255, 102, 113, 0, 28, 201, 15, 56, + 159, 50, 211, 246, 112, 181, 74, 253, 200, 226, 0, 56, 146, 31, 240, 55, 151, 39, 109, 144, 180, + 211, 188, 134, 213, 83, 162, 35, 248, 1, 224, 116, 25, 18, 66, 45, 214, 164, 226, 206, 177, 56, + 0, 142, 224, 7, 92, 9, 1, 104, 72, 169, 116, 143, 233, 136, 167, 154, 88, 57, 14, 216, 206, + 15, 128, 190, 142, 25, 232, 55, 134, 73, 193, 187, 200, 202, 69, 145, 109, 252, 0, 148, 202, 80, + 114, 213, 35, 81, 136, 168, 159, 183, 40, 0, 137, 231, 7, 4, 179, 207, 123, 252, 253, 225, 236, + 255, 108, 164, 34, 213, 136, 44, 215, 90, 20, 0, 27, 248, 1, 249, 57, 200, 71, 70, 74, 182, + 174, 211, 10, 60, 118, 6, 32, 110, 126, 0, 102, 140, 6, 83, 251, 251, 233, 8, 205, 94, 213, + 13, 203, 169, 184, 183, 217, 217, 5, 98, 230, 7, 144, 140, 246, 26, 74, 180, 58, 189, 16, 59, + 64, 39, 3, 117, 24, 65, 117, 103, 171, 7, 65, 211, 249, 1, 40, 132, 68, 205, 63, 28, 119, + 132, 85, 139, 187, 141, 243, 146, 75, 140, 129, 60, 179, 114, 143, 23, 117, 26, 140, 105, 33, 132, + 172, 16, 194, 98, 29, 141, 66, 50, 175, 21, 82, 27, 241, 5, 250, 90, 189, 16, 138, 155, 31, + 0, 149, 188, 8, 154, 248, 163, 138, 22, 216, 237, 223, 165, 109, 222, 114, 243, 151, 48, 26, 198, + 1, 113, 135, 4, 5, 21, 229, 87, 153, 20, 0, 107, 248, 1, 80, 193, 7, 41, 179, 180, 126, + 255, 14, 211, 220, 117, 222, 255, 39, 101, 163, 63, 173, 124, 166, 91, 201, 95, 252, 72, 66, 47, + 134, 162, 189, 28, 174, 56, 108, 97, 27, 168, 220, 43, 96, 191, 144, 78, 88, 158, 211, 3, 58, + 86, 152, 39, 144, 79, 136, 56, 71, 45, 99, 42, 216, 16, 176, 238, 20, 212, 191, 195, 235, 127, + 51, 171, 255, 155, 202, 15, 168, 11, 209, 172, 249, 126, 206, 122, 168, 228, 2, 69, 43, 28, 142, + 5, 18, 170, 42, 255, 83, 82, 155, 207, 131, 99, 38, 216, 255, 192, 158, 160, 49, 228, 134, 68, + 220, 16, 137, 139, 31, 176, 235, 200, 9, 241, 236, 128, 57, 162, 101, 219, 116, 99, 71, 11, 7, + 1, 157, 94, 73, 134, 8, 189, 122, 68, 151, 186, 57, 201, 226, 98, 26, 63, 224, 212, 153, 115, + 226, 131, 244, 221, 162, 97, 135, 217, 34, 197, 151, 30, 150, 5, 2, 63, 43, 210, 121, 176, 3, + 224, 252, 112, 56, 254, 7, 172, 25, 222, 253, 81, 7, 78, 59, 111, 138, 22, 137, 31, 48, 108, + 213, 62, 241, 206, 248, 223, 196, 35, 175, 109, 21, 85, 189, 115, 69, 83, 207, 120, 82, 141, 135, + 44, 110, 206, 208, 188, 125, 130, 88, 34, 221, 192, 254, 11, 239, 185, 141, 238, 252, 252, 165, 216, + 221, 22, 87, 31, 140, 184, 70, 109, 19, 85, 186, 111, 17, 53, 61, 83, 131, 14, 163, 124, 222, + 229, 77, 87, 2, 209, 248, 245, 137, 8, 75, 250, 12, 172, 35, 173, 253, 175, 51, 107, 234, 178, + 245, 193, 8, 150, 74, 67, 23, 244, 169, 247, 193, 44, 225, 106, 59, 65, 113, 26, 13, 37, 244, + 207, 120, 102, 136, 234, 237, 151, 138, 150, 67, 215, 139, 138, 67, 22, 224, 140, 48, 192, 233, 143, + 198, 98, 226, 7, 36, 183, 78, 171, 93, 175, 111, 246, 31, 174, 46, 153, 162, 78, 167, 69, 10, + 64, 225, 46, 223, 22, 113, 147, 111, 183, 34, 153, 173, 214, 47, 87, 60, 83, 32, 156, 44, 121, + 15, 71, 17, 144, 84, 171, 255, 180, 47, 171, 126, 3, 103, 123, 240, 126, 113, 71, 199, 253, 138, + 227, 170, 102, 248, 198, 54, 251, 69, 211, 47, 138, 207, 227, 241, 152, 248, 1, 106, 95, 106, 156, + 186, 73, 84, 233, 151, 87, 72, 58, 95, 42, 248, 1, 184, 157, 4, 231, 74, 133, 31, 208, 61, + 143, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 11, + 23, 46, 92, 184, 112, 225, 18, 243, 28, 205, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, + 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 68, 127, 139, 139, 249, 1, 84, 15, 230, + 7, 80, 125, 152, 31, 64, 117, 99, 126, 0, 243, 3, 146, 152, 31, 32, 7, 129, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 64, 126, 48, 63, 128, 249, 1, 14, 231, 7, 92, 107, 85, + 182, 105, 201, 31, 71, 242, 3, 208, 174, 38, 213, 201, 124, 176, 250, 22, 6, 192, 145, 252, 128, + 242, 46, 79, 218, 104, 73, 78, 51, 201, 234, 41, 209, 118, 126, 0, 157, 117, 212, 16, 180, 74, + 113, 195, 89, 119, 251, 143, 147, 118, 72, 85, 156, 60, 104, 97, 0, 28, 193, 15, 64, 49, 117, + 87, 176, 245, 178, 212, 70, 18, 90, 125, 102, 229, 56, 96, 27, 63, 0, 156, 188, 8, 156, 68, + 201, 204, 162, 16, 161, 164, 91, 55, 97, 227, 229, 22, 5, 32, 241, 252, 0, 146, 203, 220, 1, + 214, 70, 25, 232, 220, 254, 19, 81, 36, 93, 127, 205, 162, 0, 216, 194, 15, 192, 236, 243, 3, + 193, 126, 45, 66, 6, 234, 141, 22, 5, 32, 113, 252, 0, 20, 61, 42, 3, 93, 190, 114, 236, + 108, 24, 109, 176, 94, 90, 94, 60, 54, 179, 51, 0, 49, 243, 3, 80, 61, 14, 125, 253, 54, + 104, 230, 157, 193, 150, 147, 154, 44, 178, 144, 186, 240, 123, 38, 219, 217, 5, 226, 225, 7, 84, + 198, 244, 217, 6, 10, 241, 112, 66, 203, 128, 206, 207, 143, 90, 61, 8, 154, 198, 15, 128, 202, + 150, 5, 235, 130, 252, 15, 210, 3, 7, 10, 129, 19, 36, 197, 184, 33, 80, 33, 244, 245, 65, + 118, 77, 131, 69, 90, 8, 81, 127, 127, 27, 108, 147, 246, 12, 107, 2, 16, 57, 31, 121, 232, + 123, 112, 236, 184, 218, 234, 133, 80, 204, 252, 0, 76, 146, 78, 156, 144, 229, 154, 179, 23, 136, + 114, 196, 15, 149, 214, 235, 7, 168, 107, 194, 151, 194, 122, 23, 67, 50, 63, 224, 249, 172, 117, + 162, 242, 144, 5, 162, 101, 235, 244, 247, 148, 124, 226, 238, 16, 72, 138, 225, 104, 175, 11, 82, + 40, 108, 191, 193, 32, 138, 4, 138, 223, 193, 178, 192, 250, 153, 228, 188, 121, 252, 128, 174, 83, + 55, 136, 250, 111, 78, 22, 201, 222, 180, 227, 17, 161, 40, 110, 131, 150, 144, 255, 250, 33, 210, + 27, 227, 107, 217, 96, 95, 128, 125, 8, 246, 2, 174, 31, 224, 179, 30, 0, 187, 201, 174, 203, + 225, 66, 55, 68, 102, 174, 58, 44, 90, 127, 184, 90, 184, 218, 140, 143, 212, 119, 181, 127, 11, + 40, 103, 214, 147, 182, 29, 143, 4, 74, 248, 0, 156, 123, 19, 94, 123, 17, 33, 9, 240, 251, + 237, 178, 148, 222, 236, 172, 244, 49, 243, 3, 250, 45, 216, 33, 62, 158, 114, 68, 212, 236, 187, + 67, 201, 52, 221, 208, 51, 73, 184, 220, 105, 70, 14, 159, 163, 22, 112, 140, 248, 65, 72, 146, + 193, 36, 236, 253, 40, 251, 252, 191, 201, 81, 196, 236, 252, 221, 209, 183, 196, 212, 155, 162, 205, + 198, 108, 17, 181, 251, 110, 22, 53, 60, 211, 67, 153, 32, 193, 190, 172, 156, 221, 53, 202, 88, + 224, 241, 207, 6, 195, 179, 138, 172, 128, 231, 192, 254, 73, 215, 2, 127, 51, 251, 140, 198, 123, + 83, 52, 42, 126, 192, 211, 3, 102, 182, 175, 255, 110, 14, 229, 28, 79, 83, 6, 174, 250, 158, + 44, 81, 199, 51, 69, 52, 106, 59, 77, 52, 123, 111, 182, 168, 62, 112, 150, 168, 223, 43, 171, + 55, 56, 248, 48, 53, 221, 43, 32, 64, 142, 217, 214, 26, 215, 109, 113, 112, 166, 82, 189, 62, + 217, 71, 93, 221, 166, 136, 218, 157, 150, 136, 123, 125, 27, 21, 195, 220, 227, 40, 150, 76, 30, + 188, 79, 84, 26, 185, 172, 228, 62, 24, 65, 204, 197, 83, 3, 103, 13, 120, 106, 244, 106, 69, + 33, 126, 115, 219, 253, 33, 114, 217, 127, 118, 205, 19, 45, 71, 255, 34, 158, 24, 85, 60, 30, + 141, 197, 196, 15, 80, 31, 142, 34, 69, 226, 201, 190, 133, 69, 211, 213, 222, 205, 43, 217, 252, + 0, 245, 241, 120, 229, 209, 43, 242, 249, 1, 175, 229, 50, 63, 128, 249, 1, 204, 15, 96, 126, + 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 190, 132, 249, 1, 92, 184, 112, 225, + 194, 133, 11, 151, 88, 230, 104, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, + 152, 31, 192, 252, 0, 230, 7, 48, 63, 32, 250, 91, 92, 204, 15, 160, 122, 48, 63, 128, 234, + 195, 252, 0, 170, 27, 243, 3, 152, 31, 144, 196, 252, 0, 57, 8, 204, 15, 96, 126, 0, 243, + 3, 152, 31, 192, 252, 0, 242, 131, 249, 1, 204, 15, 112, 56, 63, 224, 42, 176, 103, 192, 170, + 90, 24, 0, 231, 241, 3, 200, 249, 139, 83, 220, 254, 20, 82, 140, 204, 179, 48, 0, 206, 226, + 7, 184, 60, 105, 152, 145, 238, 89, 56, 206, 208, 232, 140, 30, 181, 48, 8, 246, 243, 3, 232, + 172, 223, 3, 103, 189, 7, 28, 151, 128, 105, 117, 71, 239, 88, 24, 0, 123, 249, 1, 164, 28, + 113, 131, 45, 13, 163, 29, 220, 104, 229, 56, 144, 112, 126, 128, 146, 137, 218, 227, 191, 206, 213, + 42, 181, 49, 28, 17, 151, 177, 87, 22, 74, 27, 200, 231, 202, 91, 20, 0, 91, 248, 1, 151, + 67, 115, 71, 193, 212, 178, 136, 170, 209, 2, 251, 192, 162, 0, 36, 134, 31, 64, 156, 16, 212, + 18, 183, 3, 251, 73, 145, 192, 186, 11, 105, 6, 3, 97, 132, 150, 63, 91, 20, 0, 235, 249, + 1, 80, 249, 50, 96, 247, 130, 125, 4, 142, 174, 151, 156, 215, 242, 66, 34, 105, 14, 171, 218, + 25, 128, 152, 248, 1, 80, 233, 251, 193, 185, 183, 193, 161, 3, 145, 100, 180, 134, 138, 210, 2, + 251, 196, 206, 46, 16, 53, 63, 128, 206, 248, 141, 36, 162, 30, 175, 228, 25, 246, 68, 150, 201, + 27, 137, 171, 165, 247, 239, 74, 196, 32, 24, 55, 63, 128, 154, 251, 0, 82, 137, 26, 10, 166, + 161, 101, 4, 194, 180, 8, 189, 217, 0, 223, 91, 211, 174, 105, 48, 252, 66, 8, 254, 86, 253, + 211, 217, 152, 92, 249, 77, 98, 132, 156, 9, 194, 144, 162, 80, 145, 71, 193, 17, 177, 10, 160, + 16, 63, 63, 96, 198, 246, 95, 69, 211, 111, 22, 137, 6, 61, 38, 161, 234, 123, 151, 122, 150, + 101, 118, 128, 242, 179, 134, 9, 80, 40, 64, 110, 127, 88, 149, 57, 189, 63, 215, 150, 165, 176, + 222, 197, 208, 193, 35, 167, 196, 208, 204, 157, 226, 213, 183, 230, 235, 113, 0, 2, 225, 242, 144, + 107, 156, 14, 104, 166, 65, 197, 89, 122, 255, 17, 248, 219, 33, 34, 71, 236, 195, 139, 35, 85, + 146, 155, 208, 139, 33, 245, 114, 248, 133, 204, 245, 226, 131, 28, 24, 28, 62, 220, 39, 30, 242, + 174, 86, 50, 78, 55, 247, 100, 136, 24, 154, 54, 58, 121, 4, 142, 71, 213, 1, 14, 108, 33, + 45, 142, 144, 57, 50, 12, 254, 254, 22, 56, 175, 170, 204, 107, 194, 239, 143, 192, 177, 26, 174, + 34, 237, 186, 28, 110, 129, 138, 208, 22, 255, 219, 40, 158, 246, 206, 84, 196, 211, 5, 205, 189, + 144, 131, 72, 123, 248, 131, 254, 190, 29, 28, 89, 2, 131, 222, 122, 56, 174, 5, 27, 6, 175, + 191, 71, 96, 4, 197, 57, 212, 36, 131, 213, 64, 117, 57, 13, 164, 255, 135, 215, 9, 242, 242, + 217, 234, 27, 34, 17, 249, 1, 77, 59, 143, 47, 95, 247, 173, 201, 34, 229, 181, 73, 224, 124, + 126, 146, 245, 70, 173, 38, 40, 234, 241, 102, 190, 137, 34, 185, 243, 15, 162, 94, 159, 108, 209, + 172, 253, 88, 28, 12, 63, 37, 231, 106, 145, 131, 181, 193, 238, 198, 41, 18, 2, 129, 176, 196, + 107, 37, 231, 206, 47, 22, 183, 196, 160, 162, 183, 214, 235, 157, 185, 185, 197, 155, 51, 69, 253, + 110, 63, 42, 170, 241, 127, 250, 54, 136, 123, 189, 27, 197, 173, 190, 29, 34, 101, 232, 110, 81, + 237, 155, 165, 162, 226, 240, 133, 227, 164, 255, 115, 65, 113, 185, 41, 26, 21, 63, 160, 242, 224, + 249, 255, 169, 54, 122, 165, 162, 25, 190, 181, 93, 168, 114, 252, 209, 94, 121, 162, 97, 234, 79, + 170, 114, 188, 100, 222, 22, 87, 31, 140, 24, 41, 199, 147, 135, 236, 23, 149, 70, 151, 224, 7, + 35, 234, 163, 49, 149, 31, 112, 83, 187, 92, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, + 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 32, 137, 249, 1, 234, 151, 48, 63, 128, + 11, 23, 46, 92, 184, 112, 225, 18, 203, 28, 205, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 68, 127, 139, 139, 249, 1, 84, 15, + 230, 7, 80, 125, 152, 31, 64, 117, 99, 126, 0, 243, 3, 146, 152, 31, 32, 7, 129, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 64, 126, 48, 63, 128, 249, 1, 78, 229, 7, 144, 200, + 242, 18, 151, 39, 237, 97, 218, 114, 127, 181, 69, 1, 112, 44, 63, 224, 106, 176, 23, 83, 220, + 254, 9, 112, 220, 143, 217, 38, 45, 10, 128, 115, 248, 1, 224, 228, 101, 96, 143, 208, 25, 95, + 76, 9, 26, 3, 164, 40, 29, 102, 229, 148, 104, 59, 63, 0, 69, 21, 96, 45, 192, 252, 224, + 240, 78, 29, 125, 209, 86, 11, 3, 96, 15, 63, 128, 206, 56, 246, 241, 142, 96, 63, 146, 98, + 44, 80, 72, 67, 92, 96, 119, 89, 53, 14, 216, 193, 15, 64, 107, 8, 246, 61, 56, 252, 51, + 101, 165, 22, 58, 42, 114, 89, 142, 247, 170, 85, 139, 162, 132, 240, 3, 192, 129, 75, 193, 30, + 2, 107, 79, 152, 140, 211, 69, 16, 81, 163, 141, 180, 40, 0, 214, 243, 3, 72, 76, 93, 7, + 206, 230, 8, 56, 110, 54, 116, 222, 173, 43, 174, 86, 69, 149, 187, 45, 10, 128, 53, 252, 0, + 5, 133, 227, 241, 223, 15, 230, 35, 181, 103, 52, 41, 184, 3, 58, 201, 154, 101, 187, 223, 206, + 0, 68, 205, 15, 192, 69, 12, 156, 181, 103, 224, 76, 14, 1, 71, 48, 215, 240, 201, 88, 148, + 165, 58, 214, 198, 206, 46, 16, 158, 31, 48, 106, 169, 120, 230, 131, 233, 255, 38, 34, 204, 204, + 112, 178, 248, 72, 103, 63, 204, 152, 48, 54, 17, 131, 96, 145, 249, 1, 239, 46, 250, 69, 60, + 243, 213, 60, 209, 164, 243, 132, 57, 80, 201, 13, 112, 214, 79, 70, 155, 130, 187, 80, 127, 55, + 152, 9, 232, 125, 121, 118, 78, 131, 133, 22, 66, 203, 126, 58, 44, 62, 30, 189, 73, 252, 167, + 247, 52, 61, 237, 112, 120, 115, 135, 170, 201, 213, 159, 141, 232, 18, 210, 235, 143, 39, 98, 33, + 100, 200, 15, 232, 152, 181, 83, 164, 124, 154, 43, 30, 239, 184, 89, 84, 241, 206, 203, 207, 59, + 238, 77, 23, 81, 84, 92, 219, 18, 140, 216, 1, 70, 242, 123, 245, 247, 78, 182, 44, 133, 177, + 84, 28, 190, 112, 157, 107, 224, 114, 81, 191, 253, 44, 209, 194, 51, 214, 240, 172, 75, 249, 200, + 181, 32, 132, 63, 53, 206, 156, 36, 46, 128, 172, 56, 63, 46, 189, 7, 129, 12, 123, 32, 32, + 71, 233, 247, 61, 102, 130, 84, 138, 116, 49, 132, 114, 246, 6, 189, 50, 151, 183, 236, 150, 37, + 146, 125, 25, 138, 243, 77, 61, 227, 149, 22, 208, 216, 51, 81, 164, 120, 211, 68, 139, 246, 25, + 162, 69, 187, 177, 184, 178, 91, 160, 72, 230, 243, 121, 65, 147, 225, 231, 12, 8, 194, 76, 186, + 200, 241, 131, 125, 129, 23, 56, 96, 233, 240, 183, 65, 112, 236, 69, 28, 177, 247, 145, 27, 70, + 3, 41, 38, 106, 111, 13, 214, 22, 172, 37, 88, 5, 176, 122, 96, 77, 108, 187, 28, 174, 223, + 59, 235, 179, 250, 31, 204, 22, 45, 250, 175, 21, 119, 249, 182, 136, 123, 188, 27, 197, 253, 190, + 117, 138, 138, 188, 217, 128, 237, 162, 214, 176, 101, 162, 250, 103, 179, 151, 67, 37, 43, 130, 221, + 68, 12, 128, 178, 180, 30, 184, 142, 16, 90, 248, 218, 45, 234, 53, 62, 101, 162, 199, 215, 46, + 215, 44, 149, 47, 214, 44, 166, 206, 183, 96, 0, 44, 26, 63, 64, 121, 10, 60, 106, 153, 120, + 246, 219, 221, 226, 238, 206, 161, 26, 193, 135, 123, 228, 138, 38, 99, 54, 169, 18, 185, 146, 121, + 75, 76, 189, 41, 218, 108, 204, 22, 69, 18, 167, 21, 74, 162, 72, 178, 202, 152, 21, 197, 246, + 166, 104, 84, 252, 0, 108, 18, 42, 63, 224, 150, 246, 185, 204, 15, 96, 126, 0, 243, 3, 152, + 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 32, + 183, 4, 230, 7, 48, 63, 128, 11, 23, 46, 92, 184, 112, 225, 18, 195, 28, 205, 252, 0, 230, + 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 68, + 127, 139, 139, 249, 1, 84, 15, 230, 7, 80, 125, 152, 31, 64, 117, 99, 126, 0, 243, 3, 146, + 152, 31, 32, 7, 129, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 64, 126, 48, 63, 160, + 216, 240, 3, 80, 119, 104, 81, 16, 156, 201, 15, 32, 167, 47, 32, 93, 1, 138, 37, 254, 99, + 81, 0, 156, 199, 15, 32, 118, 64, 133, 20, 183, 191, 51, 28, 179, 72, 38, 99, 85, 214, 105, + 71, 241, 3, 206, 119, 121, 210, 80, 22, 211, 207, 213, 42, 117, 62, 216, 111, 146, 108, 22, 237, + 10, 139, 130, 96, 15, 63, 64, 201, 60, 233, 73, 251, 27, 201, 107, 16, 154, 48, 11, 243, 143, + 134, 145, 207, 87, 180, 40, 0, 182, 241, 3, 238, 34, 199, 51, 41, 65, 243, 89, 29, 217, 188, + 44, 157, 115, 91, 53, 14, 88, 206, 15, 32, 17, 20, 10, 163, 30, 35, 49, 53, 246, 237, 19, + 81, 200, 106, 229, 215, 63, 179, 106, 81, 100, 57, 63, 128, 146, 173, 162, 4, 46, 3, 206, 236, + 86, 53, 27, 173, 70, 56, 169, 159, 134, 187, 96, 12, 152, 101, 81, 0, 204, 231, 7, 64, 101, + 47, 2, 187, 147, 146, 175, 127, 7, 182, 51, 10, 113, 116, 72, 118, 106, 37, 203, 116, 104, 82, + 230, 60, 139, 2, 96, 30, 63, 160, 218, 103, 115, 238, 134, 202, 62, 10, 149, 111, 131, 103, 27, + 236, 23, 248, 249, 140, 44, 128, 14, 73, 193, 29, 185, 249, 7, 52, 65, 186, 201, 206, 0, 24, + 242, 3, 90, 79, 219, 32, 170, 126, 49, 87, 52, 111, 155, 49, 68, 66, 224, 136, 34, 37, 94, + 119, 27, 171, 203, 165, 214, 80, 215, 206, 46, 16, 50, 8, 238, 206, 61, 46, 166, 44, 62, 32, + 62, 254, 126, 189, 120, 190, 247, 20, 209, 162, 237, 216, 72, 10, 242, 200, 1, 112, 71, 12, 78, + 151, 68, 12, 130, 134, 252, 128, 218, 105, 171, 197, 171, 35, 15, 136, 7, 59, 237, 20, 143, 123, + 151, 139, 26, 158, 233, 162, 137, 103, 130, 48, 133, 14, 161, 229, 135, 184, 117, 179, 208, 127, 107, + 219, 52, 88, 187, 223, 212, 198, 117, 62, 156, 41, 92, 93, 115, 68, 75, 79, 122, 36, 135, 34, + 209, 35, 2, 133, 128, 9, 238, 144, 85, 95, 64, 135, 51, 128, 182, 34, 81, 11, 161, 66, 252, + 128, 100, 111, 90, 109, 204, 43, 142, 121, 199, 81, 58, 175, 42, 199, 27, 121, 126, 16, 46, 95, + 6, 157, 61, 165, 194, 40, 121, 207, 131, 74, 159, 148, 164, 244, 103, 228, 169, 79, 34, 66, 252, + 169, 142, 242, 74, 63, 15, 165, 198, 232, 217, 105, 219, 150, 194, 240, 229, 127, 135, 129, 110, 179, + 107, 200, 122, 241, 68, 143, 61, 226, 14, 223, 207, 74, 222, 241, 7, 188, 107, 69, 149, 215, 55, + 136, 166, 131, 150, 163, 114, 92, 180, 108, 147, 222, 13, 222, 219, 19, 172, 43, 56, 213, 14, 142, + 221, 72, 46, 63, 10, 130, 129, 83, 226, 151, 112, 28, 6, 78, 78, 129, 159, 215, 129, 45, 2, + 155, 2, 191, 231, 192, 113, 141, 2, 85, 114, 251, 247, 209, 223, 208, 246, 195, 231, 168, 140, 1, + 12, 238, 157, 182, 92, 12, 169, 151, 195, 168, 16, 175, 249, 65, 168, 56, 234, 134, 54, 185, 34, + 101, 228, 54, 81, 49, 95, 28, 245, 178, 180, 0, 250, 139, 124, 17, 67, 60, 161, 191, 131, 221, + 6, 78, 254, 149, 94, 187, 6, 126, 126, 16, 142, 229, 192, 240, 120, 21, 45, 156, 48, 91, 125, + 11, 8, 86, 99, 56, 86, 135, 99, 77, 226, 10, 220, 102, 231, 229, 112, 11, 84, 132, 54, 31, + 116, 160, 144, 68, 174, 217, 151, 185, 170, 90, 212, 146, 27, 34, 86, 220, 20, 137, 143, 31, 208, + 37, 143, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, + 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 64, 95, 192, 252, 0, 230, 7, 36, 49, 63, + 128, 11, 23, 46, 92, 184, 112, 225, 18, 243, 28, 205, 252, 0, 230, 7, 48, 63, 128, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 68, 127, 139, 139, 249, 1, 84, + 15, 230, 7, 80, 125, 152, 31, 64, 117, 99, 126, 0, 243, 3, 146, 152, 31, 32, 7, 129, 249, + 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 64, 126, 48, 63, 160, 216, 240, 3, 176, 36, 98, + 187, 188, 163, 248, 1, 228, 52, 202, 109, 43, 145, 234, 228, 94, 11, 2, 224, 44, 126, 0, 57, + 252, 80, 138, 91, 81, 156, 126, 12, 54, 31, 126, 62, 224, 242, 164, 97, 110, 242, 151, 44, 8, + 128, 189, 252, 0, 69, 84, 237, 246, 95, 238, 106, 149, 138, 50, 219, 90, 120, 166, 225, 231, 116, + 210, 14, 29, 146, 242, 145, 162, 160, 234, 13, 171, 166, 68, 187, 248, 1, 87, 80, 243, 238, 11, + 78, 79, 131, 99, 174, 70, 49, 170, 149, 222, 125, 98, 81, 0, 172, 231, 7, 72, 242, 249, 199, + 193, 94, 6, 27, 78, 73, 216, 67, 164, 114, 70, 169, 123, 201, 210, 44, 10, 128, 249, 252, 0, + 114, 24, 21, 228, 55, 128, 61, 1, 246, 60, 56, 247, 49, 24, 158, 105, 148, 207, 31, 149, 53, + 131, 58, 25, 168, 245, 108, 174, 85, 139, 34, 211, 249, 1, 212, 188, 235, 130, 253, 143, 242, 144, + 239, 215, 146, 33, 164, 220, 196, 145, 20, 227, 170, 109, 177, 40, 0, 241, 243, 3, 60, 48, 93, + 84, 249, 106, 158, 104, 222, 33, 163, 23, 38, 72, 167, 1, 172, 64, 250, 154, 239, 108, 64, 146, + 207, 43, 1, 208, 4, 33, 160, 163, 21, 150, 91, 198, 49, 139, 2, 80, 52, 126, 192, 140, 237, + 135, 196, 206, 220, 147, 74, 222, 241, 137, 115, 118, 139, 79, 71, 173, 19, 207, 189, 61, 77, 52, + 237, 60, 30, 165, 179, 168, 236, 60, 21, 162, 3, 118, 235, 136, 163, 221, 209, 169, 202, 117, 128, + 42, 255, 103, 103, 0, 58, 55, 28, 189, 78, 52, 120, 127, 167, 34, 151, 173, 236, 157, 47, 234, + 120, 166, 136, 102, 158, 113, 138, 102, 88, 163, 250, 142, 86, 45, 30, 86, 47, 172, 126, 166, 212, + 10, 238, 182, 173, 11, 52, 232, 149, 57, 166, 121, 183, 108, 225, 106, 61, 206, 56, 231, 120, 228, + 129, 44, 156, 56, 58, 16, 238, 51, 169, 53, 84, 79, 196, 32, 168, 203, 15, 104, 216, 125, 210, + 231, 77, 123, 77, 22, 201, 175, 77, 22, 245, 60, 217, 162, 174, 103, 178, 168, 239, 201, 202, 231, + 7, 120, 211, 180, 142, 96, 31, 15, 196, 12, 83, 48, 14, 214, 179, 182, 77, 131, 21, 135, 47, + 124, 180, 202, 232, 21, 194, 53, 116, 159, 184, 201, 183, 91, 220, 233, 219, 42, 238, 243, 173, 23, + 15, 122, 215, 8, 215, 123, 43, 69, 253, 15, 103, 9, 104, 37, 203, 192, 241, 52, 74, 186, 190, + 27, 17, 88, 56, 122, 211, 207, 39, 228, 49, 64, 106, 45, 17, 131, 33, 189, 183, 99, 162, 22, + 66, 133, 248, 1, 184, 28, 124, 130, 116, 195, 90, 197, 104, 253, 1, 112, 37, 56, 114, 181, 168, + 48, 98, 209, 50, 82, 127, 151, 33, 38, 216, 147, 224, 108, 10, 28, 219, 131, 97, 118, 249, 183, + 192, 62, 133, 32, 101, 40, 203, 93, 183, 178, 30, 56, 78, 171, 192, 92, 3, 182, 144, 12, 85, + 122, 223, 182, 165, 176, 122, 49, 228, 26, 189, 77, 145, 200, 106, 37, 179, 168, 30, 11, 119, 49, + 68, 153, 231, 47, 7, 187, 25, 2, 240, 15, 60, 66, 0, 174, 87, 224, 74, 110, 255, 191, 225, + 136, 83, 232, 167, 96, 131, 17, 149, 1, 54, 9, 28, 95, 5, 199, 195, 228, 60, 94, 16, 13, + 182, 237, 98, 72, 189, 28, 54, 228, 7, 140, 218, 46, 42, 142, 94, 94, 84, 156, 150, 222, 107, + 151, 130, 221, 1, 246, 48, 4, 0, 151, 206, 15, 211, 53, 195, 139, 96, 207, 217, 125, 57, 156, + 112, 126, 0, 45, 171, 207, 183, 104, 17, 196, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, + 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 48, 227, + 11, 152, 31, 192, 252, 128, 36, 230, 7, 112, 225, 194, 133, 11, 23, 46, 92, 98, 158, 163, 153, + 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, + 252, 128, 232, 111, 113, 49, 63, 128, 234, 193, 252, 0, 170, 15, 243, 3, 168, 110, 204, 15, 96, + 126, 64, 18, 243, 3, 228, 32, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 200, 15, + 230, 7, 20, 55, 126, 192, 121, 22, 4, 193, 241, 252, 128, 243, 193, 110, 119, 121, 210, 80, 78, + 223, 213, 130, 0, 56, 142, 31, 112, 81, 138, 219, 95, 1, 142, 109, 193, 70, 194, 207, 155, 21, + 33, 102, 190, 206, 120, 155, 5, 1, 176, 143, 31, 64, 194, 170, 155, 193, 26, 80, 142, 210, 241, + 96, 219, 149, 188, 162, 110, 37, 113, 243, 105, 114, 92, 21, 79, 237, 182, 106, 74, 180, 156, 31, + 64, 178, 151, 139, 73, 255, 243, 47, 104, 210, 239, 81, 6, 250, 181, 96, 59, 72, 107, 124, 50, + 140, 186, 20, 117, 199, 7, 44, 10, 128, 249, 252, 0, 82, 136, 221, 0, 149, 174, 2, 78, 188, + 2, 246, 17, 252, 156, 13, 182, 20, 126, 70, 249, 252, 65, 58, 195, 33, 194, 106, 35, 233, 45, + 253, 237, 144, 69, 1, 136, 159, 31, 208, 105, 230, 38, 81, 105, 216, 66, 81, 175, 111, 118, 23, + 168, 168, 15, 108, 0, 216, 56, 176, 133, 196, 11, 56, 172, 43, 171, 117, 231, 139, 38, 53, 242, + 88, 173, 234, 92, 181, 163, 86, 45, 138, 162, 230, 7, 180, 152, 184, 78, 124, 179, 252, 160, 152, + 185, 250, 168, 152, 188, 36, 87, 140, 155, 185, 83, 140, 24, 191, 81, 188, 248, 209, 76, 209, 160, + 103, 166, 72, 246, 165, 173, 130, 74, 111, 135, 202, 255, 161, 57, 155, 129, 48, 249, 196, 3, 97, + 116, 195, 242, 235, 39, 45, 10, 64, 116, 226, 233, 167, 62, 153, 245, 101, 147, 207, 151, 138, 70, + 189, 87, 137, 10, 222, 69, 226, 233, 86, 51, 21, 253, 176, 162, 30, 167, 51, 229, 242, 24, 74, + 228, 141, 51, 205, 187, 35, 58, 174, 254, 126, 206, 162, 0, 68, 39, 159, 135, 51, 60, 179, 101, + 247, 108, 56, 203, 224, 176, 59, 246, 228, 234, 58, 114, 88, 57, 96, 65, 57, 189, 252, 154, 212, + 21, 202, 216, 22, 128, 90, 239, 77, 27, 222, 244, 163, 121, 162, 105, 247, 197, 162, 170, 119, 174, + 146, 121, 190, 190, 39, 27, 90, 192, 120, 67, 57, 125, 152, 86, 16, 189, 96, 218, 29, 242, 57, + 23, 219, 214, 5, 112, 32, 168, 55, 102, 189, 104, 252, 89, 174, 162, 30, 87, 115, 143, 63, 226, + 93, 37, 146, 251, 207, 23, 245, 222, 156, 44, 90, 180, 29, 139, 121, 196, 87, 131, 29, 9, 235, + 164, 212, 255, 117, 40, 17, 186, 227, 5, 5, 228, 242, 68, 12, 130, 186, 252, 0, 156, 10, 112, + 59, 188, 158, 108, 54, 229, 219, 157, 162, 234, 136, 37, 226, 153, 143, 102, 188, 77, 240, 148, 231, + 193, 112, 70, 248, 28, 167, 63, 68, 230, 16, 13, 42, 234, 46, 162, 219, 109, 90, 165, 94, 109, + 231, 52, 248, 128, 202, 15, 208, 6, 0, 95, 163, 237, 241, 3, 165, 197, 207, 229, 224, 244, 109, + 112, 124, 4, 201, 15, 96, 152, 68, 189, 35, 102, 159, 151, 200, 50, 127, 26, 141, 11, 26, 110, + 128, 250, 218, 117, 137, 90, 8, 21, 141, 31, 48, 48, 79, 212, 25, 179, 46, 236, 82, 152, 22, + 71, 87, 130, 221, 130, 96, 52, 176, 71, 169, 181, 96, 80, 134, 130, 45, 85, 23, 71, 18, 61, + 34, 160, 153, 38, 111, 178, 109, 41, 172, 94, 12, 197, 202, 15, 48, 8, 202, 133, 68, 152, 186, + 14, 156, 197, 235, 130, 219, 224, 88, 7, 175, 252, 224, 152, 10, 77, 126, 11, 216, 89, 194, 239, + 96, 107, 185, 205, 182, 139, 33, 245, 114, 216, 76, 126, 64, 152, 160, 32, 67, 224, 47, 96, 151, + 209, 5, 83, 125, 100, 142, 129, 93, 99, 247, 229, 176, 93, 252, 0, 171, 238, 10, 49, 63, 128, + 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 204, 248, 2, 230, 7, 48, 63, 32, 137, 249, 1, 92, 184, 112, + 225, 194, 133, 11, 151, 152, 231, 104, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, + 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 32, 250, 91, 92, 204, 15, 160, 122, 48, 63, 128, + 234, 195, 252, 0, 170, 27, 243, 3, 152, 31, 144, 196, 252, 0, 57, 8, 204, 15, 96, 126, 0, + 243, 3, 152, 31, 192, 252, 0, 242, 131, 249, 1, 197, 130, 31, 64, 74, 147, 50, 22, 5, 193, + 121, 252, 0, 18, 73, 220, 160, 168, 71, 242, 237, 46, 176, 167, 92, 158, 52, 76, 219, 89, 222, + 228, 0, 216, 203, 15, 32, 85, 72, 89, 112, 174, 42, 28, 155, 144, 218, 12, 217, 1, 168, 46, + 79, 7, 91, 71, 57, 73, 81, 47, 116, 14, 222, 247, 111, 147, 3, 96, 29, 63, 128, 232, 15, + 23, 169, 217, 34, 233, 119, 116, 22, 29, 237, 12, 54, 16, 236, 59, 248, 125, 92, 138, 219, 63, + 203, 213, 42, 117, 131, 228, 172, 145, 53, 183, 98, 74, 140, 155, 31, 240, 13, 188, 86, 123, 204, + 114, 204, 63, 62, 2, 42, 121, 31, 216, 3, 224, 24, 230, 9, 125, 1, 156, 123, 31, 142, 83, + 40, 5, 239, 14, 120, 29, 33, 9, 123, 41, 3, 253, 185, 40, 51, 83, 171, 86, 195, 130, 0, + 68, 207, 15, 168, 157, 182, 90, 244, 157, 114, 64, 12, 206, 249, 77, 140, 154, 190, 79, 164, 229, + 236, 16, 19, 114, 182, 138, 215, 70, 44, 22, 245, 250, 231, 136, 166, 157, 198, 207, 135, 74, 206, + 2, 219, 7, 118, 86, 114, 224, 156, 44, 127, 211, 72, 233, 3, 145, 212, 165, 146, 112, 242, 9, + 11, 2, 16, 157, 112, 178, 94, 223, 236, 17, 77, 223, 153, 33, 90, 188, 62, 75, 209, 13, 215, + 246, 76, 13, 102, 158, 118, 121, 98, 16, 83, 199, 102, 101, 173, 88, 20, 69, 197, 15, 104, 208, + 51, 115, 114, 243, 238, 217, 194, 229, 203, 144, 100, 242, 178, 234, 179, 104, 234, 113, 131, 22, 16, + 73, 84, 109, 133, 114, 52, 58, 241, 116, 165, 161, 11, 250, 55, 30, 186, 74, 52, 120, 123, 147, + 120, 220, 187, 92, 84, 243, 204, 38, 126, 192, 120, 45, 253, 33, 16, 166, 25, 7, 162, 76, 208, + 94, 184, 249, 123, 210, 80, 104, 125, 137, 5, 1, 136, 78, 62, 143, 255, 52, 26, 243, 147, 168, + 243, 81, 174, 184, 209, 183, 71, 220, 238, 219, 38, 238, 245, 110, 20, 149, 218, 254, 40, 90, 190, + 57, 67, 52, 122, 227, 7, 145, 236, 73, 253, 153, 248, 32, 103, 11, 57, 225, 46, 72, 166, 174, + 4, 75, 254, 93, 51, 38, 232, 4, 41, 96, 33, 65, 34, 234, 0, 116, 110, 144, 186, 65, 212, + 31, 16, 186, 37, 254, 150, 14, 185, 162, 197, 200, 45, 162, 242, 224, 249, 162, 241, 235, 19, 187, + 128, 115, 31, 66, 101, 231, 128, 99, 152, 112, 253, 152, 44, 144, 86, 206, 98, 254, 153, 212, 6, + 198, 168, 249, 203, 175, 89, 69, 145, 41, 58, 63, 160, 144, 82, 108, 244, 54, 85, 19, 80, 157, + 230, 124, 148, 191, 222, 3, 134, 57, 197, 135, 128, 109, 208, 155, 218, 66, 2, 33, 113, 2, 180, + 8, 13, 178, 45, 86, 93, 25, 198, 205, 15, 120, 246, 187, 93, 170, 46, 64, 119, 25, 12, 142, + 254, 5, 12, 215, 7, 30, 176, 225, 96, 63, 130, 253, 30, 12, 128, 59, 108, 198, 121, 53, 24, + 203, 45, 10, 128, 53, 252, 0, 205, 146, 55, 9, 156, 44, 67, 217, 231, 81, 62, 255, 36, 88, + 50, 216, 155, 96, 99, 193, 86, 17, 76, 169, 128, 40, 81, 56, 40, 51, 44, 10, 64, 98, 248, + 1, 58, 65, 193, 165, 241, 53, 68, 151, 106, 14, 14, 247, 130, 35, 74, 230, 23, 17, 82, 235, + 119, 101, 1, 85, 16, 136, 113, 86, 221, 29, 178, 141, 31, 160, 19, 148, 43, 168, 117, 188, 10, + 54, 24, 108, 5, 65, 152, 112, 37, 57, 194, 2, 231, 157, 199, 15, 208, 4, 4, 57, 2, 15, + 210, 216, 209, 212, 138, 254, 239, 120, 126, 128, 220, 101, 172, 190, 33, 194, 252, 0, 230, 7, 48, + 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 80, 148, 15, 102, 126, 0, 243, 3, + 146, 152, 31, 192, 252, 0, 250, 2, 230, 7, 48, 63, 32, 137, 249, 1, 234, 151, 48, 63, 128, + 11, 23, 46, 92, 184, 112, 225, 18, 203, 28, 205, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 68, 127, 139, 139, 249, 1, 84, 15, + 230, 7, 80, 125, 152, 31, 64, 117, 99, 126, 0, 243, 3, 146, 152, 31, 32, 7, 129, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 64, 126, 48, 63, 160, 88, 240, 3, 176, 40, 234, 18, + 183, 223, 138, 32, 36, 142, 31, 160, 151, 55, 144, 224, 8, 151, 147, 116, 230, 114, 233, 181, 187, + 193, 170, 130, 211, 53, 225, 88, 5, 172, 2, 10, 38, 92, 173, 82, 95, 55, 83, 55, 96, 26, + 63, 96, 231, 145, 19, 226, 249, 172, 181, 162, 220, 200, 37, 65, 201, 140, 146, 109, 62, 63, 141, + 38, 42, 62, 30, 134, 202, 223, 73, 96, 132, 103, 224, 245, 255, 194, 177, 147, 203, 147, 214, 69, + 145, 210, 187, 253, 152, 173, 246, 91, 176, 249, 240, 190, 149, 168, 59, 132, 227, 28, 18, 83, 253, + 74, 114, 58, 212, 31, 174, 55, 185, 5, 68, 47, 153, 105, 218, 105, 252, 141, 21, 70, 44, 18, + 47, 167, 110, 23, 175, 125, 151, 39, 62, 206, 216, 35, 190, 207, 222, 46, 166, 206, 253, 69, 12, + 153, 178, 65, 52, 251, 106, 158, 168, 245, 222, 180, 109, 80, 201, 143, 81, 252, 4, 150, 65, 71, + 76, 194, 62, 23, 108, 15, 137, 159, 114, 81, 70, 79, 10, 243, 63, 100, 129, 100, 48, 53, 175, + 219, 80, 83, 60, 215, 138, 41, 49, 42, 209, 84, 227, 174, 19, 187, 52, 234, 153, 45, 92, 237, + 39, 42, 138, 241, 134, 158, 73, 138, 122, 188, 81, 171, 9, 34, 197, 151, 46, 203, 219, 206, 144, + 99, 231, 162, 209, 4, 23, 49, 35, 181, 21, 194, 169, 232, 248, 1, 117, 223, 154, 220, 183, 241, + 219, 211, 132, 171, 221, 68, 145, 28, 65, 0, 13, 205, 183, 64, 35, 92, 20, 231, 221, 17, 133, + 212, 189, 44, 8, 64, 212, 194, 201, 160, 116, 182, 172, 111, 179, 40, 239, 93, 172, 48, 4, 180, + 252, 0, 146, 190, 70, 206, 41, 94, 68, 163, 96, 62, 103, 197, 162, 40, 42, 126, 128, 44, 158, + 190, 222, 183, 95, 220, 236, 219, 165, 4, 162, 92, 155, 31, 69, 114, 207, 169, 162, 121, 251, 12, + 145, 220, 42, 245, 180, 70, 238, 26, 142, 17, 16, 40, 170, 132, 222, 108, 138, 12, 5, 32, 106, + 241, 116, 190, 124, 254, 227, 208, 13, 209, 119, 118, 202, 19, 205, 71, 110, 18, 85, 7, 205, 19, + 45, 218, 141, 253, 0, 42, 57, 91, 74, 190, 30, 208, 61, 235, 249, 163, 122, 160, 8, 236, 16, + 213, 254, 102, 65, 0, 138, 206, 15, 80, 55, 67, 163, 221, 213, 41, 87, 52, 31, 179, 85, 29, + 45, 113, 218, 251, 43, 88, 53, 236, 175, 96, 153, 4, 79, 57, 97, 208, 191, 3, 58, 57, 198, + 3, 58, 77, 31, 237, 55, 139, 86, 132, 38, 240, 3, 198, 252, 172, 6, 224, 222, 224, 202, 205, + 227, 191, 22, 172, 34, 114, 129, 20, 84, 14, 206, 231, 250, 221, 35, 100, 0, 212, 140, 33, 242, + 123, 127, 180, 40, 0, 230, 243, 3, 116, 86, 129, 231, 145, 106, 252, 191, 224, 232, 40, 56, 110, + 70, 34, 68, 4, 106, 140, 54, 88, 105, 86, 93, 25, 90, 206, 15, 144, 2, 129, 96, 165, 91, + 193, 106, 131, 245, 14, 233, 34, 30, 205, 180, 233, 46, 52, 54, 188, 103, 81, 0, 172, 231, 7, + 24, 4, 3, 153, 97, 247, 131, 211, 47, 210, 50, 248, 80, 132, 217, 224, 85, 139, 2, 96, 15, + 63, 64, 39, 32, 151, 224, 5, 15, 216, 231, 96, 139, 9, 198, 114, 78, 26, 12, 159, 177, 234, + 238, 144, 147, 248, 1, 151, 194, 0, 120, 7, 28, 235, 17, 93, 98, 6, 97, 183, 112, 44, 184, + 205, 2, 231, 157, 203, 15, 160, 203, 231, 107, 160, 239, 215, 129, 99, 127, 171, 250, 127, 177, 224, + 7, 224, 44, 98, 81, 0, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, + 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 102, 124, 1, 243, 3, + 152, 31, 144, 196, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 204, 115, 52, 243, 3, 152, 31, + 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 16, 253, + 45, 46, 230, 7, 80, 61, 152, 31, 64, 245, 97, 126, 0, 213, 141, 249, 1, 204, 15, 72, 98, + 126, 128, 28, 4, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 249, 193, 252, 128, 152, + 248, 1, 181, 199, 234, 243, 3, 38, 108, 57, 80, 136, 31, 144, 226, 241, 151, 145, 191, 148, 52, + 0, 231, 105, 94, 43, 163, 173, 156, 206, 255, 59, 47, 17, 233, 246, 194, 242, 3, 48, 27, 100, + 205, 247, 114, 166, 52, 251, 108, 169, 120, 118, 192, 47, 162, 253, 208, 189, 226, 243, 140, 237, 98, + 242, 156, 237, 98, 57, 52, 163, 206, 147, 214, 138, 167, 190, 158, 47, 234, 188, 61, 101, 16, 188, + 55, 5, 19, 165, 131, 61, 226, 242, 164, 213, 78, 113, 251, 159, 195, 28, 163, 240, 51, 178, 0, + 30, 7, 107, 70, 25, 105, 155, 131, 85, 86, 88, 1, 30, 229, 61, 168, 31, 106, 168, 240, 2, + 220, 202, 241, 63, 244, 90, 61, 226, 8, 224, 231, 86, 48, 49, 0, 209, 11, 38, 48, 171, 124, + 189, 62, 217, 121, 174, 206, 153, 34, 217, 51, 86, 17, 80, 55, 242, 252, 160, 168, 200, 93, 109, + 198, 43, 42, 175, 150, 109, 210, 81, 71, 188, 21, 28, 221, 34, 169, 71, 247, 193, 223, 54, 145, + 100, 94, 205, 65, 188, 19, 94, 219, 66, 82, 250, 179, 240, 126, 84, 141, 161, 244, 254, 23, 85, + 65, 70, 34, 170, 95, 72, 118, 255, 7, 41, 199, 246, 187, 90, 165, 254, 203, 196, 0, 68, 47, + 153, 105, 220, 117, 226, 53, 85, 190, 152, 43, 82, 222, 94, 32, 26, 122, 51, 195, 10, 161, 181, + 175, 23, 146, 195, 185, 11, 114, 12, 27, 42, 199, 141, 149, 166, 21, 204, 158, 18, 163, 18, 77, + 201, 178, 185, 155, 124, 187, 197, 131, 222, 53, 226, 41, 239, 44, 209, 216, 51, 81, 184, 188, 105, + 198, 9, 216, 221, 209, 73, 228, 13, 181, 196, 161, 1, 61, 171, 55, 102, 196, 25, 128, 232, 248, + 1, 90, 225, 228, 13, 190, 125, 226, 31, 190, 95, 196, 35, 222, 149, 34, 229, 141, 169, 34, 217, + 151, 22, 57, 3, 125, 248, 204, 244, 1, 35, 231, 165, 0, 44, 180, 96, 32, 44, 58, 63, 160, + 144, 72, 98, 228, 207, 162, 42, 116, 143, 102, 29, 199, 161, 70, 120, 37, 245, 227, 112, 234, 112, + 17, 131, 116, 30, 109, 160, 21, 139, 162, 34, 243, 3, 140, 196, 211, 53, 62, 152, 142, 170, 241, + 70, 96, 131, 224, 172, 253, 92, 20, 199, 229, 113, 35, 12, 100, 193, 10, 128, 130, 9, 252, 0, + 73, 62, 79, 51, 6, 170, 64, 31, 2, 123, 3, 108, 38, 41, 199, 11, 247, 121, 55, 201, 231, + 181, 168, 13, 183, 190, 148, 30, 236, 78, 11, 2, 96, 46, 63, 64, 179, 128, 249, 11, 101, 146, + 198, 64, 204, 147, 166, 184, 136, 77, 95, 233, 251, 200, 34, 41, 8, 194, 62, 139, 86, 132, 230, + 243, 3, 116, 86, 118, 184, 146, 187, 141, 22, 63, 233, 202, 58, 32, 159, 54, 83, 208, 53, 220, + 97, 103, 5, 252, 121, 162, 69, 1, 176, 158, 31, 160, 9, 198, 237, 180, 234, 27, 69, 139, 163, + 104, 73, 51, 61, 172, 186, 50, 76, 24, 63, 64, 190, 38, 128, 51, 124, 13, 52, 241, 186, 164, + 22, 95, 167, 81, 138, 235, 5, 165, 166, 69, 1, 176, 135, 31, 32, 5, 227, 10, 98, 142, 244, + 7, 91, 42, 209, 165, 130, 179, 1, 29, 255, 207, 162, 0, 56, 134, 31, 128, 64, 133, 114, 96, + 61, 145, 41, 134, 215, 18, 210, 116, 184, 214, 202, 187, 67, 142, 225, 7, 72, 193, 40, 11, 230, + 129, 174, 48, 145, 248, 1, 195, 44, 114, 222, 209, 252, 128, 139, 193, 238, 1, 123, 25, 90, 66, + 69, 171, 250, 127, 113, 225, 7, 148, 177, 40, 0, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, + 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, + 51, 190, 128, 249, 1, 204, 15, 72, 98, 126, 0, 23, 46, 92, 184, 112, 225, 194, 37, 230, 57, + 154, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, + 1, 204, 15, 136, 254, 22, 23, 243, 3, 168, 30, 204, 15, 160, 250, 48, 63, 128, 234, 198, 252, + 0, 230, 7, 36, 49, 63, 64, 14, 2, 243, 3, 152, 31, 192, 252, 0, 230, 7, 196, 190, 81, + 18, 51, 70, 226, 130, 225, 63, 217, 27, 197, 143, 251, 143, 20, 138, 94, 137, 231, 7, 52, 111, + 159, 209, 180, 118, 191, 169, 34, 229, 221, 197, 162, 126, 239, 141, 162, 219, 240, 29, 98, 204, 212, + 109, 98, 229, 218, 189, 98, 237, 142, 131, 226, 197, 31, 86, 139, 202, 67, 22, 136, 154, 239, 231, + 52, 163, 128, 161, 88, 234, 255, 80, 61, 38, 5, 241, 60, 135, 4, 160, 232, 155, 165, 235, 247, + 202, 250, 162, 41, 230, 31, 111, 59, 65, 81, 143, 183, 244, 164, 43, 234, 241, 230, 109, 50, 197, + 115, 109, 51, 68, 203, 78, 227, 69, 227, 46, 19, 243, 192, 201, 177, 46, 79, 26, 234, 5, 71, + 166, 184, 149, 196, 137, 93, 225, 216, 23, 142, 237, 72, 86, 143, 18, 153, 198, 240, 90, 117, 120, + 31, 166, 233, 189, 214, 213, 42, 245, 94, 176, 91, 164, 64, 97, 224, 46, 146, 126, 47, 99, 65, + 16, 162, 231, 7, 208, 127, 248, 188, 233, 208, 85, 162, 97, 247, 101, 162, 129, 59, 51, 146, 246, + 239, 44, 56, 40, 39, 96, 255, 149, 36, 245, 170, 52, 126, 53, 252, 125, 45, 4, 0, 83, 107, + 46, 131, 159, 211, 40, 91, 125, 22, 216, 80, 210, 26, 118, 2, 235, 14, 129, 121, 133, 248, 1, + 85, 137, 37, 112, 185, 73, 1, 40, 178, 96, 66, 145, 204, 60, 214, 243, 128, 34, 156, 46, 231, + 93, 34, 234, 182, 202, 82, 120, 2, 90, 181, 151, 129, 242, 75, 86, 135, 6, 244, 84, 227, 174, + 252, 244, 221, 234, 239, 127, 194, 251, 14, 192, 241, 48, 189, 23, 89, 2, 11, 204, 234, 70, 69, + 146, 204, 232, 137, 166, 80, 70, 127, 159, 111, 189, 104, 222, 101, 102, 36, 241, 115, 32, 37, 86, + 101, 121, 225, 192, 141, 55, 123, 74, 44, 50, 63, 160, 208, 22, 249, 193, 123, 69, 237, 65, 139, + 68, 227, 55, 38, 174, 135, 74, 110, 37, 157, 127, 100, 49, 164, 91, 55, 201, 114, 32, 130, 154, + 252, 85, 147, 3, 16, 27, 63, 160, 144, 112, 114, 228, 114, 241, 244, 128, 153, 125, 149, 1, 47, + 63, 153, 242, 185, 40, 212, 224, 133, 178, 78, 71, 124, 175, 199, 127, 147, 201, 1, 48, 129, 31, + 160, 145, 206, 66, 63, 70, 245, 184, 15, 156, 193, 180, 185, 191, 235, 158, 105, 119, 76, 93, 96, + 145, 21, 139, 34, 211, 248, 1, 82, 242, 245, 252, 148, 185, 30, 127, 45, 100, 9, 40, 26, 225, + 24, 28, 214, 105, 13, 189, 45, 8, 128, 249, 252, 0, 109, 33, 84, 206, 87, 97, 196, 210, 225, + 29, 47, 8, 222, 19, 22, 4, 192, 58, 126, 128, 188, 250, 3, 187, 25, 236, 89, 232, 30, 99, + 73, 13, 122, 14, 167, 74, 205, 116, 25, 208, 58, 173, 4, 35, 255, 231, 237, 22, 173, 8, 173, + 231, 7, 104, 130, 241, 15, 176, 182, 148, 95, 60, 234, 41, 18, 2, 49, 200, 162, 0, 36, 150, + 31, 64, 65, 56, 95, 9, 132, 91, 89, 26, 207, 4, 231, 14, 69, 17, 132, 70, 86, 93, 25, + 38, 156, 31, 32, 5, 226, 74, 26, 40, 135, 210, 76, 17, 8, 153, 10, 11, 250, 254, 41, 176, + 11, 44, 10, 128, 189, 252, 0, 181, 128, 211, 247, 129, 147, 31, 163, 84, 158, 28, 150, 207, 254, + 4, 11, 175, 10, 29, 195, 15, 184, 0, 206, 248, 221, 112, 108, 133, 243, 189, 38, 8, 173, 172, + 188, 59, 228, 68, 126, 192, 121, 68, 142, 155, 72, 87, 145, 183, 88, 228, 188, 51, 249, 1, 180, + 144, 186, 28, 172, 34, 216, 75, 22, 158, 253, 226, 193, 15, 176, 48, 0, 204, 15, 96, 126, 0, + 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, + 152, 31, 192, 252, 0, 51, 190, 128, 249, 1, 204, 15, 72, 98, 126, 0, 23, 46, 92, 184, 112, + 225, 194, 37, 230, 57, 154, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, + 7, 48, 63, 128, 249, 1, 204, 15, 136, 254, 22, 23, 243, 3, 168, 30, 204, 15, 160, 250, 48, + 63, 128, 234, 198, 252, 0, 230, 7, 36, 49, 63, 64, 14, 2, 243, 3, 152, 31, 16, 47, 63, + 0, 165, 47, 229, 191, 93, 188, 163, 221, 180, 173, 98, 51, 244, 27, 237, 148, 87, 226, 249, 1, + 13, 123, 76, 26, 218, 184, 103, 150, 72, 238, 57, 87, 52, 236, 185, 86, 188, 159, 186, 93, 44, + 89, 185, 91, 236, 218, 115, 88, 108, 255, 237, 132, 120, 246, 135, 213, 162, 220, 247, 75, 130, 27, + 37, 113, 227, 179, 3, 3, 16, 51, 63, 224, 178, 154, 255, 203, 89, 150, 252, 6, 4, 192, 59, + 86, 73, 180, 220, 220, 147, 33, 26, 250, 178, 197, 139, 157, 179, 197, 43, 189, 179, 69, 131, 126, + 83, 49, 1, 251, 10, 120, 111, 123, 176, 250, 164, 10, 185, 35, 197, 237, 191, 85, 13, 136, 19, + 36, 244, 49, 241, 3, 212, 205, 210, 41, 67, 183, 137, 170, 237, 150, 41, 201, 215, 101, 117, 151, + 162, 2, 43, 200, 20, 189, 139, 146, 171, 163, 58, 124, 18, 216, 16, 176, 193, 240, 158, 206, 164, + 4, 71, 9, 125, 121, 74, 180, 120, 190, 204, 24, 72, 80, 0, 138, 190, 89, 90, 222, 46, 127, + 163, 111, 143, 184, 199, 247, 147, 168, 238, 157, 45, 154, 120, 38, 4, 211, 110, 187, 244, 51, 200, + 170, 63, 31, 83, 179, 205, 194, 107, 40, 141, 65, 101, 233, 114, 176, 17, 196, 22, 248, 175, 34, + 148, 112, 251, 239, 165, 220, 197, 183, 200, 82, 121, 179, 187, 83, 76, 252, 0, 173, 96, 2, 101, + 244, 149, 188, 11, 69, 138, 55, 205, 56, 225, 186, 129, 144, 154, 116, 131, 231, 20, 153, 124, 1, + 99, 96, 19, 29, 151, 146, 216, 242, 67, 48, 212, 36, 55, 81, 186, 82, 126, 30, 227, 219, 76, + 10, 64, 108, 252, 128, 66, 146, 153, 158, 185, 162, 201, 208, 85, 162, 126, 159, 44, 108, 1, 121, + 49, 38, 95, 87, 91, 138, 86, 60, 121, 142, 186, 17, 38, 99, 205, 131, 215, 114, 224, 248, 47, + 147, 2, 16, 31, 63, 32, 68, 52, 53, 106, 141, 168, 58, 104, 238, 26, 168, 220, 59, 224, 200, + 6, 56, 158, 142, 50, 159, 112, 65, 139, 208, 87, 142, 235, 165, 224, 46, 111, 230, 148, 24, 63, + 63, 96, 232, 254, 16, 217, 28, 105, 1, 39, 132, 147, 201, 199, 161, 32, 95, 106, 242, 56, 96, + 18, 63, 64, 18, 78, 146, 234, 235, 38, 176, 214, 148, 115, 252, 184, 70, 66, 95, 120, 144, 116, + 71, 37, 159, 197, 255, 219, 206, 228, 0, 152, 207, 15, 144, 70, 109, 36, 196, 36, 131, 205, 82, + 207, 120, 136, 88, 218, 109, 56, 96, 6, 194, 72, 232, 175, 54, 123, 81, 100, 58, 63, 64, 103, + 249, 92, 6, 243, 8, 131, 45, 145, 154, 126, 64, 146, 203, 70, 215, 29, 220, 254, 145, 22, 172, + 7, 18, 194, 15, 64, 170, 196, 223, 136, 29, 132, 168, 156, 125, 210, 212, 167, 183, 102, 8, 24, + 4, 164, 142, 5, 1, 176, 158, 31, 160, 9, 198, 173, 152, 73, 30, 28, 220, 27, 118, 128, 44, + 108, 155, 45, 90, 17, 38, 150, 31, 64, 65, 184, 8, 236, 65, 176, 47, 225, 140, 255, 98, 16, + 8, 203, 241, 25, 69, 237, 2, 166, 241, 3, 164, 153, 226, 33, 176, 55, 193, 233, 61, 58, 11, + 34, 109, 64, 254, 97, 213, 149, 161, 109, 252, 0, 41, 24, 40, 157, 31, 6, 246, 155, 78, 23, + 192, 223, 51, 45, 188, 40, 178, 159, 31, 64, 179, 68, 89, 186, 132, 158, 143, 23, 77, 154, 233, + 239, 89, 11, 3, 224, 12, 126, 0, 5, 2, 47, 139, 235, 210, 42, 82, 37, 73, 228, 89, 125, + 119, 200, 137, 252, 128, 43, 192, 158, 7, 155, 14, 246, 158, 133, 206, 59, 147, 31, 32, 173, 34, + 27, 131, 221, 104, 101, 255, 103, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, + 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 128, 120, 190, 128, 249, 1, 204, 15, + 72, 98, 126, 128, 220, 18, 152, 31, 192, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 12, 115, + 52, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, + 3, 152, 31, 16, 253, 45, 46, 230, 7, 80, 61, 152, 31, 64, 245, 97, 126, 0, 213, 141, 249, + 1, 204, 15, 72, 98, 126, 128, 28, 4, 115, 248, 1, 45, 219, 164, 63, 89, 121, 240, 252, 95, + 219, 78, 205, 87, 144, 151, 42, 126, 64, 138, 199, 127, 113, 131, 158, 153, 147, 155, 244, 200, 18, + 45, 187, 206, 20, 45, 223, 92, 35, 190, 155, 178, 91, 252, 132, 81, 133, 254, 149, 123, 252, 164, + 104, 63, 99, 83, 137, 230, 7, 156, 95, 253, 211, 217, 7, 92, 221, 166, 40, 185, 199, 113, 59, + 123, 99, 247, 68, 145, 210, 57, 71, 116, 233, 63, 83, 244, 254, 102, 177, 120, 102, 240, 124, 81, + 237, 243, 57, 235, 212, 36, 105, 86, 228, 13, 143, 51, 8, 177, 111, 147, 11, 110, 148, 252, 122, + 159, 168, 216, 122, 169, 104, 234, 25, 31, 76, 181, 157, 220, 42, 127, 127, 127, 178, 39, 85, 64, + 55, 89, 77, 170, 241, 47, 48, 91, 100, 138, 91, 209, 252, 62, 64, 1, 177, 85, 66, 31, 215, + 70, 73, 121, 171, 44, 166, 222, 126, 208, 187, 70, 212, 246, 76, 13, 182, 6, 29, 201, 203, 73, + 82, 129, 173, 130, 227, 56, 176, 239, 41, 167, 96, 89, 120, 237, 81, 85, 7, 152, 232, 128, 196, + 197, 15, 144, 55, 75, 223, 224, 219, 39, 238, 247, 173, 19, 141, 91, 103, 7, 69, 145, 46, 61, + 189, 112, 129, 114, 244, 4, 29, 115, 169, 133, 140, 65, 117, 56, 4, 173, 2, 182, 16, 89, 27, + 100, 37, 125, 34, 110, 126, 64, 161, 237, 242, 159, 237, 21, 141, 222, 201, 17, 201, 173, 211, 206, + 162, 179, 6, 194, 39, 61, 97, 244, 25, 69, 90, 155, 223, 114, 230, 192, 241, 27, 176, 129, 96, + 77, 21, 126, 128, 219, 127, 15, 170, 204, 172, 8, 136, 41, 252, 128, 160, 96, 98, 228, 54, 81, + 117, 216, 34, 1, 51, 132, 31, 28, 89, 174, 168, 199, 221, 133, 20, 224, 145, 164, 177, 170, 96, + 242, 136, 170, 21, 4, 27, 9, 239, 65, 121, 93, 27, 248, 249, 78, 178, 235, 77, 10, 128, 73, + 252, 0, 73, 50, 83, 227, 253, 233, 152, 96, 249, 101, 176, 157, 133, 84, 161, 161, 73, 150, 11, + 126, 119, 135, 145, 217, 187, 149, 32, 161, 248, 250, 48, 177, 4, 48, 13, 231, 39, 102, 4, 193, + 92, 126, 128, 70, 52, 133, 208, 20, 100, 134, 128, 29, 13, 155, 81, 222, 29, 150, 41, 96, 196, + 21, 152, 108, 230, 148, 104, 58, 63, 128, 250, 235, 5, 96, 143, 64, 229, 223, 131, 227, 79, 202, + 217, 115, 27, 182, 8, 189, 92, 195, 129, 66, 1, 42, 248, 91, 93, 19, 3, 96, 62, 63, 32, + 100, 225, 228, 246, 95, 13, 21, 238, 8, 118, 82, 231, 140, 7, 34, 81, 37, 116, 198, 139, 57, + 38, 15, 132, 214, 241, 3, 164, 214, 112, 30, 156, 109, 212, 10, 163, 114, 252, 15, 109, 147, 214, + 142, 13, 17, 172, 185, 217, 139, 34, 203, 249, 1, 20, 132, 75, 136, 29, 52, 28, 108, 79, 136, + 124, 222, 45, 49, 5, 244, 90, 66, 193, 223, 150, 89, 176, 30, 176, 158, 31, 160, 9, 196, 117, + 40, 152, 134, 86, 112, 38, 194, 153, 214, 11, 198, 11, 22, 4, 32, 177, 252, 0, 181, 53, 64, + 179, 175, 1, 199, 44, 8, 196, 233, 176, 179, 68, 129, 173, 183, 104, 69, 152, 120, 126, 0, 5, + 1, 229, 177, 13, 105, 57, 124, 88, 62, 243, 242, 236, 32, 29, 91, 89, 20, 0, 123, 248, 1, + 106, 1, 103, 111, 161, 133, 141, 238, 204, 64, 173, 99, 155, 149, 87, 134, 78, 224, 7, 96, 158, + 241, 231, 112, 138, 35, 180, 150, 182, 249, 119, 180, 48, 0, 246, 243, 3, 40, 8, 215, 40, 138, + 113, 183, 127, 145, 116, 197, 136, 118, 192, 202, 27, 42, 142, 226, 7, 208, 194, 233, 46, 162, 73, + 168, 1, 232, 97, 245, 221, 33, 71, 241, 3, 136, 38, 113, 35, 209, 36, 230, 200, 56, 61, 11, + 156, 119, 52, 63, 224, 239, 8, 89, 177, 248, 236, 51, 63, 128, 249, 1, 204, 15, 96, 126, 0, + 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, + 226, 253, 2, 230, 7, 48, 63, 32, 137, 249, 1, 234, 151, 48, 63, 128, 11, 23, 46, 92, 184, + 112, 225, 18, 203, 28, 205, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, + 3, 152, 31, 192, 252, 0, 230, 7, 68, 127, 139, 139, 249, 1, 84, 15, 230, 7, 80, 125, 152, + 31, 64, 117, 99, 126, 128, 101, 252, 128, 20, 143, 255, 137, 234, 159, 206, 222, 144, 50, 113, 67, + 233, 227, 7, 128, 243, 23, 54, 232, 153, 57, 177, 81, 207, 44, 225, 122, 61, 71, 252, 187, 255, + 58, 49, 99, 217, 1, 177, 119, 255, 239, 165, 131, 31, 128, 87, 88, 149, 191, 158, 127, 208, 213, + 123, 186, 146, 131, 28, 37, 45, 245, 61, 217, 162, 245, 59, 243, 69, 234, 196, 53, 34, 103, 205, + 30, 81, 23, 230, 230, 242, 223, 45, 41, 153, 91, 100, 228, 77, 82, 21, 124, 75, 66, 133, 211, + 190, 12, 69, 4, 213, 160, 87, 166, 168, 215, 39, 27, 83, 107, 187, 48, 175, 56, 37, 89, 44, + 227, 128, 32, 196, 199, 15, 160, 15, 81, 182, 201, 149, 235, 155, 43, 110, 241, 237, 20, 143, 121, + 127, 20, 13, 220, 153, 122, 234, 113, 220, 246, 190, 1, 2, 178, 17, 142, 61, 225, 245, 154, 112, + 124, 156, 186, 145, 157, 65, 48, 129, 31, 32, 109, 148, 188, 217, 7, 35, 189, 119, 169, 104, 238, + 27, 31, 84, 143, 187, 66, 19, 169, 158, 35, 189, 224, 54, 69, 39, 212, 42, 117, 56, 252, 94, + 15, 149, 226, 86, 38, 83, 139, 48, 30, 196, 207, 15, 40, 180, 85, 246, 131, 85, 162, 69, 135, + 140, 80, 231, 245, 53, 129, 199, 73, 10, 55, 27, 142, 31, 82, 230, 249, 127, 144, 114, 228, 146, + 4, 5, 193, 28, 126, 64, 193, 102, 233, 60, 209, 100, 228, 6, 68, 103, 136, 22, 173, 211, 150, + 232, 165, 213, 45, 164, 14, 43, 144, 194, 238, 130, 159, 49, 193, 250, 32, 146, 206, 96, 234, 238, + 11, 172, 236, 42, 230, 243, 3, 104, 187, 124, 185, 239, 150, 204, 68, 233, 11, 156, 249, 31, 225, + 120, 42, 66, 16, 2, 58, 63, 31, 134, 247, 44, 130, 255, 63, 157, 72, 19, 15, 131, 221, 99, + 81, 16, 76, 228, 7, 104, 4, 19, 152, 33, 158, 20, 31, 193, 174, 96, 164, 15, 212, 123, 29, + 94, 59, 171, 234, 5, 193, 190, 131, 223, 251, 195, 177, 18, 34, 55, 204, 74, 191, 109, 9, 63, + 64, 189, 164, 37, 245, 248, 125, 208, 180, 81, 255, 243, 187, 174, 18, 212, 29, 54, 209, 186, 246, + 53, 236, 34, 71, 137, 31, 208, 217, 12, 5, 185, 165, 252, 0, 181, 255, 130, 85, 7, 71, 199, + 129, 29, 50, 76, 171, 237, 214, 85, 139, 107, 37, 245, 170, 160, 50, 64, 227, 75, 77, 179, 166, + 68, 75, 248, 1, 154, 64, 160, 56, 242, 219, 34, 40, 196, 3, 17, 254, 62, 197, 196, 113, 192, + 90, 126, 128, 218, 18, 224, 236, 97, 18, 213, 158, 136, 214, 40, 160, 205, 164, 69, 28, 11, 130, + 83, 105, 104, 171, 41, 111, 98, 0, 172, 231, 7, 72, 129, 248, 39, 166, 209, 5, 251, 89, 154, + 38, 3, 81, 181, 138, 130, 0, 124, 105, 246, 162, 40, 33, 252, 0, 41, 8, 151, 80, 75, 56, + 29, 77, 243, 15, 182, 150, 86, 169, 1, 133, 71, 210, 42, 245, 90, 147, 3, 144, 88, 126, 128, + 20, 132, 150, 96, 63, 70, 41, 157, 87, 91, 64, 39, 11, 214, 2, 137, 231, 7, 80, 16, 174, + 7, 123, 21, 28, 91, 40, 45, 152, 2, 97, 90, 195, 106, 139, 22, 67, 246, 240, 3, 164, 64, + 60, 7, 205, 250, 96, 20, 235, 128, 166, 22, 5, 192, 94, 126, 128, 52, 56, 78, 10, 179, 82, + 28, 111, 229, 149, 161, 19, 248, 1, 151, 65, 87, 168, 13, 199, 116, 176, 223, 116, 90, 192, 67, + 22, 6, 192, 25, 252, 0, 10, 68, 85, 8, 196, 44, 66, 102, 169, 206, 127, 100, 241, 101, 177, + 195, 248, 1, 249, 247, 4, 62, 167, 32, 228, 194, 248, 112, 185, 197, 1, 112, 36, 63, 0, 161, + 75, 239, 131, 165, 88, 236, 188, 163, 249, 1, 55, 37, 224, 174, 16, 243, 3, 152, 31, 192, 252, + 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, + 7, 48, 63, 32, 222, 47, 96, 126, 0, 243, 3, 146, 152, 31, 160, 126, 9, 243, 3, 184, 112, + 225, 194, 133, 11, 23, 46, 177, 204, 209, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, + 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 64, 244, 183, 184, 152, 31, 64, 245, 96, 126, + 0, 213, 39, 49, 252, 0, 220, 240, 92, 251, 221, 169, 169, 21, 190, 93, 92, 58, 249, 1, 13, + 187, 79, 122, 179, 94, 239, 44, 145, 210, 117, 178, 120, 229, 163, 245, 98, 227, 182, 35, 226, 240, + 145, 19, 165, 131, 31, 128, 165, 252, 55, 139, 151, 54, 127, 119, 182, 72, 246, 101, 136, 100, 79, + 154, 104, 216, 54, 71, 244, 31, 186, 82, 44, 254, 113, 167, 210, 228, 134, 192, 136, 92, 34, 249, + 1, 234, 218, 30, 55, 72, 36, 15, 222, 47, 42, 181, 93, 30, 148, 203, 182, 240, 140, 21, 255, + 238, 148, 41, 94, 123, 111, 186, 168, 254, 217, 108, 81, 249, 235, 249, 103, 43, 124, 179, 168, 102, + 146, 205, 197, 116, 126, 128, 188, 69, 6, 197, 211, 143, 123, 151, 137, 198, 158, 137, 133, 182, 186, + 55, 111, 151, 129, 73, 152, 63, 82, 228, 115, 249, 74, 145, 139, 109, 12, 130, 249, 252, 128, 155, + 218, 238, 87, 30, 133, 223, 238, 219, 38, 170, 122, 231, 138, 22, 222, 113, 218, 220, 227, 138, 232, + 209, 229, 73, 251, 141, 242, 141, 98, 146, 197, 39, 105, 16, 181, 35, 8, 230, 242, 3, 66, 246, + 6, 249, 246, 8, 87, 143, 217, 193, 0, 200, 123, 254, 225, 103, 85, 254, 178, 23, 37, 244, 96, + 31, 82, 26, 237, 107, 109, 8, 130, 185, 252, 128, 224, 70, 201, 158, 185, 162, 225, 119, 107, 69, + 157, 183, 167, 96, 16, 206, 68, 144, 202, 162, 12, 246, 56, 4, 102, 0, 28, 95, 66, 213, 8, + 181, 138, 243, 18, 52, 30, 88, 192, 15, 160, 173, 178, 213, 63, 157, 157, 3, 142, 124, 175, 52, + 253, 232, 114, 10, 99, 146, 245, 197, 96, 189, 40, 227, 252, 45, 9, 8, 130, 5, 252, 0, 105, + 179, 52, 169, 199, 23, 66, 0, 206, 26, 74, 98, 221, 5, 93, 133, 82, 109, 158, 37, 181, 248, + 135, 138, 162, 204, 227, 191, 202, 194, 0, 88, 195, 15, 144, 183, 203, 83, 206, 192, 9, 134, 114, + 88, 79, 225, 188, 226, 210, 184, 177, 21, 108, 29, 152, 135, 130, 121, 131, 5, 65, 176, 142, 31, + 64, 1, 184, 24, 167, 63, 112, 42, 67, 209, 6, 186, 141, 85, 162, 122, 93, 69, 82, 155, 207, + 133, 227, 215, 74, 78, 210, 252, 207, 252, 139, 73, 1, 176, 142, 31, 160, 185, 102, 104, 1, 182, + 70, 129, 169, 132, 215, 5, 23, 158, 61, 242, 37, 179, 249, 220, 17, 143, 63, 15, 254, 54, 131, + 6, 205, 167, 227, 197, 242, 88, 206, 15, 208, 4, 225, 105, 26, 236, 98, 149, 208, 203, 124, 129, + 115, 148, 200, 253, 18, 51, 166, 68, 203, 249, 1, 20, 128, 139, 40, 249, 250, 156, 40, 103, 134, + 66, 1, 208, 0, 21, 146, 77, 234, 6, 214, 243, 3, 164, 32, 32, 255, 195, 11, 182, 10, 156, + 56, 87, 132, 214, 160, 56, 45, 117, 141, 175, 76, 28, 8, 19, 199, 15, 144, 2, 209, 26, 156, + 57, 17, 238, 236, 135, 81, 149, 111, 54, 243, 122, 34, 225, 252, 128, 96, 119, 112, 43, 188, 160, + 159, 117, 154, 125, 32, 2, 88, 161, 158, 201, 83, 97, 226, 249, 1, 20, 132, 155, 145, 7, 64, + 11, 159, 104, 73, 50, 31, 90, 176, 22, 176, 135, 31, 64, 65, 184, 142, 86, 125, 39, 162, 24, + 7, 86, 88, 180, 34, 180, 151, 31, 64, 129, 232, 19, 5, 60, 165, 170, 69, 1, 176, 151, 31, + 64, 172, 161, 219, 192, 250, 130, 237, 49, 24, 15, 250, 90, 121, 101, 104, 59, 63, 128, 2, 241, + 48, 156, 237, 145, 42, 63, 64, 2, 39, 205, 181, 248, 170, 208, 81, 252, 0, 228, 138, 140, 209, + 12, 128, 143, 88, 28, 0, 231, 240, 3, 240, 102, 8, 94, 245, 17, 86, 239, 156, 21, 212, 152, + 152, 151, 194, 241, 92, 12, 197, 16, 4, 188, 129, 218, 62, 1, 206, 59, 154, 31, 112, 81, 2, + 2, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, + 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 136, 247, 11, 152, 31, 192, 252, 128, 36, 230, + 7, 168, 95, 194, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 44, 115, 52, 243, 3, 152, 31, + 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 16, 253, + 45, 46, 123, 249, 1, 205, 59, 100, 52, 168, 252, 245, 252, 141, 165, 146, 31, 144, 226, 241, 223, + 81, 191, 119, 214, 230, 198, 221, 38, 137, 87, 7, 172, 22, 219, 15, 28, 23, 39, 254, 56, 85, + 58, 248, 1, 234, 84, 247, 212, 128, 89, 194, 213, 254, 7, 37, 5, 119, 147, 142, 179, 68, 218, + 148, 159, 197, 246, 157, 135, 74, 62, 63, 64, 126, 56, 90, 165, 211, 154, 160, 100, 182, 169, 119, + 162, 232, 210, 127, 150, 152, 4, 83, 215, 23, 63, 238, 16, 229, 190, 95, 82, 50, 249, 1, 234, + 227, 241, 170, 253, 242, 196, 77, 190, 221, 226, 113, 239, 114, 209, 204, 51, 78, 9, 2, 242, 4, + 158, 109, 157, 46, 26, 118, 159, 36, 158, 26, 56, 235, 68, 173, 254, 211, 94, 181, 113, 53, 106, + 45, 63, 64, 221, 32, 113, 135, 239, 103, 81, 221, 59, 91, 52, 247, 100, 132, 108, 123, 111, 233, + 77, 207, 131, 227, 15, 184, 251, 147, 36, 244, 151, 217, 20, 4, 107, 248, 1, 234, 227, 112, 124, + 42, 124, 167, 119, 171, 104, 222, 54, 83, 79, 15, 120, 150, 4, 211, 83, 72, 71, 248, 24, 13, + 162, 137, 238, 14, 214, 240, 3, 130, 155, 164, 94, 207, 21, 45, 7, 175, 19, 77, 59, 142, 15, + 167, 0, 219, 239, 242, 164, 141, 131, 159, 95, 3, 59, 31, 236, 210, 68, 15, 140, 150, 240, 3, + 212, 109, 114, 149, 191, 93, 42, 106, 124, 56, 125, 11, 169, 69, 245, 117, 64, 110, 69, 12, 113, + 136, 210, 115, 99, 139, 120, 60, 129, 65, 176, 136, 31, 128, 27, 37, 199, 108, 84, 182, 200, 85, + 255, 116, 54, 58, 53, 16, 83, 230, 41, 93, 65, 205, 46, 173, 159, 110, 251, 32, 37, 91, 236, + 2, 127, 79, 50, 67, 17, 26, 197, 120, 96, 29, 63, 64, 221, 42, 75, 185, 3, 83, 163, 144, + 194, 169, 175, 227, 96, 57, 95, 30, 35, 44, 12, 130, 181, 252, 0, 10, 192, 121, 148, 118, 59, + 221, 136, 27, 96, 16, 144, 99, 96, 126, 176, 174, 74, 58, 78, 24, 39, 44, 8, 128, 245, 252, + 0, 10, 2, 78, 123, 205, 193, 102, 71, 72, 171, 171, 23, 144, 125, 96, 107, 81, 39, 12, 246, + 168, 5, 65, 176, 150, 31, 160, 9, 4, 142, 248, 191, 70, 145, 95, 88, 143, 50, 113, 134, 90, + 196, 27, 96, 215, 128, 93, 104, 82, 0, 18, 195, 15, 144, 186, 67, 207, 40, 149, 227, 186, 51, + 7, 252, 223, 221, 112, 92, 137, 169, 56, 81, 101, 102, 66, 0, 18, 202, 15, 64, 187, 29, 206, + 232, 199, 112, 252, 67, 15, 156, 16, 65, 74, 31, 160, 217, 68, 109, 33, 216, 34, 106, 155, 49, + 37, 38, 132, 31, 32, 5, 226, 25, 90, 13, 158, 144, 157, 147, 250, 127, 192, 160, 75, 4, 52, + 45, 98, 141, 73, 0, 133, 196, 241, 3, 164, 32, 52, 66, 129, 164, 206, 160, 23, 8, 139, 215, + 40, 248, 249, 148, 89, 131, 162, 93, 252, 128, 243, 193, 153, 166, 170, 84, 214, 104, 92, 208, 5, + 171, 228, 191, 246, 172, 153, 139, 162, 132, 243, 3, 148, 32, 184, 21, 249, 124, 87, 112, 104, 71, + 180, 131, 32, 89, 31, 147, 167, 66, 123, 248, 1, 212, 18, 238, 3, 203, 128, 32, 28, 143, 114, + 48, 28, 101, 193, 90, 192, 62, 126, 0, 5, 1, 103, 134, 197, 81, 204, 10, 203, 33, 80, 23, + 216, 25, 0, 75, 248, 1, 52, 61, 62, 68, 87, 133, 194, 224, 234, 241, 119, 176, 251, 45, 186, + 38, 176, 151, 31, 64, 65, 184, 26, 28, 245, 21, 186, 124, 46, 104, 17, 205, 172, 188, 50, 116, + 10, 63, 0, 47, 122, 62, 166, 187, 70, 114, 43, 120, 195, 226, 171, 66, 71, 241, 3, 46, 130, + 22, 240, 149, 228, 252, 136, 4, 220, 28, 113, 20, 63, 0, 111, 143, 61, 12, 131, 221, 40, 56, + 46, 72, 212, 221, 33, 71, 241, 3, 164, 149, 226, 245, 9, 112, 222, 185, 252, 128, 4, 157, 125, + 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, + 48, 63, 128, 249, 1, 204, 15, 96, 126, 64, 188, 95, 192, 252, 0, 230, 7, 36, 49, 63, 64, + 253, 18, 230, 7, 112, 225, 194, 133, 11, 23, 46, 92, 98, 153, 163, 153, 31, 192, 252, 0, 230, + 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 138, 31, 63, 160, 238, 155, 147, 93, + 165, 150, 31, 208, 228, 245, 9, 109, 234, 245, 201, 62, 218, 248, 131, 89, 34, 123, 243, 33, 113, + 244, 228, 233, 210, 195, 15, 192, 254, 84, 121, 240, 252, 141, 205, 186, 103, 138, 150, 173, 39, 138, + 198, 29, 103, 137, 249, 43, 15, 136, 131, 135, 142, 151, 124, 126, 0, 125, 113, 254, 131, 145, 255, + 109, 21, 205, 60, 249, 138, 209, 102, 109, 179, 197, 160, 81, 43, 197, 166, 159, 243, 74, 62, 63, + 32, 255, 209, 216, 62, 113, 231, 107, 251, 197, 3, 222, 181, 65, 241, 116, 75, 223, 56, 209, 166, + 103, 150, 120, 11, 86, 102, 21, 70, 44, 42, 217, 252, 0, 124, 56, 138, 143, 196, 48, 7, 121, + 101, 239, 252, 66, 10, 114, 24, 32, 255, 172, 247, 102, 246, 64, 179, 20, 95, 69, 168, 99, 98, + 248, 1, 178, 130, 252, 94, 223, 70, 209, 208, 155, 169, 151, 132, 125, 41, 166, 203, 116, 121, 210, + 254, 102, 86, 234, 220, 34, 116, 9, 235, 249, 1, 106, 0, 110, 108, 155, 43, 154, 191, 189, 218, + 72, 251, 115, 156, 164, 111, 40, 134, 124, 32, 193, 65, 176, 158, 31, 128, 199, 170, 253, 114, 69, + 141, 239, 126, 20, 117, 250, 77, 13, 39, 131, 219, 15, 246, 13, 152, 139, 52, 133, 231, 37, 168, + 59, 88, 204, 15, 144, 54, 73, 213, 248, 96, 250, 104, 112, 108, 178, 170, 6, 11, 10, 32, 241, + 152, 223, 34, 80, 63, 184, 203, 213, 42, 181, 63, 28, 239, 69, 177, 117, 162, 6, 70, 75, 249, + 1, 56, 37, 170, 125, 137, 176, 25, 7, 13, 50, 201, 202, 50, 185, 44, 176, 14, 16, 152, 91, + 19, 16, 4, 235, 249, 1, 242, 60, 79, 28, 1, 163, 148, 219, 74, 96, 40, 56, 152, 112, 29, + 83, 107, 87, 129, 191, 93, 109, 241, 120, 96, 61, 63, 128, 156, 47, 3, 246, 79, 104, 230, 227, + 140, 114, 141, 107, 236, 20, 201, 231, 187, 97, 186, 110, 11, 131, 96, 61, 63, 64, 10, 2, 102, + 140, 127, 21, 130, 176, 198, 8, 162, 128, 73, 214, 53, 122, 161, 63, 225, 119, 28, 63, 186, 210, + 103, 92, 96, 114, 0, 18, 195, 15, 208, 4, 97, 48, 73, 225, 3, 36, 130, 214, 71, 106, 184, + 67, 90, 201, 97, 248, 121, 60, 28, 43, 96, 174, 98, 147, 131, 144, 80, 126, 0, 218, 133, 96, + 57, 82, 159, 15, 68, 236, 18, 5, 127, 91, 5, 246, 1, 88, 37, 19, 3, 144, 56, 126, 0, + 5, 225, 82, 148, 188, 99, 18, 229, 48, 76, 17, 185, 85, 4, 52, 250, 97, 100, 140, 32, 157, + 230, 101, 68, 115, 152, 16, 128, 196, 241, 3, 164, 32, 220, 8, 14, 126, 68, 120, 140, 72, 204, + 128, 2, 250, 76, 129, 144, 242, 44, 5, 229, 107, 20, 81, 227, 32, 27, 239, 148, 152, 80, 126, + 0, 5, 161, 44, 74, 97, 41, 179, 108, 56, 181, 184, 46, 106, 67, 26, 44, 59, 152, 208, 10, + 108, 225, 7, 36, 209, 160, 182, 61, 162, 100, 222, 173, 27, 16, 60, 126, 107, 214, 56, 144, 112, + 126, 128, 18, 4, 183, 146, 120, 189, 163, 166, 43, 104, 7, 69, 117, 12, 208, 182, 132, 116, 51, + 23, 69, 182, 240, 3, 168, 37, 60, 14, 150, 169, 147, 125, 62, 16, 102, 70, 200, 54, 121, 42, + 180, 143, 31, 64, 65, 168, 7, 125, 250, 207, 136, 211, 96, 254, 113, 182, 217, 249, 136, 109, 231, + 7, 80, 16, 60, 96, 127, 134, 1, 42, 161, 45, 3, 51, 125, 227, 165, 237, 252, 0, 10, 192, + 253, 52, 173, 157, 54, 232, 6, 235, 193, 110, 178, 232, 154, 192, 126, 126, 0, 5, 1, 113, 91, + 171, 181, 83, 35, 205, 20, 101, 173, 188, 50, 116, 4, 63, 0, 11, 52, 253, 206, 154, 0, 228, + 130, 61, 98, 241, 189, 1, 103, 240, 3, 104, 109, 112, 21, 216, 231, 210, 189, 195, 74, 73, 22, + 23, 167, 241, 3, 240, 222, 65, 45, 176, 105, 120, 76, 208, 205, 82, 71, 242, 3, 110, 73, 144, + 243, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 128, 120, 190, 128, 249, 1, 204, 15, 72, 98, 126, + 128, 220, 18, 152, 31, 192, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 12, 115, 52, 243, 3, + 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 197, 153, 31, 80, 231, 237, 41, 55, 151, + 90, 126, 0, 170, 200, 33, 0, 251, 171, 126, 187, 184, 244, 241, 3, 112, 113, 82, 227, 253, 233, + 7, 155, 117, 203, 20, 174, 46, 57, 98, 218, 138, 95, 197, 177, 227, 167, 74, 7, 63, 32, 120, + 83, 116, 212, 90, 209, 172, 211, 12, 209, 194, 51, 86, 52, 109, 155, 45, 178, 103, 111, 19, 251, + 115, 143, 150, 124, 126, 128, 124, 91, 188, 108, 155, 109, 162, 177, 103, 98, 126, 14, 242, 118, 153, + 226, 171, 239, 150, 138, 109, 176, 144, 89, 3, 83, 84, 137, 230, 7, 224, 46, 82, 220, 52, 129, + 137, 216, 159, 240, 46, 83, 90, 1, 6, 225, 217, 54, 227, 132, 167, 103, 166, 168, 63, 124, 161, + 42, 163, 47, 153, 252, 0, 249, 209, 216, 221, 190, 77, 162, 182, 103, 106, 136, 130, 188, 121, 135, + 12, 81, 175, 79, 54, 10, 35, 31, 74, 208, 52, 156, 120, 126, 128, 44, 164, 174, 216, 122, 153, + 72, 241, 166, 105, 101, 244, 187, 104, 35, 100, 69, 171, 51, 76, 83, 29, 19, 207, 15, 192, 227, + 173, 29, 14, 8, 215, 144, 61, 162, 97, 143, 73, 122, 219, 223, 81, 32, 177, 55, 197, 173, 40, + 199, 239, 78, 64, 16, 18, 207, 15, 192, 13, 18, 229, 190, 95, 130, 57, 200, 207, 38, 251, 210, + 118, 24, 40, 65, 246, 43, 9, 214, 220, 254, 106, 9, 10, 66, 226, 248, 1, 234, 22, 25, 8, + 0, 170, 199, 191, 167, 156, 129, 122, 90, 128, 131, 208, 66, 86, 83, 122, 173, 43, 19, 208, 29, + 18, 199, 15, 80, 55, 73, 129, 99, 117, 192, 14, 201, 178, 55, 141, 58, 236, 44, 73, 106, 95, + 131, 191, 61, 153, 136, 129, 49, 97, 252, 0, 10, 0, 102, 145, 235, 33, 171, 193, 12, 114, 143, + 99, 174, 193, 49, 96, 77, 45, 14, 66, 194, 249, 1, 104, 143, 129, 77, 138, 34, 195, 236, 113, + 101, 128, 244, 248, 221, 86, 237, 26, 79, 40, 63, 64, 10, 194, 37, 148, 60, 125, 123, 33, 45, + 96, 168, 50, 44, 160, 10, 168, 32, 24, 239, 91, 165, 27, 72, 40, 63, 64, 10, 194, 77, 96, + 139, 194, 106, 133, 67, 237, 12, 4, 107, 58, 28, 95, 81, 91, 146, 153, 203, 230, 132, 242, 3, + 200, 1, 164, 197, 60, 5, 103, 250, 68, 17, 178, 207, 159, 33, 170, 68, 47, 179, 87, 144, 9, + 229, 7, 168, 5, 154, 245, 45, 224, 200, 251, 97, 198, 128, 64, 152, 204, 211, 223, 130, 61, 103, + 98, 0, 18, 203, 15, 144, 90, 66, 51, 104, 5, 75, 180, 66, 105, 3, 193, 164, 214, 86, 193, + 251, 122, 192, 241, 255, 76, 144, 206, 39, 158, 31, 32, 5, 225, 19, 73, 51, 28, 208, 81, 139, + 7, 66, 184, 67, 5, 127, 195, 1, 242, 52, 4, 239, 51, 248, 253, 41, 51, 166, 196, 132, 243, + 3, 40, 0, 120, 6, 23, 71, 41, 152, 54, 250, 29, 133, 215, 247, 197, 25, 128, 196, 243, 3, + 40, 0, 87, 128, 51, 207, 211, 156, 95, 184, 27, 184, 163, 234, 26, 175, 154, 49, 14, 216, 194, + 15, 160, 32, 60, 2, 206, 141, 165, 81, 62, 114, 255, 15, 93, 73, 182, 48, 107, 81, 100, 27, + 63, 128, 130, 240, 111, 176, 35, 58, 14, 26, 173, 17, 14, 153, 9, 88, 178, 157, 31, 64, 65, + 248, 194, 168, 169, 107, 186, 196, 86, 179, 181, 196, 182, 243, 3, 240, 66, 9, 70, 244, 10, 224, + 224, 124, 157, 179, 45, 211, 165, 144, 31, 112, 155, 5, 75, 98, 71, 240, 3, 144, 48, 249, 30, + 221, 32, 41, 132, 206, 1, 155, 138, 131, 166, 69, 215, 4, 142, 225, 7, 60, 74, 172, 0, 45, + 63, 32, 213, 234, 59, 69, 142, 224, 7, 40, 151, 204, 110, 255, 139, 154, 51, 111, 249, 78, 115, + 199, 240, 3, 40, 8, 255, 128, 62, 63, 148, 120, 66, 9, 145, 220, 57, 138, 31, 64, 65, 120, + 9, 57, 163, 9, 124, 132, 239, 44, 126, 0, 206, 10, 9, 116, 158, 249, 1, 204, 15, 96, 126, + 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, + 3, 152, 31, 16, 207, 23, 48, 63, 128, 249, 1, 73, 204, 15, 144, 91, 2, 243, 3, 152, 31, + 192, 133, 11, 23, 46, 92, 184, 112, 137, 97, 142, 102, 126, 0, 243, 3, 152, 31, 192, 252, 0, + 230, 7, 20, 123, 126, 0, 42, 68, 158, 249, 112, 198, 176, 82, 201, 15, 104, 208, 43, 243, 142, + 122, 189, 51, 55, 55, 232, 153, 41, 124, 35, 87, 151, 46, 126, 128, 122, 67, 164, 198, 192, 217, + 194, 213, 254, 7, 209, 220, 55, 65, 76, 154, 183, 91, 28, 58, 124, 188, 116, 240, 3, 112, 244, + 197, 155, 163, 184, 157, 174, 102, 235, 89, 34, 185, 85, 170, 104, 209, 54, 83, 76, 157, 181, 89, + 28, 252, 237, 120, 41, 224, 7, 140, 89, 167, 220, 25, 190, 181, 221, 126, 81, 214, 183, 89, 52, + 241, 76, 80, 182, 188, 189, 208, 49, 75, 12, 27, 189, 92, 28, 56, 118, 82, 124, 183, 110, 79, + 201, 230, 7, 220, 76, 79, 140, 49, 17, 123, 53, 239, 156, 224, 166, 199, 231, 219, 101, 136, 151, + 250, 231, 136, 234, 35, 22, 138, 39, 71, 45, 45, 185, 252, 128, 144, 77, 19, 222, 21, 162, 41, + 180, 2, 151, 180, 27, 188, 97, 143, 73, 121, 48, 80, 246, 178, 112, 234, 181, 159, 31, 16, 124, + 62, 136, 42, 242, 215, 38, 235, 101, 162, 159, 5, 246, 162, 149, 57, 199, 109, 231, 7, 40, 15, + 71, 135, 237, 65, 1, 181, 112, 121, 11, 5, 224, 52, 233, 4, 80, 47, 112, 171, 133, 65, 176, + 145, 31, 64, 143, 199, 43, 127, 61, 255, 88, 139, 246, 99, 103, 25, 232, 130, 48, 47, 121, 119, + 176, 7, 44, 238, 18, 137, 231, 7, 200, 27, 36, 80, 234, 230, 106, 149, 186, 71, 47, 199, 48, + 188, 190, 19, 142, 40, 157, 185, 205, 170, 46, 97, 27, 63, 64, 221, 34, 67, 178, 217, 173, 36, + 123, 11, 20, 74, 181, 157, 207, 23, 152, 7, 175, 253, 211, 66, 109, 128, 61, 252, 0, 10, 192, + 69, 164, 8, 215, 147, 196, 200, 186, 193, 41, 96, 47, 89, 61, 48, 38, 148, 31, 32, 5, 225, + 73, 176, 172, 40, 68, 211, 75, 105, 92, 40, 99, 166, 96, 90, 30, 187, 18, 202, 15, 8, 6, + 192, 237, 191, 140, 164, 49, 7, 194, 138, 37, 243, 13, 103, 136, 158, 96, 119, 89, 52, 30, 36, + 150, 31, 32, 5, 225, 9, 41, 0, 34, 138, 64, 96, 154, 238, 106, 22, 4, 33, 241, 252, 0, + 234, 6, 216, 172, 123, 71, 80, 133, 6, 52, 45, 1, 185, 67, 85, 77, 14, 64, 226, 249, 1, + 20, 128, 243, 40, 237, 246, 2, 93, 135, 221, 186, 66, 234, 19, 36, 177, 173, 6, 118, 189, 137, + 65, 72, 60, 63, 128, 130, 112, 3, 229, 22, 61, 28, 86, 44, 93, 120, 205, 176, 18, 172, 29, + 216, 181, 38, 5, 192, 30, 126, 0, 5, 161, 62, 52, 255, 125, 81, 100, 152, 215, 34, 119, 214, + 83, 240, 202, 196, 43, 175, 177, 155, 31, 128, 14, 12, 210, 57, 203, 1, 131, 68, 236, 242, 207, + 184, 104, 26, 2, 129, 185, 207, 140, 41, 209, 46, 126, 192, 5, 52, 22, 108, 12, 113, 222, 29, + 130, 209, 8, 24, 96, 54, 212, 227, 183, 240, 254, 166, 113, 6, 192, 30, 126, 0, 5, 225, 78, + 56, 139, 131, 21, 249, 124, 152, 190, 31, 50, 83, 132, 254, 109, 79, 188, 96, 21, 91, 249, 1, + 20, 132, 23, 201, 17, 189, 166, 31, 208, 8, 168, 229, 86, 49, 197, 140, 124, 228, 182, 243, 3, + 40, 8, 89, 65, 130, 140, 59, 42, 180, 206, 7, 38, 78, 133, 246, 242, 3, 104, 44, 168, 9, + 150, 103, 208, 236, 229, 113, 0, 73, 19, 47, 152, 188, 32, 178, 151, 31, 64, 65, 120, 128, 144, + 123, 1, 45, 63, 64, 106, 17, 203, 193, 30, 182, 96, 73, 108, 63, 63, 0, 11, 12, 134, 175, + 41, 99, 129, 91, 106, 1, 5, 206, 143, 136, 151, 25, 100, 70, 23, 176, 154, 31, 128, 176, 181, + 37, 242, 226, 135, 186, 65, 71, 139, 31, 222, 58, 134, 31, 112, 49, 88, 138, 212, 252, 17, 166, + 80, 35, 1, 79, 175, 29, 197, 15, 120, 28, 108, 28, 113, 7, 175, 75, 208, 227, 123, 199, 241, + 3, 42, 36, 120, 255, 130, 179, 248, 1, 9, 118, 158, 249, 1, 204, 15, 96, 126, 0, 243, 3, + 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, + 16, 207, 23, 48, 63, 128, 249, 1, 73, 204, 15, 144, 91, 2, 243, 3, 152, 31, 192, 133, 11, + 23, 46, 92, 184, 112, 137, 97, 142, 102, 126, 0, 243, 3, 152, 31, 80, 194, 248, 1, 41, 30, + 255, 101, 165, 146, 31, 128, 217, 164, 26, 117, 251, 161, 95, 205, 255, 229, 252, 82, 234, 248, 1, + 88, 202, 125, 183, 164, 111, 221, 183, 167, 136, 150, 111, 100, 137, 78, 195, 86, 138, 205, 191, 30, + 45, 29, 252, 0, 26, 217, 51, 240, 102, 104, 202, 167, 107, 68, 178, 47, 67, 180, 244, 164, 139, + 169, 11, 118, 138, 223, 14, 159, 40, 29, 252, 0, 188, 73, 138, 123, 135, 30, 234, 118, 64, 60, + 237, 157, 169, 108, 124, 122, 238, 181, 108, 49, 117, 214, 22, 113, 70, 106, 5, 37, 154, 31, 128, + 183, 201, 111, 240, 237, 19, 15, 120, 215, 6, 19, 177, 123, 123, 76, 17, 147, 166, 110, 20, 219, + 127, 59, 81, 178, 249, 1, 242, 163, 115, 37, 17, 123, 171, 169, 180, 1, 210, 47, 254, 213, 118, + 172, 120, 246, 235, 249, 226, 169, 239, 151, 148, 108, 126, 128, 108, 79, 121, 103, 139, 100, 111, 122, + 112, 47, 112, 139, 246, 25, 162, 214, 187, 211, 150, 86, 255, 116, 118, 109, 19, 91, 160, 243, 248, + 1, 248, 144, 4, 159, 19, 54, 251, 223, 79, 133, 37, 244, 110, 69, 3, 212, 151, 166, 76, 51, + 187, 162, 131, 248, 1, 29, 115, 149, 135, 164, 21, 70, 44, 18, 13, 186, 103, 234, 109, 125, 71, + 13, 97, 71, 176, 107, 44, 24, 144, 29, 192, 15, 160, 135, 163, 176, 38, 56, 211, 176, 219, 164, + 209, 148, 47, 48, 184, 33, 26, 236, 79, 202, 61, 238, 5, 187, 195, 130, 32, 216, 207, 15, 80, + 31, 143, 187, 90, 165, 214, 7, 39, 215, 25, 104, 128, 126, 130, 46, 241, 46, 28, 47, 52, 91, + 47, 236, 8, 126, 128, 180, 60, 158, 136, 25, 229, 180, 41, 119, 213, 13, 210, 240, 122, 127, 84, + 154, 91, 208, 18, 236, 229, 7, 144, 243, 120, 118, 27, 232, 73, 102, 229, 124, 228, 240, 183, 78, + 112, 188, 199, 162, 238, 96, 15, 63, 64, 45, 224, 232, 227, 148, 46, 55, 127, 12, 80, 91, 66, + 168, 106, 108, 31, 24, 182, 132, 155, 173, 26, 24, 109, 225, 7, 80, 43, 248, 11, 216, 167, 186, + 178, 217, 208, 64, 252, 2, 193, 249, 154, 192, 11, 231, 153, 28, 4, 123, 248, 1, 82, 16, 26, + 194, 217, 63, 84, 40, 179, 116, 97, 93, 32, 138, 166, 223, 130, 64, 220, 109, 193, 120, 96, 15, + 63, 128, 2, 112, 62, 137, 34, 140, 37, 179, 5, 134, 41, 56, 251, 153, 77, 153, 177, 141, 31, + 64, 1, 184, 156, 4, 147, 39, 195, 228, 30, 151, 109, 39, 169, 202, 46, 52, 49, 0, 246, 240, + 3, 130, 131, 161, 39, 13, 181, 130, 115, 35, 36, 89, 150, 95, 59, 14, 134, 99, 194, 237, 38, + 6, 193, 30, 126, 0, 181, 2, 84, 143, 251, 33, 16, 127, 232, 201, 228, 13, 214, 9, 104, 239, + 131, 149, 51, 41, 0, 246, 241, 3, 40, 8, 141, 32, 0, 103, 117, 21, 226, 6, 1, 128, 247, + 35, 121, 42, 21, 142, 255, 136, 119, 197, 104, 43, 63, 128, 2, 112, 51, 216, 15, 6, 103, 58, + 96, 24, 8, 183, 255, 32, 37, 109, 255, 91, 188, 130, 74, 219, 248, 1, 20, 128, 171, 192, 186, + 5, 147, 175, 27, 15, 130, 162, 208, 194, 41, 223, 48, 103, 121, 165, 56, 3, 96, 31, 63, 128, + 130, 208, 152, 176, 24, 133, 166, 66, 131, 110, 16, 124, 15, 252, 29, 167, 200, 119, 112, 86, 137, + 103, 28, 176, 149, 31, 64, 65, 152, 38, 143, 254, 26, 199, 3, 6, 45, 3, 95, 255, 196, 4, + 142, 136, 189, 252, 0, 37, 229, 182, 199, 223, 44, 44, 77, 166, 176, 164, 30, 47, 169, 235, 152, + 52, 19, 216, 203, 15, 160, 32, 60, 73, 68, 136, 112, 211, 159, 233, 240, 4, 10, 128, 35, 248, + 1, 87, 42, 68, 153, 124, 58, 140, 209, 130, 104, 5, 216, 211, 22, 92, 29, 58, 131, 31, 0, + 206, 181, 128, 179, 255, 171, 129, 243, 150, 105, 143, 156, 196, 15, 184, 140, 6, 67, 25, 163, 131, + 60, 129, 42, 22, 63, 198, 115, 12, 63, 224, 10, 176, 87, 232, 234, 79, 168, 183, 203, 19, 240, + 28, 211, 81, 252, 128, 58, 96, 3, 205, 90, 235, 199, 179, 16, 178, 133, 31, 16, 239, 156, 110, + 233, 82, 216, 138, 139, 33, 187, 11, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, + 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 48, 229, 11, 152, 31, 192, 252, 128, 36, 230, 7, 112, 225, 194, 133, 11, 23, 46, 92, 98, + 158, 163, 153, 31, 192, 252, 128, 146, 200, 15, 192, 252, 1, 205, 59, 100, 220, 81, 90, 249, 1, + 215, 52, 232, 153, 57, 177, 206, 219, 83, 142, 215, 76, 91, 81, 122, 248, 1, 212, 212, 43, 227, + 141, 198, 186, 253, 115, 132, 171, 75, 150, 232, 240, 197, 114, 177, 41, 239, 104, 233, 224, 7, 224, + 200, 141, 253, 18, 239, 28, 39, 15, 216, 44, 146, 189, 99, 149, 189, 126, 139, 87, 238, 22, 191, + 31, 61, 89, 58, 248, 1, 120, 199, 24, 117, 133, 101, 59, 238, 17, 53, 60, 211, 149, 93, 95, + 175, 116, 207, 17, 243, 150, 108, 47, 29, 252, 0, 245, 118, 249, 141, 190, 61, 226, 9, 239, 178, + 224, 70, 200, 158, 239, 207, 16, 171, 55, 236, 19, 187, 127, 63, 81, 178, 249, 1, 242, 109, 242, + 135, 188, 171, 69, 35, 247, 15, 74, 0, 158, 109, 61, 86, 188, 218, 43, 75, 188, 8, 78, 212, + 25, 187, 162, 100, 243, 3, 212, 103, 4, 183, 249, 182, 139, 166, 190, 31, 20, 21, 121, 50, 237, + 251, 173, 247, 230, 100, 81, 237, 243, 57, 75, 75, 60, 63, 0, 229, 180, 85, 223, 57, 32, 146, + 251, 204, 212, 108, 128, 246, 163, 100, 230, 171, 20, 119, 252, 34, 73, 71, 243, 3, 240, 97, 233, + 51, 163, 86, 138, 106, 159, 204, 86, 90, 128, 70, 58, 187, 139, 182, 190, 95, 108, 82, 107, 116, + 30, 63, 64, 29, 196, 158, 249, 112, 198, 104, 24, 8, 231, 147, 224, 65, 14, 194, 105, 104, 5, + 255, 133, 227, 141, 38, 142, 73, 206, 225, 7, 208, 3, 202, 20, 112, 242, 90, 176, 28, 112, 244, + 140, 78, 254, 113, 84, 142, 120, 76, 30, 152, 157, 195, 15, 144, 150, 198, 61, 41, 119, 160, 94, + 242, 229, 69, 96, 207, 155, 169, 23, 118, 26, 63, 160, 12, 56, 124, 47, 28, 127, 51, 208, 3, + 97, 96, 48, 29, 239, 93, 240, 190, 75, 77, 14, 130, 189, 252, 0, 41, 8, 247, 131, 165, 71, + 200, 59, 158, 1, 99, 196, 83, 22, 116, 7, 123, 249, 1, 20, 128, 75, 193, 62, 131, 177, 224, + 168, 164, 13, 10, 72, 93, 66, 253, 25, 245, 194, 143, 154, 28, 4, 123, 249, 1, 82, 16, 94, + 10, 2, 85, 220, 186, 217, 102, 49, 16, 185, 208, 10, 210, 41, 25, 163, 153, 65, 176, 157, 31, + 160, 36, 91, 5, 7, 183, 135, 81, 138, 169, 249, 70, 79, 131, 245, 48, 147, 48, 99, 59, 63, + 128, 130, 112, 61, 56, 56, 64, 118, 90, 203, 21, 209, 176, 134, 58, 163, 210, 212, 196, 32, 216, + 199, 15, 160, 0, 156, 7, 214, 30, 108, 127, 56, 237, 176, 100, 171, 32, 64, 157, 77, 12, 128, + 189, 252, 0, 37, 8, 110, 69, 34, 243, 83, 36, 213, 56, 181, 12, 4, 46, 29, 3, 123, 10, + 245, 69, 38, 5, 193, 62, 126, 128, 212, 18, 230, 66, 32, 254, 140, 2, 165, 161, 190, 134, 203, + 232, 122, 38, 5, 192, 118, 126, 192, 165, 212, 13, 68, 88, 233, 108, 104, 70, 122, 65, 137, 89, + 107, 155, 16, 0, 123, 249, 1, 20, 132, 134, 96, 155, 244, 28, 47, 52, 40, 186, 131, 221, 228, + 8, 181, 132, 255, 51, 65, 60, 109, 31, 63, 128, 2, 240, 40, 56, 54, 61, 204, 12, 16, 186, + 70, 112, 7, 3, 113, 154, 248, 1, 247, 198, 25, 0, 123, 249, 1, 20, 132, 239, 229, 171, 67, + 189, 46, 96, 48, 77, 226, 216, 241, 22, 216, 109, 241, 140, 3, 78, 224, 7, 52, 52, 156, 2, + 221, 17, 185, 34, 157, 226, 129, 175, 217, 206, 15, 160, 0, 60, 12, 182, 80, 14, 0, 177, 6, + 195, 117, 139, 145, 102, 0, 215, 156, 194, 15, 184, 29, 108, 120, 24, 164, 150, 220, 50, 240, 102, + 74, 53, 19, 23, 68, 246, 243, 3, 40, 8, 111, 130, 29, 148, 91, 128, 166, 59, 172, 1, 51, + 125, 195, 181, 147, 248, 1, 143, 19, 60, 77, 239, 58, 160, 157, 133, 79, 177, 28, 195, 15, 64, + 38, 208, 4, 229, 204, 187, 131, 221, 160, 31, 12, 124, 151, 38, 89, 88, 156, 195, 15, 112, 251, + 255, 74, 151, 189, 216, 228, 135, 154, 201, 12, 51, 107, 26, 76, 4, 63, 160, 10, 216, 125, 9, + 126, 130, 237, 28, 126, 128, 77, 251, 23, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 177, 126, + 9, 243, 3, 152, 31, 192, 252, 0, 230, 7, 200, 45, 129, 249, 1, 204, 15, 224, 194, 133, 11, + 23, 46, 92, 184, 196, 48, 71, 51, 63, 160, 68, 243, 3, 80, 25, 134, 91, 219, 74, 29, 63, + 0, 75, 157, 183, 167, 252, 165, 65, 207, 204, 201, 245, 250, 100, 31, 173, 241, 253, 146, 210, 195, + 15, 160, 166, 222, 6, 239, 182, 214, 251, 112, 182, 112, 117, 206, 20, 173, 7, 44, 20, 203, 247, + 29, 41, 61, 252, 0, 188, 131, 140, 183, 209, 155, 125, 180, 45, 40, 163, 223, 0, 103, 237, 68, + 152, 0, 148, 44, 126, 192, 224, 253, 202, 51, 132, 219, 218, 238, 22, 181, 60, 211, 148, 61, 64, + 157, 250, 207, 17, 171, 215, 239, 43, 29, 252, 0, 124, 118, 128, 247, 10, 111, 246, 237, 18, 21, + 189, 11, 243, 119, 126, 122, 211, 197, 192, 175, 23, 138, 35, 196, 0, 40, 209, 252, 0, 245, 193, + 9, 90, 57, 239, 18, 209, 204, 51, 78, 9, 194, 75, 29, 199, 139, 126, 48, 32, 118, 159, 179, + 185, 228, 243, 3, 212, 187, 197, 247, 123, 215, 137, 22, 222, 113, 193, 29, 161, 201, 190, 116, 241, + 244, 160, 121, 162, 220, 200, 37, 37, 155, 31, 128, 199, 251, 186, 230, 138, 228, 175, 246, 136, 228, + 54, 25, 170, 124, 94, 57, 54, 107, 63, 110, 69, 243, 14, 25, 205, 99, 188, 197, 85, 76, 248, + 1, 253, 114, 149, 39, 199, 85, 71, 44, 17, 245, 123, 101, 105, 119, 132, 162, 8, 98, 124, 60, + 137, 85, 157, 207, 15, 24, 179, 90, 121, 108, 94, 225, 155, 69, 203, 154, 118, 26, 63, 74, 171, + 11, 114, 181, 74, 61, 10, 199, 46, 241, 202, 227, 28, 205, 15, 80, 247, 12, 128, 147, 93, 117, + 36, 115, 184, 73, 26, 83, 236, 214, 55, 35, 231, 144, 35, 249, 1, 210, 53, 65, 21, 24, 0, + 127, 55, 208, 2, 96, 87, 168, 105, 210, 216, 228, 60, 126, 0, 5, 224, 90, 74, 161, 167, 23, + 0, 148, 214, 79, 160, 212, 188, 102, 4, 193, 57, 252, 0, 41, 0, 183, 65, 115, 31, 20, 70, + 41, 138, 73, 215, 218, 194, 184, 112, 173, 73, 45, 193, 57, 252, 0, 10, 192, 249, 224, 220, 255, + 224, 120, 84, 87, 51, 232, 86, 6, 197, 141, 240, 123, 178, 137, 235, 20, 103, 240, 3, 164, 32, + 120, 80, 34, 31, 34, 145, 41, 204, 19, 200, 4, 171, 110, 98, 16, 28, 195, 15, 80, 186, 65, + 36, 213, 40, 4, 72, 29, 15, 46, 49, 49, 8, 246, 242, 3, 164, 32, 220, 10, 205, 60, 91, + 171, 20, 13, 81, 141, 228, 219, 89, 120, 205, 3, 199, 27, 76, 28, 15, 236, 229, 7, 80, 0, + 110, 34, 128, 82, 164, 76, 179, 200, 28, 90, 9, 65, 120, 193, 196, 86, 96, 47, 63, 64, 45, + 224, 20, 102, 142, 254, 85, 195, 19, 18, 58, 60, 17, 65, 146, 185, 10, 38, 5, 192, 126, 126, + 0, 181, 130, 166, 116, 29, 16, 48, 74, 177, 45, 141, 13, 191, 67, 64, 50, 80, 117, 110, 82, + 16, 236, 229, 7, 208, 64, 136, 11, 162, 157, 97, 5, 211, 90, 221, 176, 219, 143, 227, 193, 223, + 77, 8, 128, 189, 252, 0, 10, 194, 157, 224, 220, 40, 137, 28, 35, 12, 166, 69, 217, 150, 130, + 253, 215, 132, 0, 56, 130, 31, 128, 12, 128, 79, 96, 144, 59, 83, 8, 157, 33, 139, 165, 221, + 154, 89, 194, 227, 95, 0, 246, 96, 188, 200, 45, 219, 249, 1, 20, 132, 55, 192, 193, 131, 170, + 115, 232, 184, 1, 75, 68, 30, 39, 142, 131, 125, 139, 93, 40, 206, 0, 56, 130, 31, 240, 184, + 230, 140, 7, 34, 81, 101, 130, 107, 5, 183, 223, 139, 72, 158, 120, 198, 1, 39, 240, 3, 110, + 35, 138, 156, 190, 98, 220, 29, 22, 175, 179, 24, 172, 82, 60, 139, 34, 251, 249, 1, 110, 255, + 29, 36, 151, 21, 209, 44, 138, 164, 35, 94, 77, 62, 16, 103, 23, 176, 159, 31, 64, 173, 224, + 19, 8, 196, 17, 3, 134, 144, 60, 35, 228, 17, 54, 227, 122, 147, 214, 2, 142, 225, 7, 188, + 170, 4, 192, 45, 177, 3, 220, 33, 205, 125, 3, 252, 222, 22, 111, 146, 152, 124, 43, 223, 126, + 126, 0, 97, 181, 110, 77, 201, 39, 204, 106, 87, 127, 115, 224, 104, 153, 236, 198, 73, 252, 128, + 123, 192, 38, 73, 103, 124, 44, 204, 10, 79, 39, 224, 49, 158, 67, 248, 1, 176, 180, 5, 251, + 218, 140, 129, 205, 170, 105, 48, 17, 252, 128, 75, 147, 18, 92, 152, 31, 192, 252, 0, 230, 7, + 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, + 128, 249, 1, 204, 15, 96, 126, 64, 204, 95, 192, 252, 0, 230, 7, 36, 49, 63, 64, 110, 9, + 204, 15, 96, 126, 0, 23, 46, 92, 184, 112, 225, 194, 37, 134, 57, 186, 244, 240, 3, 82, 60, + 254, 91, 74, 29, 63, 64, 45, 245, 250, 102, 127, 84, 191, 119, 214, 230, 42, 95, 207, 47, 61, + 252, 0, 106, 222, 216, 140, 183, 213, 250, 108, 158, 72, 126, 35, 75, 188, 242, 193, 220, 210, 197, + 15, 192, 219, 233, 120, 55, 185, 229, 23, 59, 21, 25, 61, 218, 47, 59, 14, 137, 211, 58, 66, + 166, 18, 201, 15, 80, 238, 22, 247, 205, 19, 255, 104, 183, 91, 212, 246, 76, 85, 182, 195, 244, + 255, 114, 137, 216, 182, 227, 80, 233, 224, 7, 168, 15, 82, 110, 241, 237, 20, 85, 188, 243, 148, + 0, 60, 223, 97, 162, 24, 155, 185, 174, 116, 240, 3, 212, 231, 7, 55, 248, 246, 137, 202, 222, + 249, 34, 217, 157, 191, 37, 174, 83, 223, 201, 98, 225, 250, 125, 98, 230, 142, 131, 37, 159, 31, + 160, 254, 94, 222, 187, 88, 180, 132, 49, 64, 217, 21, 230, 245, 139, 103, 251, 79, 19, 77, 136, + 13, 80, 162, 249, 1, 202, 147, 228, 158, 185, 34, 229, 195, 213, 33, 59, 65, 91, 182, 78, 23, + 53, 62, 156, 190, 229, 201, 145, 75, 63, 45, 217, 252, 128, 33, 251, 21, 117, 217, 211, 67, 22, + 41, 78, 107, 182, 194, 162, 78, 176, 106, 17, 111, 113, 21, 47, 126, 0, 118, 15, 124, 122, 252, + 212, 192, 89, 51, 146, 91, 167, 173, 211, 236, 15, 254, 149, 54, 78, 159, 23, 67, 61, 138, 7, + 63, 128, 154, 110, 117, 112, 242, 33, 37, 231, 104, 232, 78, 112, 180, 147, 164, 46, 191, 52, 198, + 250, 56, 159, 31, 32, 93, 15, 44, 39, 161, 180, 150, 35, 128, 58, 162, 242, 113, 182, 76, 231, + 242, 3, 200, 249, 203, 104, 123, 172, 208, 209, 11, 29, 64, 113, 181, 9, 99, 147, 51, 249, 1, + 20, 128, 43, 49, 163, 180, 162, 17, 82, 69, 147, 161, 234, 144, 173, 240, 122, 53, 19, 132, 81, + 206, 227, 7, 72, 65, 120, 77, 85, 142, 234, 201, 98, 32, 0, 99, 226, 201, 41, 170, 9, 130, + 115, 248, 1, 82, 0, 254, 3, 182, 75, 163, 19, 150, 131, 129, 26, 130, 86, 102, 168, 199, 157, + 200, 15, 64, 189, 208, 67, 112, 150, 207, 160, 36, 78, 82, 130, 105, 37, 51, 51, 224, 88, 199, + 164, 245, 138, 51, 248, 1, 82, 16, 110, 6, 91, 175, 57, 235, 90, 101, 216, 97, 176, 180, 120, + 178, 203, 106, 199, 46, 71, 240, 3, 40, 0, 247, 163, 58, 36, 10, 181, 216, 89, 176, 6, 102, + 36, 94, 119, 12, 63, 128, 2, 112, 29, 52, 239, 84, 131, 156, 194, 122, 120, 157, 234, 38, 181, + 2, 103, 240, 3, 40, 8, 223, 210, 234, 207, 72, 39, 24, 160, 197, 209, 33, 56, 126, 105, 82, + 0, 28, 195, 15, 56, 159, 166, 194, 128, 238, 130, 168, 112, 242, 229, 221, 96, 117, 227, 93, 27, + 144, 63, 246, 242, 3, 40, 0, 23, 194, 168, 223, 32, 130, 227, 1, 13, 100, 229, 7, 147, 178, + 78, 219, 207, 15, 160, 32, 180, 0, 251, 201, 168, 21, 104, 97, 10, 116, 237, 208, 57, 222, 1, + 209, 17, 252, 0, 44, 112, 182, 159, 6, 103, 86, 135, 105, 246, 122, 134, 185, 202, 155, 153, 49, + 37, 58, 129, 31, 240, 23, 76, 187, 45, 59, 110, 16, 4, 89, 81, 122, 12, 142, 147, 241, 130, + 42, 206, 0, 56, 130, 31, 112, 121, 80, 53, 234, 214, 37, 70, 4, 116, 0, 10, 170, 61, 7, + 83, 232, 213, 241, 140, 3, 78, 224, 7, 160, 106, 244, 67, 29, 140, 86, 164, 108, 244, 104, 83, + 113, 86, 136, 103, 81, 100, 59, 63, 0, 206, 44, 78, 133, 255, 163, 11, 159, 64, 36, 118, 0, + 156, 113, 249, 61, 163, 227, 145, 218, 58, 137, 31, 208, 13, 199, 1, 237, 89, 215, 96, 53, 2, + 210, 0, 216, 14, 87, 145, 38, 12, 130, 246, 243, 3, 240, 170, 16, 28, 173, 7, 199, 83, 97, + 6, 193, 173, 68, 157, 50, 117, 103, 167, 35, 248, 1, 20, 132, 127, 18, 99, 84, 110, 222, 136, + 220, 28, 12, 246, 140, 133, 79, 177, 28, 195, 15, 168, 8, 54, 139, 28, 71, 124, 222, 179, 102, + 44, 119, 99, 25, 4, 237, 226, 7, 32, 89, 174, 150, 89, 108, 16, 43, 166, 65, 203, 249, 1, + 54, 61, 190, 103, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 32, 198, 47, 97, 126, 0, + 243, 3, 152, 31, 192, 252, 0, 185, 37, 48, 63, 128, 249, 1, 92, 184, 112, 225, 194, 133, 11, + 151, 104, 139, 154, 108, 181, 84, 241, 3, 176, 52, 233, 50, 225, 177, 134, 221, 39, 13, 125, 230, + 195, 25, 147, 74, 21, 63, 128, 42, 156, 81, 105, 200, 2, 209, 164, 103, 150, 112, 189, 157, 83, + 186, 248, 1, 248, 108, 1, 31, 176, 224, 83, 38, 87, 251, 73, 34, 165, 117, 134, 88, 190, 37, + 79, 28, 63, 117, 166, 148, 240, 3, 6, 239, 87, 110, 152, 220, 219, 121, 159, 168, 233, 201, 81, + 50, 80, 15, 73, 93, 45, 242, 126, 61, 86, 58, 248, 1, 234, 83, 37, 69, 70, 239, 153, 171, + 236, 9, 106, 213, 125, 178, 88, 184, 124, 71, 233, 224, 7, 168, 118, 147, 111, 183, 168, 12, 1, + 80, 114, 144, 183, 74, 19, 31, 127, 189, 160, 116, 240, 3, 228, 32, 84, 247, 204, 18, 45, 61, + 233, 202, 214, 184, 151, 94, 27, 39, 62, 130, 202, 246, 95, 178, 173, 228, 243, 3, 148, 199, 234, + 221, 115, 69, 203, 30, 243, 67, 50, 209, 55, 125, 107, 178, 168, 254, 205, 226, 18, 206, 15, 232, + 149, 167, 60, 51, 104, 50, 114, 163, 104, 240, 254, 44, 197, 241, 100, 218, 28, 217, 178, 77, 250, + 209, 122, 125, 179, 71, 148, 108, 126, 64, 234, 79, 202, 94, 130, 114, 223, 45, 17, 181, 250, 79, + 251, 73, 71, 49, 130, 187, 199, 111, 140, 242, 22, 87, 241, 228, 7, 144, 214, 255, 9, 218, 39, + 156, 167, 229, 8, 64, 16, 94, 86, 151, 200, 81, 214, 163, 120, 241, 3, 164, 235, 128, 118, 234, + 70, 105, 73, 19, 240, 39, 252, 60, 45, 197, 237, 47, 27, 67, 125, 138, 21, 63, 0, 243, 140, + 53, 4, 71, 79, 235, 108, 141, 199, 237, 243, 222, 88, 19, 172, 57, 158, 31, 32, 5, 160, 2, + 216, 233, 48, 130, 136, 199, 226, 104, 157, 206, 229, 7, 72, 65, 104, 74, 170, 177, 194, 219, 228, + 221, 138, 180, 190, 111, 60, 105, 246, 28, 205, 15, 160, 0, 52, 4, 91, 23, 38, 205, 38, 6, + 231, 238, 56, 7, 106, 103, 242, 3, 40, 0, 15, 128, 195, 121, 17, 148, 163, 93, 193, 254, 26, + 103, 16, 156, 197, 15, 144, 2, 112, 37, 137, 37, 244, 8, 18, 234, 107, 115, 227, 17, 71, 105, + 7, 70, 199, 240, 3, 40, 0, 15, 131, 205, 142, 32, 148, 252, 141, 248, 66, 101, 76, 8, 130, + 115, 248, 1, 20, 128, 71, 193, 178, 245, 50, 79, 75, 45, 0, 91, 197, 49, 232, 42, 119, 197, + 27, 4, 71, 241, 3, 40, 0, 215, 81, 254, 224, 16, 193, 164, 158, 102, 16, 254, 214, 31, 142, + 183, 155, 208, 10, 28, 197, 15, 56, 143, 164, 112, 231, 12, 166, 66, 57, 0, 8, 94, 122, 222, + 132, 0, 56, 131, 31, 64, 1, 248, 43, 216, 247, 26, 109, 176, 209, 88, 128, 57, 199, 77, 217, + 120, 225, 8, 126, 0, 5, 224, 38, 37, 241, 114, 193, 217, 15, 132, 85, 144, 230, 7, 167, 42, + 216, 197, 113, 6, 192, 33, 252, 0, 183, 255, 98, 24, 240, 134, 225, 5, 80, 84, 218, 225, 252, + 159, 135, 192, 241, 254, 56, 3, 224, 12, 126, 0, 181, 130, 97, 82, 226, 101, 185, 37, 232, 103, + 159, 111, 149, 186, 25, 142, 109, 205, 152, 18, 109, 231, 7, 80, 0, 250, 134, 227, 5, 72, 82, + 122, 153, 34, 177, 12, 217, 66, 40, 189, 141, 35, 0, 142, 224, 7, 32, 64, 193, 19, 36, 73, + 145, 66, 92, 254, 221, 32, 48, 39, 224, 125, 255, 194, 255, 31, 207, 56, 96, 59, 63, 128, 198, + 129, 246, 202, 66, 39, 50, 58, 67, 27, 20, 148, 217, 62, 17, 207, 162, 200, 118, 126, 0, 5, + 0, 161, 40, 123, 195, 66, 19, 220, 186, 124, 161, 92, 176, 14, 113, 4, 192, 25, 252, 0, 104, + 238, 207, 131, 35, 71, 12, 103, 129, 194, 23, 71, 184, 112, 122, 11, 209, 155, 113, 14, 130, 142, + 224, 7, 156, 79, 119, 133, 130, 215, 0, 58, 125, 127, 39, 216, 112, 82, 149, 255, 205, 196, 91, + 249, 142, 225, 7, 212, 128, 179, 187, 71, 106, 1, 127, 210, 237, 176, 46, 96, 143, 88, 248, 20, + 203, 49, 252, 128, 38, 96, 233, 180, 34, 108, 24, 239, 42, 47, 158, 65, 208, 46, 126, 192, 69, + 73, 54, 20, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, + 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 128, 88, + 191, 128, 249, 1, 204, 15, 72, 98, 126, 128, 220, 18, 152, 31, 192, 252, 0, 46, 92, 184, 112, + 225, 194, 133, 75, 180, 133, 246, 8, 220, 215, 178, 77, 122, 229, 82, 197, 15, 160, 11, 169, 22, + 79, 142, 90, 250, 69, 189, 62, 217, 121, 245, 250, 102, 139, 234, 169, 203, 75, 15, 63, 0, 239, + 21, 224, 253, 67, 124, 168, 226, 234, 61, 93, 60, 219, 105, 146, 24, 56, 99, 83, 233, 226, 7, + 224, 163, 182, 154, 239, 231, 138, 58, 237, 231, 41, 26, 226, 145, 227, 215, 137, 99, 199, 79, 149, + 14, 126, 128, 250, 136, 237, 70, 223, 30, 81, 209, 187, 80, 145, 207, 118, 238, 55, 83, 108, 254, + 57, 175, 116, 240, 3, 84, 187, 217, 183, 75, 84, 242, 46, 16, 46, 183, 95, 188, 208, 110, 130, + 152, 56, 101, 67, 233, 224, 7, 4, 31, 177, 251, 246, 137, 167, 188, 179, 130, 10, 242, 158, 239, + 229, 136, 147, 167, 206, 148, 14, 126, 128, 186, 199, 160, 97, 219, 28, 104, 1, 249, 251, 0, 159, + 109, 157, 38, 218, 248, 87, 136, 214, 196, 14, 40, 185, 252, 128, 30, 121, 202, 222, 130, 148, 239, + 127, 17, 45, 123, 76, 9, 182, 0, 180, 186, 111, 77, 22, 21, 135, 45, 44, 225, 252, 0, 152, + 254, 112, 26, 172, 50, 124, 145, 168, 215, 59, 59, 100, 115, 100, 203, 214, 233, 155, 26, 119, 157, + 216, 190, 68, 243, 3, 232, 153, 65, 78, 181, 207, 231, 188, 145, 236, 75, 75, 211, 166, 219, 117, + 121, 210, 190, 142, 226, 22, 87, 241, 230, 7, 208, 82, 248, 74, 87, 171, 212, 89, 218, 109, 178, + 16, 0, 148, 213, 95, 19, 141, 38, 160, 216, 242, 3, 164, 235, 129, 213, 170, 114, 76, 179, 97, + 250, 191, 24, 160, 34, 212, 167, 248, 240, 3, 36, 231, 81, 59, 56, 221, 96, 135, 184, 31, 142, + 143, 199, 80, 55, 231, 243, 3, 130, 1, 112, 251, 239, 7, 71, 199, 203, 25, 39, 165, 150, 176, + 37, 86, 125, 80, 177, 224, 7, 80, 11, 120, 0, 250, 127, 54, 57, 31, 208, 72, 233, 79, 130, + 229, 196, 65, 146, 112, 54, 63, 0, 11, 56, 127, 3, 56, 184, 150, 24, 34, 1, 3, 150, 192, + 67, 113, 6, 193, 153, 252, 0, 233, 134, 72, 94, 56, 173, 16, 4, 233, 237, 104, 208, 58, 17, + 186, 131, 243, 248, 1, 20, 128, 178, 96, 75, 35, 8, 166, 144, 33, 80, 59, 206, 41, 219, 121, + 252, 0, 10, 0, 38, 92, 156, 105, 148, 115, 152, 36, 116, 216, 66, 222, 51, 97, 209, 230, 44, + 126, 128, 20, 128, 197, 154, 236, 178, 90, 237, 208, 57, 90, 43, 92, 23, 103, 0, 156, 197, 15, + 80, 6, 65, 79, 218, 157, 224, 216, 62, 201, 233, 130, 32, 104, 85, 164, 173, 82, 93, 69, 33, + 76, 25, 4, 193, 57, 252, 0, 106, 1, 215, 130, 29, 213, 85, 139, 186, 11, 229, 26, 70, 169, + 253, 93, 113, 6, 192, 57, 252, 0, 10, 64, 57, 176, 13, 70, 90, 65, 205, 184, 128, 194, 233, + 231, 76, 24, 11, 156, 193, 15, 160, 0, 96, 222, 225, 229, 97, 166, 64, 249, 247, 51, 96, 35, + 75, 12, 63, 128, 2, 80, 27, 108, 163, 180, 240, 9, 24, 200, 102, 213, 215, 127, 65, 177, 101, + 156, 1, 112, 20, 63, 224, 97, 37, 243, 180, 246, 90, 32, 92, 66, 118, 143, 191, 15, 216, 37, + 241, 78, 137, 182, 243, 3, 148, 212, 219, 158, 180, 178, 210, 165, 112, 180, 54, 15, 3, 23, 103, + 0, 236, 231, 7, 72, 99, 192, 118, 3, 98, 68, 64, 151, 35, 224, 246, 31, 6, 107, 23, 207, + 148, 232, 28, 126, 128, 199, 255, 47, 149, 40, 23, 134, 32, 19, 208, 65, 105, 32, 122, 243, 142, + 120, 22, 69, 206, 224, 7, 120, 252, 46, 137, 31, 16, 48, 12, 64, 225, 224, 236, 129, 215, 158, + 143, 35, 0, 246, 243, 3, 240, 18, 23, 156, 168, 161, 133, 42, 6, 167, 62, 205, 64, 40, 45, + 138, 14, 129, 205, 0, 107, 132, 87, 147, 49, 6, 192, 17, 252, 128, 50, 96, 79, 25, 156, 121, + 245, 181, 3, 96, 83, 192, 16, 165, 149, 28, 239, 74, 48, 150, 0, 88, 221, 5, 94, 36, 39, + 243, 155, 181, 199, 159, 9, 246, 54, 209, 38, 111, 179, 240, 41, 150, 99, 248, 1, 15, 128, 85, + 71, 170, 84, 130, 159, 95, 58, 131, 31, 96, 227, 211, 107, 230, 7, 48, 63, 128, 249, 1, 204, + 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, + 126, 0, 243, 3, 152, 31, 16, 219, 151, 48, 63, 128, 249, 1, 204, 15, 96, 126, 128, 220, 18, + 152, 31, 192, 252, 0, 46, 92, 184, 112, 225, 194, 133, 75, 12, 115, 244, 133, 245, 123, 103, 189, + 216, 176, 219, 164, 207, 171, 127, 58, 187, 103, 233, 225, 7, 228, 51, 0, 86, 224, 189, 196, 90, + 3, 102, 139, 166, 61, 179, 69, 243, 193, 11, 74, 25, 63, 96, 244, 54, 229, 161, 139, 171, 255, + 10, 225, 106, 59, 65, 244, 30, 186, 164, 20, 241, 3, 62, 200, 83, 158, 43, 220, 218, 110, 191, + 120, 196, 187, 74, 225, 8, 116, 233, 63, 75, 252, 122, 232, 120, 233, 224, 7, 4, 5, 150, 190, + 61, 226, 113, 239, 114, 101, 115, 244, 127, 58, 103, 138, 101, 171, 118, 151, 14, 126, 128, 106, 127, + 247, 237, 21, 149, 60, 243, 21, 17, 245, 115, 173, 199, 138, 239, 210, 87, 150, 14, 126, 128, 108, + 181, 60, 211, 130, 42, 242, 78, 111, 78, 22, 191, 30, 253, 163, 116, 240, 3, 112, 12, 168, 63, + 224, 128, 112, 117, 204, 12, 217, 37, 254, 106, 218, 10, 225, 129, 190, 93, 178, 249, 1, 163, 182, + 43, 45, 165, 238, 136, 149, 162, 121, 231, 137, 33, 123, 5, 107, 245, 207, 17, 229, 191, 89, 92, + 178, 249, 1, 180, 187, 68, 84, 251, 124, 206, 186, 230, 29, 50, 246, 231, 43, 69, 242, 3, 144, + 236, 75, 155, 214, 176, 251, 164, 6, 37, 154, 31, 64, 78, 93, 69, 27, 35, 87, 107, 182, 204, + 162, 118, 248, 63, 97, 110, 113, 21, 127, 126, 128, 90, 104, 27, 236, 94, 205, 118, 217, 147, 96, + 95, 69, 202, 69, 84, 18, 248, 1, 23, 192, 160, 151, 12, 107, 128, 211, 218, 20, 155, 208, 10, + 150, 68, 155, 127, 188, 88, 242, 3, 40, 0, 151, 17, 44, 65, 79, 48, 133, 251, 136, 95, 40, + 98, 221, 138, 15, 63, 128, 2, 112, 9, 166, 222, 212, 147, 206, 146, 156, 230, 155, 24, 78, 80, + 241, 224, 7, 72, 65, 24, 13, 77, 254, 76, 33, 225, 100, 126, 80, 80, 41, 118, 67, 81, 115, + 140, 22, 11, 126, 0, 57, 143, 25, 168, 103, 129, 157, 13, 163, 25, 140, 41, 19, 157, 227, 249, + 1, 20, 128, 171, 193, 230, 232, 40, 71, 3, 146, 120, 226, 35, 176, 91, 99, 28, 175, 156, 203, + 15, 160, 0, 60, 66, 18, 24, 189, 65, 80, 13, 2, 182, 144, 103, 226, 152, 177, 156, 201, 15, + 160, 0, 212, 160, 92, 162, 129, 48, 210, 89, 84, 147, 116, 138, 115, 221, 226, 60, 126, 0, 5, + 160, 46, 17, 99, 194, 37, 95, 62, 77, 153, 41, 47, 143, 35, 0, 206, 227, 7, 224, 0, 8, + 14, 214, 162, 236, 242, 90, 231, 3, 193, 110, 144, 191, 64, 218, 20, 11, 87, 72, 227, 135, 227, + 248, 1, 23, 145, 252, 77, 43, 149, 15, 104, 166, 66, 65, 9, 154, 219, 199, 25, 0, 199, 241, + 3, 206, 7, 39, 219, 24, 76, 125, 122, 106, 210, 31, 192, 46, 141, 51, 8, 206, 225, 7, 80, + 16, 62, 149, 51, 208, 235, 130, 20, 10, 102, 4, 4, 170, 220, 19, 103, 0, 156, 195, 15, 80, + 2, 224, 246, 143, 85, 168, 81, 250, 206, 235, 137, 42, 95, 137, 115, 48, 116, 20, 63, 224, 34, + 18, 75, 26, 167, 220, 118, 23, 154, 30, 71, 128, 221, 27, 239, 148, 104, 59, 63, 128, 2, 80, + 150, 50, 201, 135, 83, 142, 107, 133, 213, 136, 220, 104, 22, 103, 0, 28, 195, 15, 40, 79, 169, + 182, 195, 13, 124, 218, 4, 236, 7, 97, 76, 232, 31, 239, 56, 224, 20, 126, 64, 21, 176, 149, + 145, 90, 128, 102, 118, 192, 75, 228, 217, 96, 87, 197, 17, 0, 251, 249, 1, 120, 121, 75, 24, + 157, 131, 6, 77, 221, 112, 90, 132, 214, 176, 15, 236, 233, 56, 2, 224, 16, 126, 128, 199, 95, + 193, 144, 32, 163, 15, 85, 8, 208, 173, 178, 63, 225, 248, 102, 28, 1, 176, 159, 31, 128, 5, + 206, 98, 10, 216, 185, 66, 119, 131, 10, 198, 129, 223, 193, 214, 129, 33, 116, 241, 107, 176, 30, + 96, 255, 38, 197, 249, 53, 69, 189, 73, 18, 75, 0, 172, 230, 7, 248, 192, 217, 41, 202, 29, + 33, 143, 255, 61, 176, 214, 116, 243, 3, 33, 138, 87, 91, 248, 20, 203, 25, 252, 0, 27, 31, + 222, 50, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, + 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 196, 244, 37, + 204, 15, 96, 126, 0, 243, 3, 152, 31, 32, 183, 4, 230, 7, 48, 63, 128, 11, 23, 46, 92, + 184, 112, 225, 18, 195, 28, 125, 33, 173, 40, 27, 84, 25, 52, 175, 115, 131, 158, 153, 147, 235, + 190, 53, 121, 66, 169, 226, 7, 224, 67, 88, 188, 176, 170, 63, 124, 165, 104, 209, 45, 91, 36, + 191, 155, 83, 202, 248, 1, 67, 247, 41, 79, 159, 154, 124, 180, 67, 184, 124, 25, 226, 229, 238, + 217, 202, 181, 124, 169, 224, 7, 4, 159, 53, 248, 14, 136, 178, 190, 205, 138, 122, 244, 223, 237, + 199, 139, 61, 112, 29, 95, 42, 248, 1, 65, 249, 141, 47, 31, 164, 128, 123, 132, 158, 109, 157, + 33, 230, 46, 218, 86, 58, 248, 1, 114, 0, 170, 120, 231, 5, 55, 74, 13, 29, 181, 188, 244, + 240, 3, 84, 171, 231, 201, 14, 10, 168, 95, 235, 155, 45, 142, 157, 60, 83, 10, 248, 1, 3, + 243, 148, 189, 199, 41, 223, 236, 16, 201, 208, 244, 93, 210, 246, 88, 111, 214, 186, 146, 207, 15, + 192, 173, 54, 120, 59, 237, 169, 175, 22, 138, 150, 190, 244, 144, 253, 130, 79, 125, 50, 75, 148, + 27, 185, 180, 100, 243, 3, 168, 175, 14, 172, 223, 39, 235, 91, 151, 215, 127, 84, 222, 52, 217, + 162, 221, 216, 111, 171, 124, 53, 175, 97, 137, 230, 7, 168, 159, 7, 14, 15, 164, 221, 161, 114, + 43, 152, 167, 77, 186, 84, 226, 248, 1, 82, 0, 178, 73, 34, 39, 239, 17, 62, 12, 246, 4, + 42, 204, 12, 234, 81, 188, 249, 1, 146, 243, 215, 129, 45, 210, 106, 6, 113, 199, 56, 24, 238, + 15, 190, 44, 66, 125, 138, 39, 63, 64, 10, 64, 101, 176, 85, 6, 122, 129, 1, 112, 188, 57, + 202, 186, 21, 47, 126, 128, 20, 128, 23, 11, 41, 72, 221, 193, 76, 212, 57, 240, 251, 99, 69, + 168, 99, 241, 226, 7, 80, 0, 122, 129, 229, 26, 8, 38, 118, 210, 46, 242, 162, 212, 179, 120, + 240, 3, 200, 121, 20, 79, 12, 138, 144, 128, 181, 125, 36, 160, 138, 65, 16, 156, 205, 15, 160, + 0, 220, 12, 54, 70, 71, 57, 34, 15, 136, 95, 197, 146, 131, 208, 241, 252, 0, 10, 0, 170, + 199, 166, 25, 40, 197, 84, 17, 245, 116, 176, 170, 49, 14, 218, 206, 229, 7, 40, 1, 112, 251, + 27, 107, 65, 74, 65, 5, 57, 201, 105, 224, 184, 21, 222, 247, 82, 28, 211, 182, 51, 249, 1, + 212, 2, 16, 163, 243, 107, 184, 132, 203, 16, 128, 19, 112, 236, 23, 71, 162, 85, 231, 241, 3, + 200, 249, 75, 193, 186, 232, 13, 128, 164, 29, 14, 72, 162, 42, 63, 216, 223, 227, 104, 5, 206, + 226, 7, 80, 0, 110, 4, 251, 56, 66, 186, 109, 148, 203, 97, 32, 16, 181, 81, 45, 142, 0, + 56, 139, 31, 64, 1, 184, 15, 108, 108, 152, 204, 211, 90, 166, 144, 55, 206, 107, 24, 199, 241, + 3, 202, 133, 145, 207, 202, 205, 31, 23, 70, 120, 165, 56, 40, 206, 0, 56, 140, 31, 0, 83, + 155, 14, 64, 33, 160, 167, 31, 166, 233, 112, 94, 164, 11, 163, 8, 1, 112, 20, 63, 0, 57, + 98, 207, 70, 232, 255, 5, 203, 226, 252, 110, 130, 201, 218, 203, 199, 217, 10, 28, 195, 15, 64, + 138, 84, 31, 35, 86, 128, 70, 60, 29, 160, 153, 225, 128, 9, 227, 128, 99, 248, 1, 215, 210, + 212, 166, 199, 9, 48, 106, 5, 184, 30, 24, 17, 239, 56, 224, 12, 126, 128, 91, 9, 192, 70, + 77, 166, 249, 64, 24, 245, 56, 6, 1, 197, 214, 235, 138, 122, 97, 164, 93, 20, 57, 129, 31, + 128, 20, 185, 219, 9, 157, 169, 11, 76, 33, 134, 72, 225, 64, 184, 21, 166, 208, 61, 113, 4, + 192, 17, 252, 0, 12, 64, 45, 109, 147, 15, 98, 115, 220, 33, 8, 157, 80, 152, 138, 91, 89, + 53, 198, 115, 93, 224, 12, 126, 0, 56, 241, 6, 56, 249, 103, 24, 124, 6, 66, 22, 143, 128, + 237, 131, 64, 252, 12, 182, 22, 126, 94, 162, 208, 229, 220, 10, 78, 167, 140, 213, 1, 176, 154, + 31, 80, 22, 2, 80, 141, 22, 67, 15, 128, 221, 129, 107, 125, 176, 43, 193, 44, 123, 194, 203, + 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, + 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 128, 24, 191, + 132, 249, 1, 204, 15, 96, 126, 0, 243, 3, 228, 150, 192, 252, 0, 230, 7, 112, 225, 194, 133, + 11, 23, 46, 92, 98, 152, 163, 131, 252, 0, 98, 5, 160, 0, 186, 39, 50, 4, 144, 37, 128, + 76, 129, 82, 195, 15, 192, 199, 241, 184, 39, 1, 55, 102, 52, 127, 107, 134, 104, 217, 35, 75, + 52, 79, 93, 94, 122, 248, 1, 184, 23, 1, 55, 100, 224, 174, 148, 22, 237, 179, 197, 115, 109, + 51, 68, 250, 202, 93, 165, 132, 31, 32, 223, 89, 242, 109, 23, 77, 91, 101, 40, 219, 101, 54, + 108, 206, 21, 103, 207, 158, 43, 249, 252, 0, 217, 30, 244, 174, 17, 45, 61, 233, 202, 102, 169, + 156, 185, 91, 197, 233, 211, 103, 74, 62, 63, 64, 54, 228, 8, 36, 227, 46, 49, 176, 33, 208, + 101, 140, 2, 80, 226, 248, 1, 249, 119, 151, 97, 44, 240, 102, 6, 119, 140, 117, 239, 63, 173, + 116, 240, 3, 112, 223, 17, 110, 190, 106, 241, 205, 198, 144, 45, 179, 207, 182, 30, 43, 222, 93, + 248, 75, 201, 231, 7, 40, 55, 83, 191, 95, 170, 48, 3, 148, 237, 241, 202, 198, 73, 191, 104, + 217, 38, 93, 60, 61, 100, 65, 201, 231, 7, 40, 187, 183, 190, 154, 215, 176, 121, 219, 244, 111, + 101, 5, 9, 50, 5, 144, 45, 80, 226, 249, 1, 88, 80, 28, 73, 162, 8, 121, 127, 240, 239, + 16, 144, 129, 154, 91, 92, 37, 146, 31, 112, 30, 52, 253, 39, 192, 217, 195, 154, 109, 179, 200, + 20, 200, 54, 168, 71, 201, 224, 7, 80, 0, 46, 131, 193, 239, 223, 6, 89, 104, 145, 45, 112, + 93, 152, 250, 20, 111, 126, 0, 5, 0, 53, 196, 3, 130, 98, 169, 80, 29, 1, 178, 5, 42, + 71, 81, 183, 226, 201, 15, 160, 0, 60, 6, 150, 99, 176, 123, 124, 11, 4, 228, 197, 40, 235, + 88, 44, 249, 1, 104, 13, 97, 12, 216, 105, 16, 128, 92, 8, 64, 175, 34, 212, 179, 248, 240, + 3, 176, 64, 147, 191, 136, 24, 1, 1, 131, 228, 139, 231, 136, 49, 80, 166, 136, 65, 112, 62, + 63, 64, 105, 1, 110, 255, 109, 112, 246, 191, 50, 18, 77, 210, 120, 48, 38, 90, 150, 136, 166, + 59, 56, 155, 31, 64, 45, 160, 42, 216, 116, 73, 46, 175, 167, 26, 155, 22, 139, 102, 208, 241, + 252, 0, 26, 3, 94, 2, 219, 138, 226, 41, 21, 156, 160, 211, 13, 86, 35, 107, 32, 198, 153, + 203, 209, 252, 128, 243, 21, 54, 64, 190, 38, 48, 92, 254, 97, 100, 12, 252, 55, 198, 0, 56, + 147, 31, 64, 1, 64, 157, 144, 63, 255, 2, 40, 45, 92, 210, 229, 115, 196, 26, 184, 52, 198, + 32, 56, 143, 31, 64, 1, 64, 225, 212, 66, 149, 23, 82, 40, 223, 112, 168, 33, 107, 224, 198, + 24, 3, 224, 60, 126, 0, 5, 192, 11, 77, 254, 151, 16, 237, 160, 126, 230, 121, 161, 176, 6, + 60, 254, 251, 226, 88, 194, 59, 139, 31, 64, 1, 24, 68, 108, 0, 237, 242, 87, 79, 75, 136, + 172, 129, 114, 113, 4, 192, 113, 252, 128, 203, 130, 151, 191, 238, 16, 90, 132, 81, 50, 230, 63, + 98, 197, 233, 72, 151, 210, 206, 224, 7, 80, 0, 202, 19, 19, 64, 20, 193, 144, 57, 112, 73, + 28, 65, 112, 6, 63, 32, 216, 255, 61, 254, 3, 146, 70, 88, 68, 202, 70, 175, 48, 7, 220, + 177, 103, 166, 118, 12, 63, 128, 2, 48, 2, 236, 68, 4, 199, 133, 148, 164, 93, 197, 233, 92, + 27, 207, 56, 224, 12, 126, 128, 199, 143, 23, 64, 235, 144, 9, 16, 162, 28, 55, 30, 4, 85, + 67, 230, 64, 60, 1, 176, 159, 31, 64, 1, 184, 135, 88, 0, 114, 243, 15, 232, 224, 51, 180, + 118, 146, 216, 3, 231, 197, 24, 0, 251, 249, 1, 210, 250, 255, 156, 238, 217, 14, 63, 29, 162, + 213, 138, 35, 0, 246, 243, 3, 240, 186, 30, 206, 242, 43, 10, 11, 32, 159, 9, 176, 22, 25, + 1, 200, 10, 64, 102, 0, 116, 135, 51, 210, 69, 81, 64, 19, 24, 100, 14, 188, 17, 71, 23, + 112, 6, 63, 32, 138, 32, 93, 72, 44, 129, 191, 19, 91, 224, 1, 98, 13, 224, 210, 185, 108, + 28, 1, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, + 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, + 64, 44, 95, 194, 252, 0, 230, 7, 48, 63, 128, 249, 1, 114, 75, 96, 126, 0, 243, 3, 184, + 112, 225, 194, 133, 11, 23, 46, 49, 204, 209, 186, 252, 0, 178, 206, 213, 63, 157, 221, 179, 97, + 183, 73, 159, 215, 239, 157, 245, 98, 169, 226, 7, 160, 41, 63, 127, 182, 72, 52, 237, 149, 45, + 234, 124, 50, 171, 20, 241, 3, 62, 206, 55, 252, 185, 121, 175, 197, 34, 165, 221, 4, 241, 86, + 234, 143, 165, 135, 31, 160, 218, 205, 190, 93, 162, 166, 39, 71, 217, 65, 150, 51, 103, 139, 248, + 253, 196, 169, 210, 193, 15, 8, 222, 114, 247, 109, 16, 141, 61, 19, 149, 253, 65, 63, 76, 221, + 40, 142, 30, 59, 89, 58, 248, 1, 170, 85, 244, 46, 204, 7, 41, 64, 0, 134, 67, 23, 56, + 118, 252, 84, 41, 224, 7, 116, 200, 223, 138, 87, 246, 181, 125, 162, 33, 114, 4, 104, 203, 220, + 59, 3, 103, 149, 18, 126, 192, 152, 159, 69, 243, 209, 91, 68, 139, 175, 87, 7, 37, 244, 24, + 128, 87, 123, 100, 138, 25, 219, 127, 45, 225, 252, 128, 252, 187, 201, 162, 252, 55, 139, 69, 173, + 247, 114, 130, 206, 163, 181, 232, 60, 65, 52, 73, 93, 94, 178, 249, 1, 244, 64, 227, 222, 134, + 221, 39, 53, 72, 246, 165, 133, 164, 226, 109, 222, 33, 99, 127, 181, 207, 231, 172, 43, 209, 252, + 0, 181, 128, 195, 255, 41, 148, 139, 60, 63, 53, 111, 211, 18, 203, 15, 144, 156, 199, 93, 228, + 95, 169, 137, 216, 36, 219, 11, 150, 172, 169, 71, 201, 225, 7, 72, 1, 184, 155, 246, 15, 107, + 55, 72, 159, 118, 181, 74, 77, 134, 227, 5, 58, 245, 41, 254, 252, 0, 41, 0, 47, 192, 212, + 119, 192, 64, 68, 241, 223, 112, 57, 71, 139, 53, 63, 64, 10, 192, 55, 114, 34, 102, 13, 76, + 161, 47, 252, 126, 73, 132, 58, 22, 63, 126, 0, 57, 142, 118, 3, 56, 248, 139, 129, 70, 0, + 211, 239, 141, 142, 178, 158, 197, 139, 31, 64, 1, 184, 24, 33, 10, 97, 132, 18, 103, 73, 95, + 112, 94, 17, 130, 80, 60, 248, 1, 20, 128, 91, 225, 236, 127, 100, 168, 29, 206, 239, 6, 115, + 48, 91, 109, 17, 90, 108, 241, 224, 7, 80, 0, 158, 161, 51, 92, 88, 62, 87, 240, 251, 12, + 176, 71, 138, 56, 110, 57, 159, 31, 64, 1, 232, 4, 182, 39, 130, 132, 30, 69, 214, 53, 98, + 152, 185, 156, 203, 15, 32, 231, 47, 7, 75, 135, 230, 126, 90, 23, 161, 33, 211, 100, 60, 254, + 186, 49, 4, 192, 185, 252, 0, 10, 192, 227, 96, 155, 12, 20, 99, 242, 241, 120, 172, 138, 49, + 199, 242, 3, 40, 0, 72, 144, 57, 22, 2, 81, 201, 215, 14, 234, 205, 6, 141, 98, 73, 188, + 236, 100, 126, 192, 165, 96, 63, 104, 4, 147, 1, 41, 33, 179, 54, 8, 109, 16, 187, 17, 227, + 42, 214, 145, 252, 128, 123, 192, 242, 36, 229, 104, 64, 183, 27, 228, 75, 235, 255, 128, 32, 125, + 26, 199, 53, 140, 227, 248, 1, 56, 248, 189, 18, 116, 88, 79, 49, 234, 14, 97, 11, 225, 21, + 226, 216, 56, 2, 224, 56, 126, 192, 189, 164, 32, 151, 23, 62, 1, 194, 104, 20, 102, 10, 229, + 7, 35, 51, 206, 228, 235, 142, 226, 7, 52, 3, 91, 110, 4, 79, 210, 53, 183, 127, 66, 156, + 202, 81, 231, 240, 3, 160, 63, 247, 7, 103, 14, 26, 128, 18, 140, 2, 48, 53, 22, 162, 148, + 60, 14, 56, 133, 31, 112, 21, 156, 245, 217, 33, 151, 190, 225, 101, 243, 106, 0, 16, 164, 82, + 37, 142, 0, 56, 132, 31, 224, 246, 63, 77, 106, 113, 35, 71, 245, 90, 68, 128, 90, 12, 138, + 168, 99, 13, 128, 99, 248, 1, 111, 146, 20, 62, 28, 47, 196, 232, 231, 10, 69, 65, 235, 105, + 2, 224, 8, 126, 0, 246, 255, 107, 224, 88, 29, 12, 57, 162, 61, 192, 190, 70, 120, 42, 97, + 53, 126, 215, 220, 9, 18, 210, 204, 128, 93, 38, 37, 142, 46, 80, 108, 248, 1, 87, 131, 61, + 68, 55, 73, 90, 131, 189, 135, 119, 132, 192, 166, 128, 249, 226, 8, 0, 243, 3, 152, 31, 192, + 252, 0, 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 152, 31, 192, 252, 0, + 230, 7, 48, 63, 128, 249, 1, 204, 15, 96, 126, 0, 243, 3, 98, 249, 18, 230, 7, 48, 63, + 128, 249, 1, 204, 15, 144, 91, 2, 243, 3, 152, 31, 192, 133, 11, 23, 46, 92, 184, 112, 137, + 97, 142, 14, 203, 15, 160, 215, 240, 111, 15, 180, 108, 147, 94, 25, 19, 45, 197, 186, 81, 210, + 41, 14, 23, 137, 31, 128, 175, 225, 223, 170, 140, 252, 81, 212, 237, 147, 37, 234, 245, 201, 206, + 123, 114, 212, 210, 47, 74, 13, 63, 160, 17, 188, 134, 127, 115, 125, 178, 89, 164, 116, 152, 36, + 254, 211, 63, 167, 244, 240, 3, 84, 251, 187, 111, 175, 120, 194, 187, 76, 36, 123, 210, 197, 128, + 225, 139, 197, 98, 184, 172, 45, 21, 252, 128, 224, 190, 36, 223, 22, 81, 203, 51, 77, 217, 43, + 244, 253, 216, 149, 226, 8, 61, 217, 45, 241, 252, 0, 213, 30, 247, 46, 23, 77, 61, 227, 149, + 0, 124, 227, 95, 33, 14, 253, 118, 162, 228, 243, 3, 112, 55, 26, 254, 140, 205, 191, 174, 103, + 114, 112, 183, 216, 192, 33, 11, 197, 201, 83, 103, 74, 56, 63, 96, 204, 86, 197, 90, 140, 222, + 42, 254, 191, 189, 243, 128, 115, 162, 218, 254, 248, 162, 239, 169, 79, 253, 63, 223, 179, 247, 94, + 158, 229, 217, 17, 21, 41, 74, 239, 176, 176, 187, 32, 246, 231, 144, 13, 85, 138, 32, 29, 65, + 193, 174, 8, 98, 3, 68, 80, 201, 46, 75, 223, 165, 87, 233, 168, 128, 72, 81, 4, 41, 210, + 165, 73, 145, 34, 228, 252, 207, 153, 61, 179, 123, 51, 153, 73, 38, 201, 194, 206, 196, 115, 63, + 159, 243, 153, 36, 155, 77, 238, 57, 115, 219, 220, 204, 239, 123, 210, 250, 47, 131, 140, 38, 124, + 31, 177, 63, 27, 186, 190, 61, 61, 249, 249, 1, 134, 61, 52, 112, 30, 84, 125, 121, 66, 104, + 26, 190, 30, 19, 160, 227, 172, 159, 146, 155, 31, 192, 55, 51, 94, 89, 187, 253, 152, 150, 13, + 154, 102, 255, 168, 222, 69, 138, 235, 0, 40, 243, 217, 130, 228, 230, 7, 24, 133, 239, 30, 221, + 171, 6, 32, 205, 159, 149, 85, 182, 223, 172, 14, 73, 203, 15, 96, 199, 201, 46, 224, 27, 163, + 67, 213, 35, 154, 46, 174, 60, 143, 235, 145, 124, 252, 0, 14, 192, 121, 216, 231, 159, 85, 132, + 20, 70, 16, 142, 227, 243, 101, 22, 245, 73, 30, 126, 0, 7, 224, 62, 116, 52, 96, 115, 23, + 249, 84, 180, 139, 109, 234, 230, 125, 126, 0, 7, 160, 121, 122, 147, 225, 107, 44, 133, 212, 90, + 96, 20, 62, 190, 61, 66, 29, 189, 201, 15, 80, 156, 63, 13, 109, 10, 51, 68, 194, 116, 131, + 24, 24, 186, 165, 254, 142, 40, 245, 244, 30, 63, 64, 113, 254, 78, 83, 147, 55, 75, 232, 150, + 19, 104, 193, 65, 125, 189, 197, 15, 224, 0, 92, 142, 103, 184, 167, 133, 116, 38, 168, 8, 170, + 119, 58, 221, 16, 241, 20, 63, 128, 3, 80, 25, 109, 182, 109, 2, 230, 252, 113, 96, 81, 44, + 178, 57, 207, 240, 3, 56, 0, 125, 248, 12, 135, 167, 220, 45, 12, 202, 116, 180, 135, 98, 28, + 188, 221, 205, 15, 96, 231, 47, 102, 90, 212, 113, 59, 21, 25, 7, 96, 65, 28, 1, 112, 61, + 63, 224, 239, 232, 92, 186, 154, 122, 87, 113, 60, 104, 210, 21, 110, 197, 247, 222, 16, 199, 250, + 197, 213, 252, 128, 27, 209, 134, 170, 35, 127, 129, 118, 24, 157, 54, 173, 6, 247, 199, 147, 121, + 218, 181, 252, 0, 14, 64, 35, 180, 159, 212, 81, 223, 106, 16, 100, 233, 220, 74, 18, 78, 198, + 185, 138, 117, 37, 63, 128, 248, 65, 195, 24, 148, 4, 14, 116, 196, 36, 182, 174, 22, 103, 0, + 220, 197, 15, 224, 0, 144, 10, 116, 93, 52, 205, 176, 66, 147, 88, 69, 211, 101, 156, 1, 112, + 29, 63, 224, 44, 180, 110, 142, 100, 243, 133, 118, 4, 237, 174, 4, 174, 100, 93, 197, 15, 184, + 11, 207, 236, 215, 22, 83, 94, 48, 4, 168, 80, 56, 16, 26, 151, 196, 55, 37, 32, 158, 118, + 7, 63, 128, 83, 107, 183, 64, 103, 247, 198, 216, 2, 214, 199, 59, 6, 24, 227, 128, 91, 248, + 1, 215, 163, 243, 227, 44, 251, 187, 153, 35, 16, 42, 162, 94, 141, 199, 199, 19, 8, 128, 107, + 248, 1, 141, 117, 116, 150, 22, 253, 172, 155, 166, 68, 66, 107, 166, 39, 16, 128, 226, 231, 7, + 208, 213, 28, 3, 145, 8, 142, 182, 219, 140, 204, 137, 194, 19, 33, 220, 86, 5, 79, 243, 3, + 108, 86, 130, 196, 11, 237, 205, 242, 248, 237, 38, 160, 146, 57, 8, 229, 79, 69, 0, 138, 155, + 31, 112, 13, 97, 116, 209, 122, 50, 58, 103, 51, 143, 7, 20, 156, 167, 78, 69, 23, 112, 29, + 63, 0, 29, 191, 130, 169, 19, 119, 20, 229, 32, 40, 252, 0, 225, 7, 8, 63, 64, 248, 1, + 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 56, 249, 105, 76, 248, 1, 194, + 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 80, + 91, 130, 240, 3, 132, 31, 32, 69, 138, 20, 41, 82, 164, 72, 137, 99, 142, 118, 204, 15, 48, + 118, 109, 232, 254, 34, 47, 59, 28, 31, 63, 0, 223, 195, 251, 142, 223, 61, 246, 230, 180, 113, + 53, 59, 142, 251, 180, 78, 187, 209, 247, 122, 201, 241, 196, 248, 1, 248, 30, 253, 189, 36, 175, + 237, 62, 21, 234, 118, 201, 133, 210, 159, 206, 253, 107, 240, 3, 10, 182, 221, 90, 108, 135, 7, + 94, 90, 15, 13, 50, 71, 192, 179, 237, 199, 253, 117, 248, 1, 5, 1, 240, 111, 132, 82, 190, + 249, 122, 78, 242, 62, 239, 207, 130, 173, 123, 14, 254, 53, 248, 1, 134, 221, 238, 255, 1, 170, + 251, 242, 244, 27, 165, 62, 25, 182, 24, 118, 238, 58, 144, 252, 252, 0, 245, 14, 181, 71, 125, + 211, 33, 77, 203, 191, 99, 236, 19, 28, 75, 246, 154, 126, 230, 74, 62, 126, 0, 62, 166, 215, + 50, 134, 224, 223, 222, 94, 5, 105, 153, 35, 245, 230, 255, 120, 179, 17, 208, 247, 211, 249, 201, + 207, 15, 208, 13, 95, 43, 51, 112, 1, 212, 238, 62, 177, 240, 62, 193, 204, 0, 116, 253, 104, + 14, 108, 218, 119, 40, 185, 249, 1, 252, 184, 92, 181, 238, 121, 131, 27, 52, 203, 222, 175, 222, + 44, 89, 251, 237, 233, 240, 84, 238, 242, 228, 230, 7, 240, 170, 239, 114, 180, 176, 155, 170, 43, + 245, 158, 188, 186, 212, 208, 133, 201, 201, 15, 80, 156, 167, 91, 234, 159, 203, 208, 2, 191, 133, + 104, 9, 52, 93, 91, 248, 82, 210, 242, 3, 148, 0, 144, 48, 98, 178, 145, 148, 77, 185, 141, + 158, 192, 10, 45, 148, 250, 36, 23, 63, 128, 157, 39, 33, 117, 38, 158, 237, 63, 44, 116, 3, + 71, 57, 7, 89, 9, 83, 221, 146, 131, 31, 192, 1, 184, 23, 109, 146, 162, 32, 15, 154, 2, + 240, 160, 85, 2, 102, 207, 243, 3, 148, 179, 223, 29, 237, 183, 48, 9, 93, 126, 16, 72, 53, + 82, 55, 66, 61, 189, 201, 15, 80, 2, 112, 51, 59, 105, 173, 28, 209, 244, 156, 132, 53, 163, + 212, 215, 123, 252, 0, 118, 254, 124, 180, 246, 166, 36, 139, 102, 219, 233, 228, 150, 121, 207, 241, + 3, 56, 0, 85, 117, 134, 64, 33, 51, 4, 44, 164, 52, 219, 12, 148, 142, 131, 32, 120, 138, + 31, 64, 125, 255, 83, 180, 61, 17, 101, 180, 90, 96, 102, 44, 226, 73, 175, 240, 3, 78, 99, + 237, 208, 1, 7, 185, 135, 9, 164, 114, 79, 12, 1, 112, 55, 63, 128, 3, 112, 45, 139, 166, + 130, 97, 208, 132, 112, 174, 192, 2, 59, 150, 80, 132, 32, 184, 151, 31, 192, 1, 104, 108, 164, + 225, 182, 104, 250, 65, 37, 16, 68, 152, 152, 103, 181, 6, 136, 18, 0, 247, 242, 3, 56, 0, + 185, 104, 7, 29, 202, 103, 9, 182, 112, 126, 28, 171, 88, 215, 242, 3, 202, 20, 140, 246, 90, + 88, 158, 225, 160, 5, 75, 224, 61, 82, 144, 197, 17, 0, 23, 242, 3, 180, 192, 237, 104, 159, + 56, 72, 188, 110, 172, 4, 233, 194, 104, 32, 5, 46, 142, 0, 184, 139, 31, 192, 45, 160, 121, + 1, 62, 35, 146, 118, 184, 176, 133, 252, 129, 54, 48, 129, 43, 89, 119, 240, 3, 244, 244, 219, + 190, 172, 51, 240, 184, 216, 22, 157, 99, 223, 18, 186, 39, 16, 0, 151, 240, 3, 180, 192, 57, + 36, 131, 199, 32, 28, 82, 207, 176, 186, 244, 101, 108, 134, 85, 215, 240, 161, 157, 19, 103, 0, + 92, 194, 15, 208, 2, 247, 163, 19, 163, 109, 174, 246, 130, 182, 52, 169, 38, 195, 15, 96, 96, + 90, 38, 208, 2, 92, 194, 15, 240, 5, 90, 161, 237, 112, 48, 248, 153, 255, 78, 252, 128, 182, + 9, 4, 160, 248, 249, 1, 74, 16, 30, 65, 123, 153, 23, 54, 209, 240, 57, 198, 107, 251, 104, + 209, 148, 64, 0, 220, 199, 15, 224, 96, 92, 136, 205, 187, 33, 30, 7, 161, 109, 44, 88, 10, + 211, 62, 160, 22, 134, 215, 120, 48, 222, 28, 3, 94, 226, 7, 220, 141, 214, 142, 183, 197, 78, + 40, 173, 128, 56, 2, 21, 78, 69, 23, 112, 13, 63, 128, 87, 139, 53, 121, 5, 152, 141, 173, + 160, 78, 81, 14, 130, 158, 227, 7, 96, 16, 206, 72, 32, 0, 194, 15, 16, 126, 128, 240, 3, + 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, + 32, 252, 0, 225, 7, 8, 63, 32, 190, 47, 17, 126, 128, 240, 3, 132, 31, 32, 252, 0, 181, + 37, 8, 63, 64, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 56, 230, 232, 152, 249, 1, 94, + 119, 56, 97, 126, 0, 255, 47, 125, 198, 255, 101, 248, 2, 87, 121, 197, 241, 34, 227, 7, 24, + 226, 203, 210, 31, 206, 129, 234, 93, 115, 127, 170, 214, 61, 239, 45, 55, 55, 239, 162, 227, 7, + 180, 204, 255, 45, 130, 254, 151, 62, 35, 163, 199, 215, 144, 209, 49, 23, 170, 12, 248, 250, 175, + 193, 15, 40, 144, 234, 248, 183, 193, 245, 254, 181, 144, 230, 203, 134, 198, 45, 70, 194, 224, 111, + 55, 252, 53, 248, 1, 134, 93, 135, 206, 151, 201, 156, 173, 223, 48, 245, 242, 219, 211, 97, 199, + 158, 131, 127, 13, 126, 128, 254, 24, 207, 126, 201, 204, 69, 80, 71, 27, 173, 223, 53, 246, 233, + 23, 223, 192, 142, 223, 14, 36, 55, 63, 160, 225, 231, 191, 234, 239, 169, 254, 206, 14, 120, 176, + 195, 90, 168, 217, 100, 156, 126, 75, 109, 67, 127, 54, 12, 250, 234, 91, 56, 124, 248, 88, 114, + 243, 3, 232, 111, 244, 158, 170, 159, 45, 129, 250, 61, 166, 22, 220, 51, 248, 116, 171, 28, 248, + 106, 212, 178, 228, 231, 7, 232, 127, 27, 182, 168, 111, 133, 55, 167, 174, 169, 239, 15, 213, 17, + 190, 156, 179, 20, 54, 236, 61, 148, 188, 252, 0, 163, 232, 170, 146, 252, 132, 75, 5, 206, 167, + 53, 205, 130, 199, 6, 206, 131, 134, 185, 203, 147, 147, 31, 160, 56, 95, 130, 244, 132, 216, 239, + 127, 83, 239, 35, 198, 0, 252, 80, 254, 221, 25, 211, 146, 150, 31, 160, 59, 175, 5, 254, 193, + 55, 85, 31, 86, 157, 231, 123, 134, 73, 97, 122, 103, 82, 242, 3, 148, 179, 95, 10, 237, 11, + 38, 71, 168, 98, 10, 82, 153, 124, 99, 170, 91, 242, 240, 3, 148, 0, 188, 102, 206, 62, 167, + 216, 71, 104, 103, 155, 234, 232, 125, 126, 128, 210, 239, 203, 226, 89, 255, 57, 68, 53, 18, 74, + 147, 104, 107, 37, 162, 246, 60, 63, 128, 251, 62, 165, 219, 251, 42, 66, 58, 78, 26, 16, 91, + 71, 168, 175, 55, 249, 1, 124, 246, 41, 29, 111, 19, 150, 200, 217, 9, 168, 55, 225, 241, 153, + 40, 245, 246, 30, 63, 128, 10, 14, 112, 85, 56, 29, 103, 36, 29, 17, 101, 170, 190, 51, 90, + 250, 93, 79, 241, 3, 248, 236, 159, 129, 205, 60, 11, 143, 123, 109, 50, 207, 6, 185, 27, 172, + 64, 187, 210, 233, 216, 229, 122, 126, 0, 59, 79, 91, 92, 53, 208, 254, 116, 160, 34, 27, 129, + 118, 187, 195, 0, 184, 159, 31, 192, 1, 40, 135, 103, 119, 148, 197, 148, 23, 84, 206, 190, 49, + 27, 12, 143, 133, 33, 224, 122, 126, 0, 7, 224, 3, 116, 112, 183, 85, 0, 20, 69, 41, 5, + 225, 48, 62, 31, 18, 227, 26, 198, 189, 252, 0, 158, 243, 9, 158, 242, 171, 42, 159, 13, 33, + 200, 104, 97, 74, 210, 214, 177, 74, 231, 92, 201, 15, 224, 0, 220, 130, 54, 214, 166, 217, 171, + 193, 80, 91, 66, 141, 88, 249, 194, 238, 228, 7, 228, 15, 124, 109, 245, 172, 242, 249, 163, 123, + 208, 129, 148, 126, 53, 30, 99, 70, 233, 186, 149, 31, 80, 79, 151, 206, 106, 150, 121, 198, 237, + 22, 65, 203, 48, 88, 143, 198, 121, 49, 231, 14, 126, 0, 59, 127, 54, 218, 4, 70, 231, 196, + 194, 15, 216, 149, 128, 124, 222, 37, 252, 0, 95, 224, 223, 120, 182, 27, 133, 73, 228, 11, 31, + 7, 149, 181, 127, 208, 52, 16, 18, 97, 242, 220, 56, 3, 224, 18, 126, 64, 254, 168, 63, 201, + 114, 181, 23, 189, 59, 188, 137, 118, 89, 156, 1, 112, 13, 63, 224, 14, 116, 240, 75, 203, 129, + 174, 240, 138, 207, 28, 132, 32, 3, 22, 95, 75, 64, 61, 238, 30, 126, 0, 7, 226, 98, 194, + 99, 90, 48, 4, 236, 140, 250, 255, 75, 9, 108, 231, 185, 147, 31, 160, 180, 138, 94, 120, 252, + 57, 194, 116, 120, 4, 173, 90, 180, 171, 192, 162, 8, 64, 113, 243, 3, 30, 67, 251, 24, 237, + 119, 83, 0, 8, 165, 119, 107, 2, 45, 192, 91, 252, 0, 125, 153, 172, 5, 26, 42, 192, 149, + 25, 104, 15, 37, 16, 0, 239, 242, 3, 208, 241, 75, 208, 42, 197, 131, 209, 138, 103, 26, 20, + 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, + 240, 3, 132, 31, 32, 252, 128, 130, 190, 36, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, + 72, 17, 126, 128, 241, 5, 194, 15, 16, 126, 64, 138, 240, 3, 164, 72, 145, 34, 69, 138, 20, + 41, 113, 207, 209, 194, 15, 72, 132, 31, 224, 37, 199, 139, 156, 31, 192, 251, 14, 36, 139, 173, + 159, 225, 11, 252, 39, 158, 228, 75, 167, 162, 121, 159, 52, 126, 0, 125, 22, 125, 102, 249, 65, + 139, 160, 90, 183, 188, 253, 53, 58, 143, 159, 80, 165, 231, 196, 115, 220, 226, 252, 73, 227, 7, + 24, 70, 159, 245, 104, 239, 29, 144, 214, 125, 62, 52, 106, 63, 30, 106, 188, 59, 227, 175, 193, + 15, 48, 118, 164, 47, 245, 111, 133, 155, 50, 127, 130, 6, 77, 2, 208, 184, 249, 8, 24, 186, + 240, 151, 191, 6, 63, 192, 120, 126, 139, 127, 53, 60, 234, 155, 174, 223, 60, 213, 235, 221, 153, + 176, 115, 247, 193, 36, 231, 7, 188, 155, 63, 110, 208, 255, 94, 238, 223, 12, 229, 155, 204, 128, + 6, 190, 108, 61, 0, 195, 70, 44, 133, 93, 232, 124, 82, 243, 3, 232, 189, 244, 63, 233, 67, + 215, 65, 253, 110, 139, 160, 126, 230, 72, 221, 249, 231, 91, 143, 134, 236, 113, 63, 36, 57, 63, + 32, 255, 61, 243, 30, 24, 182, 16, 202, 127, 240, 53, 164, 53, 205, 46, 184, 167, 248, 127, 109, + 71, 195, 188, 69, 27, 146, 155, 31, 96, 148, 212, 86, 57, 169, 245, 94, 24, 249, 93, 254, 125, + 196, 124, 243, 36, 14, 128, 131, 151, 110, 74, 94, 126, 128, 81, 72, 34, 131, 70, 170, 178, 125, + 234, 13, 213, 213, 187, 228, 66, 217, 33, 11, 146, 147, 31, 160, 56, 255, 47, 78, 189, 181, 223, + 124, 23, 121, 221, 54, 163, 190, 120, 240, 179, 249, 139, 147, 146, 31, 192, 206, 159, 142, 86, 157, + 147, 176, 5, 195, 238, 29, 214, 2, 237, 185, 110, 201, 199, 15, 224, 0, 84, 228, 166, 111, 165, + 45, 160, 155, 169, 31, 81, 234, 152, 28, 252, 0, 118, 220, 72, 201, 61, 90, 207, 71, 174, 89, + 10, 168, 104, 64, 188, 200, 84, 79, 239, 243, 3, 168, 224, 217, 189, 8, 251, 122, 243, 176, 164, + 172, 161, 40, 141, 1, 120, 188, 198, 162, 190, 222, 229, 7, 40, 45, 32, 13, 109, 149, 133, 114, + 212, 48, 26, 16, 95, 195, 64, 157, 110, 83, 111, 111, 242, 3, 216, 249, 114, 5, 220, 32, 205, + 82, 69, 10, 156, 179, 212, 23, 229, 228, 121, 139, 31, 192, 206, 159, 85, 208, 239, 77, 205, 222, + 156, 130, 147, 81, 27, 81, 199, 46, 79, 240, 3, 216, 249, 75, 209, 41, 159, 193, 16, 40, 56, + 219, 154, 69, 255, 111, 50, 60, 15, 143, 87, 59, 24, 191, 188, 193, 15, 208, 7, 62, 95, 214, + 19, 232, 212, 18, 43, 209, 148, 133, 142, 208, 113, 18, 118, 175, 240, 3, 40, 155, 236, 20, 11, + 165, 184, 213, 12, 64, 20, 153, 94, 49, 76, 225, 238, 229, 7, 176, 243, 255, 64, 135, 115, 76, + 42, 177, 96, 132, 124, 196, 148, 127, 184, 110, 140, 11, 57, 151, 242, 3, 180, 192, 101, 232, 188, + 47, 236, 140, 107, 17, 19, 49, 211, 178, 248, 162, 88, 244, 131, 174, 228, 7, 240, 217, 127, 22, + 7, 180, 69, 182, 205, 93, 179, 148, 212, 126, 129, 199, 27, 98, 108, 1, 238, 226, 7, 48, 58, + 227, 191, 104, 115, 85, 80, 74, 20, 118, 128, 193, 16, 162, 1, 240, 159, 113, 92, 204, 185, 138, + 31, 64, 77, 120, 8, 218, 65, 243, 104, 79, 103, 219, 86, 61, 174, 233, 250, 225, 14, 113, 94, + 205, 186, 134, 31, 112, 9, 58, 146, 105, 110, 218, 54, 200, 12, 8, 131, 171, 104, 129, 251, 226, + 12, 128, 107, 248, 1, 15, 163, 45, 48, 53, 237, 160, 9, 150, 16, 180, 105, 1, 243, 173, 46, + 128, 28, 6, 192, 29, 252, 0, 14, 194, 29, 104, 3, 204, 211, 156, 185, 233, 171, 12, 33, 62, + 126, 138, 118, 125, 156, 1, 112, 23, 63, 160, 160, 59, 248, 2, 47, 163, 237, 180, 165, 199, 21, + 158, 253, 125, 104, 239, 37, 176, 157, 231, 106, 126, 192, 105, 232, 28, 93, 255, 175, 84, 160, 105, + 230, 41, 144, 22, 64, 207, 159, 138, 0, 20, 55, 63, 32, 3, 131, 49, 203, 180, 253, 69, 203, + 99, 66, 104, 92, 77, 83, 232, 201, 238, 2, 174, 224, 7, 16, 47, 136, 201, 113, 70, 16, 136, + 32, 115, 75, 2, 45, 192, 155, 252, 0, 101, 192, 252, 40, 94, 130, 76, 172, 211, 160, 43, 249, + 1, 116, 209, 148, 224, 143, 58, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, + 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 196, 249, + 37, 194, 15, 16, 126, 128, 240, 3, 132, 31, 160, 182, 4, 225, 7, 8, 63, 64, 138, 20, 41, + 82, 164, 72, 145, 18, 199, 28, 45, 252, 0, 225, 7, 20, 1, 63, 192, 205, 205, 251, 164, 243, + 3, 202, 230, 95, 134, 211, 133, 78, 219, 212, 86, 57, 215, 187, 2, 164, 112, 202, 248, 1, 248, + 153, 244, 217, 244, 29, 229, 135, 45, 133, 42, 61, 39, 30, 172, 209, 121, 252, 24, 12, 194, 5, + 197, 229, 248, 41, 227, 7, 168, 45, 163, 100, 183, 237, 80, 191, 251, 55, 58, 72, 161, 90, 159, + 41, 127, 13, 126, 64, 129, 76, 215, 191, 25, 254, 227, 95, 5, 105, 90, 22, 60, 209, 34, 7, + 250, 207, 248, 49, 249, 249, 1, 234, 103, 221, 145, 185, 28, 42, 251, 38, 233, 55, 81, 189, 222, + 127, 54, 108, 219, 115, 48, 185, 249, 1, 244, 191, 244, 25, 119, 117, 220, 174, 83, 36, 200, 121, + 227, 110, 210, 49, 19, 87, 194, 239, 251, 15, 39, 47, 63, 128, 254, 135, 254, 247, 177, 47, 151, + 66, 157, 207, 87, 64, 70, 155, 60, 72, 99, 138, 196, 11, 93, 243, 96, 230, 188, 117, 201, 205, + 15, 224, 45, 174, 231, 202, 246, 159, 181, 168, 90, 143, 9, 250, 29, 164, 105, 116, 107, 61, 90, + 243, 206, 227, 97, 203, 182, 223, 147, 151, 31, 96, 20, 202, 55, 140, 14, 127, 152, 158, 159, 120, + 173, 224, 78, 210, 103, 94, 159, 6, 203, 182, 255, 158, 156, 252, 0, 197, 249, 51, 89, 45, 178, + 73, 189, 155, 156, 90, 65, 185, 190, 51, 161, 202, 136, 111, 147, 143, 31, 160, 56, 127, 57, 158, + 249, 103, 241, 120, 212, 66, 96, 53, 231, 177, 55, 167, 125, 153, 148, 252, 0, 37, 0, 164, 44, + 91, 17, 150, 137, 210, 23, 56, 134, 175, 79, 33, 9, 78, 82, 241, 3, 20, 199, 75, 160, 131, + 141, 241, 56, 223, 70, 68, 117, 4, 255, 222, 89, 169, 103, 114, 240, 3, 168, 160, 99, 255, 64, + 39, 111, 68, 39, 183, 226, 227, 35, 22, 1, 32, 93, 193, 30, 166, 201, 157, 102, 10, 130, 183, + 249, 1, 122, 0, 154, 12, 47, 143, 142, 229, 68, 209, 16, 103, 91, 37, 98, 247, 52, 63, 128, + 155, 254, 61, 104, 67, 13, 45, 177, 13, 72, 129, 32, 10, 239, 219, 221, 69, 238, 85, 126, 128, + 193, 16, 200, 102, 72, 130, 57, 31, 185, 170, 38, 163, 100, 237, 79, 71, 27, 187, 60, 195, 15, + 224, 126, 79, 155, 26, 157, 104, 186, 83, 212, 98, 65, 203, 1, 80, 11, 172, 199, 199, 127, 139, + 164, 33, 246, 20, 63, 128, 129, 73, 148, 139, 124, 123, 4, 229, 184, 250, 248, 29, 12, 216, 37, + 14, 6, 113, 247, 243, 3, 56, 0, 228, 252, 210, 104, 138, 82, 254, 59, 37, 96, 109, 233, 68, + 64, 229, 5, 126, 0, 101, 161, 47, 143, 103, 147, 242, 144, 159, 176, 26, 245, 45, 70, 255, 213, + 216, 5, 170, 196, 176, 142, 113, 39, 63, 128, 3, 64, 185, 132, 231, 152, 154, 120, 80, 213, 14, + 155, 154, 62, 5, 105, 118, 140, 43, 89, 215, 242, 3, 42, 163, 147, 35, 149, 51, 30, 116, 152, + 138, 187, 101, 44, 34, 42, 55, 242, 3, 8, 146, 246, 79, 62, 243, 251, 172, 70, 121, 213, 97, + 147, 243, 63, 162, 213, 140, 227, 98, 206, 85, 252, 0, 90, 190, 246, 215, 167, 59, 11, 74, 132, + 149, 148, 94, 9, 194, 84, 90, 40, 197, 17, 0, 215, 240, 3, 174, 65, 167, 95, 182, 28, 240, + 52, 107, 21, 185, 137, 32, 49, 52, 206, 203, 121, 215, 240, 3, 206, 64, 107, 99, 49, 181, 69, + 66, 103, 4, 149, 247, 213, 140, 51, 0, 238, 225, 7, 112, 32, 110, 65, 27, 102, 177, 232, 9, + 70, 8, 196, 60, 180, 187, 226, 12, 128, 251, 248, 1, 28, 136, 178, 4, 81, 50, 161, 242, 66, + 90, 134, 242, 124, 16, 218, 181, 113, 6, 192, 189, 252, 0, 14, 68, 58, 218, 247, 22, 23, 61, + 134, 237, 194, 32, 245, 72, 96, 63, 211, 51, 252, 128, 22, 124, 61, 16, 52, 117, 139, 67, 24, + 156, 251, 18, 8, 128, 119, 248, 1, 180, 200, 65, 123, 197, 52, 67, 16, 102, 239, 194, 4, 2, + 224, 61, 126, 0, 245, 119, 28, 3, 62, 229, 22, 208, 9, 131, 112, 126, 2, 1, 240, 46, 63, + 0, 157, 191, 77, 37, 200, 198, 25, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, + 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, + 226, 252, 18, 225, 7, 8, 63, 64, 248, 1, 194, 15, 80, 91, 130, 240, 3, 132, 31, 32, 69, + 138, 20, 41, 82, 164, 72, 137, 99, 142, 22, 126, 128, 240, 3, 132, 31, 80, 116, 252, 0, 215, + 116, 147, 98, 225, 7, 124, 185, 204, 216, 225, 109, 150, 225, 11, 156, 93, 92, 142, 23, 15, 63, + 0, 191, 131, 190, 139, 190, 147, 190, 187, 226, 107, 83, 214, 213, 122, 105, 236, 43, 241, 164, 227, + 74, 196, 249, 98, 225, 7, 24, 70, 223, 69, 32, 133, 180, 222, 75, 33, 237, 165, 241, 80, 181, + 231, 68, 120, 224, 243, 133, 221, 79, 213, 200, 94, 108, 252, 128, 130, 0, 248, 127, 133, 91, 253, + 43, 161, 129, 47, 27, 158, 106, 53, 18, 218, 141, 88, 146, 228, 252, 128, 78, 74, 64, 253, 91, + 225, 110, 223, 18, 168, 238, 203, 213, 239, 40, 123, 231, 227, 185, 176, 105, 207, 193, 36, 230, 7, + 240, 84, 74, 159, 121, 79, 231, 29, 112, 159, 239, 27, 116, 62, 79, 119, 254, 233, 150, 57, 48, + 103, 193, 122, 56, 244, 199, 145, 228, 229, 7, 232, 159, 129, 159, 85, 246, 179, 69, 80, 231, 189, + 249, 144, 238, 31, 161, 43, 200, 41, 0, 47, 191, 57, 29, 126, 88, 189, 45, 121, 249, 1, 70, + 41, 215, 119, 102, 229, 74, 175, 78, 90, 148, 138, 103, 220, 184, 153, 178, 113, 243, 108, 232, 59, + 112, 126, 242, 242, 3, 168, 24, 26, 65, 60, 118, 231, 20, 189, 33, 247, 19, 127, 52, 117, 117, + 114, 242, 3, 148, 0, 92, 128, 246, 66, 186, 47, 203, 172, 45, 132, 26, 157, 199, 195, 131, 131, + 231, 39, 39, 63, 128, 157, 39, 85, 105, 38, 103, 166, 62, 81, 32, 169, 203, 239, 255, 187, 107, + 117, 24, 251, 37, 206, 253, 199, 146, 138, 31, 96, 52, 123, 180, 191, 163, 189, 74, 122, 65, 43, + 57, 13, 190, 246, 3, 6, 162, 122, 82, 241, 3, 148, 0, 144, 196, 166, 55, 218, 122, 155, 12, + 149, 199, 209, 198, 40, 245, 77, 14, 126, 0, 59, 127, 75, 134, 198, 250, 34, 205, 86, 67, 76, + 127, 171, 65, 173, 68, 169, 183, 183, 249, 1, 236, 252, 149, 120, 134, 233, 204, 111, 141, 0, 80, + 32, 27, 143, 118, 159, 69, 221, 189, 199, 15, 96, 199, 41, 41, 243, 25, 216, 167, 41, 191, 224, + 186, 48, 120, 66, 40, 72, 97, 47, 90, 95, 180, 115, 236, 198, 46, 79, 241, 3, 168, 160, 227, + 55, 235, 137, 88, 181, 192, 33, 43, 237, 176, 169, 21, 236, 198, 247, 212, 140, 112, 2, 189, 195, + 15, 208, 207, 190, 166, 103, 151, 124, 133, 7, 54, 136, 144, 141, 222, 8, 6, 165, 225, 60, 61, + 202, 56, 230, 25, 126, 0, 77, 117, 67, 57, 187, 124, 129, 144, 210, 54, 13, 183, 47, 112, 24, + 237, 41, 180, 115, 163, 4, 192, 221, 252, 0, 118, 254, 90, 206, 41, 122, 48, 66, 18, 246, 160, + 138, 208, 65, 155, 141, 193, 169, 232, 112, 42, 119, 53, 63, 224, 1, 180, 215, 163, 56, 109, 150, + 213, 19, 76, 45, 160, 242, 131, 162, 4, 192, 125, 252, 0, 90, 225, 225, 25, 188, 14, 143, 195, + 57, 171, 124, 84, 29, 177, 210, 29, 254, 196, 199, 181, 98, 188, 148, 118, 21, 63, 224, 52, 210, + 0, 114, 94, 225, 93, 97, 200, 12, 53, 0, 154, 37, 72, 97, 44, 30, 99, 82, 175, 186, 141, + 31, 240, 48, 243, 3, 66, 29, 212, 66, 179, 77, 135, 77, 127, 90, 65, 10, 238, 151, 240, 249, + 191, 98, 12, 128, 107, 248, 1, 231, 162, 245, 226, 169, 46, 104, 179, 192, 9, 207, 68, 95, 216, + 10, 136, 42, 87, 59, 142, 203, 121, 119, 240, 3, 56, 8, 167, 51, 28, 17, 98, 178, 252, 32, + 77, 142, 115, 63, 195, 93, 252, 0, 14, 68, 21, 180, 31, 34, 78, 121, 225, 173, 161, 94, 36, + 122, 84, 132, 0, 184, 147, 31, 192, 129, 120, 195, 230, 108, 155, 109, 34, 90, 201, 56, 91, 128, + 235, 249, 1, 148, 109, 254, 59, 155, 169, 143, 90, 196, 239, 156, 132, 253, 188, 147, 29, 128, 226, + 230, 7, 188, 98, 211, 29, 126, 67, 139, 251, 231, 112, 175, 241, 3, 30, 65, 91, 104, 10, 196, + 228, 68, 126, 249, 245, 42, 63, 160, 59, 59, 79, 83, 230, 255, 136, 59, 150, 64, 0, 188, 201, + 15, 224, 107, 133, 119, 105, 198, 72, 240, 71, 29, 111, 243, 3, 162, 93, 247, 23, 217, 82, 248, + 84, 93, 12, 157, 226, 155, 55, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, + 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 137, 124, 129, 240, + 3, 132, 31, 144, 34, 252, 0, 181, 37, 8, 63, 64, 248, 1, 82, 164, 72, 145, 34, 69, 138, + 148, 56, 230, 104, 225, 7, 8, 63, 64, 248, 1, 194, 15, 40, 114, 126, 64, 113, 58, 238, 10, + 126, 0, 139, 53, 75, 159, 106, 231, 139, 159, 31, 208, 61, 191, 69, 80, 29, 168, 46, 143, 189, + 57, 109, 96, 134, 22, 184, 247, 84, 140, 236, 197, 206, 15, 48, 140, 190, 155, 182, 238, 51, 250, + 45, 215, 149, 164, 213, 187, 228, 254, 84, 163, 203, 248, 235, 79, 150, 243, 174, 224, 7, 168, 223, + 67, 32, 133, 155, 253, 63, 66, 106, 230, 104, 120, 250, 197, 177, 80, 169, 239, 204, 36, 231, 7, + 40, 183, 240, 92, 233, 223, 4, 247, 248, 190, 131, 26, 190, 241, 250, 109, 117, 111, 125, 52, 55, + 185, 249, 1, 244, 153, 244, 217, 105, 31, 111, 133, 91, 95, 220, 1, 165, 51, 231, 64, 77, 223, + 56, 221, 121, 127, 135, 177, 176, 106, 205, 14, 216, 127, 248, 104, 114, 242, 3, 232, 179, 232, 51, + 239, 31, 182, 8, 202, 14, 156, 7, 105, 221, 166, 65, 154, 191, 144, 37, 48, 96, 200, 66, 216, + 180, 101, 111, 242, 241, 3, 204, 5, 7, 184, 46, 53, 59, 141, 219, 169, 230, 39, 205, 196, 179, + 63, 113, 198, 79, 201, 201, 15, 48, 10, 171, 75, 73, 50, 55, 35, 52, 53, 103, 0, 124, 61, + 38, 192, 129, 35, 71, 147, 143, 31, 160, 56, 127, 53, 218, 147, 44, 157, 9, 73, 217, 153, 158, + 25, 128, 114, 56, 242, 127, 240, 221, 198, 228, 225, 7, 152, 156, 191, 3, 29, 238, 200, 169, 247, + 194, 4, 21, 245, 91, 142, 152, 81, 250, 163, 57, 7, 146, 138, 31, 160, 52, 249, 107, 88, 101, + 182, 209, 70, 82, 179, 25, 3, 209, 41, 169, 248, 1, 84, 208, 41, 202, 76, 125, 43, 218, 215, + 44, 154, 176, 18, 88, 81, 2, 215, 159, 177, 27, 92, 193, 245, 246, 62, 63, 64, 57, 251, 79, + 179, 84, 6, 162, 104, 139, 52, 146, 221, 43, 117, 247, 38, 63, 128, 157, 54, 196, 149, 212, 223, + 23, 89, 57, 110, 18, 87, 230, 162, 149, 180, 26, 187, 60, 199, 15, 224, 102, 127, 35, 58, 214, + 153, 71, 250, 104, 98, 106, 66, 234, 244, 193, 0, 156, 109, 225, 131, 183, 248, 1, 124, 246, 41, + 11, 237, 91, 49, 232, 9, 183, 163, 243, 247, 71, 104, 197, 222, 224, 7, 176, 243, 101, 72, 27, + 132, 45, 96, 159, 157, 132, 182, 32, 51, 125, 225, 243, 174, 145, 84, 228, 94, 225, 7, 92, 66, + 103, 30, 29, 219, 66, 217, 101, 163, 144, 35, 212, 215, 230, 162, 61, 72, 211, 100, 148, 217, 204, + 213, 252, 128, 139, 40, 213, 46, 58, 191, 196, 36, 156, 12, 218, 41, 200, 21, 128, 74, 63, 180, + 75, 29, 172, 101, 92, 201, 15, 56, 157, 71, 250, 126, 44, 136, 182, 84, 139, 154, 29, 55, 132, + 148, 248, 58, 225, 117, 28, 221, 143, 232, 58, 126, 0, 7, 128, 50, 201, 126, 194, 139, 155, 208, + 126, 173, 89, 76, 123, 161, 253, 158, 158, 15, 112, 74, 144, 48, 166, 68, 55, 241, 3, 234, 162, + 147, 67, 98, 81, 142, 155, 90, 194, 42, 238, 251, 127, 139, 33, 0, 238, 224, 7, 112, 0, 238, + 164, 181, 187, 221, 0, 71, 103, 59, 66, 139, 56, 130, 246, 49, 218, 13, 49, 94, 209, 186, 135, + 31, 192, 65, 184, 130, 151, 184, 65, 171, 179, 29, 1, 159, 67, 129, 123, 42, 142, 253, 12, 247, + 241, 3, 56, 16, 111, 132, 244, 243, 40, 36, 9, 90, 246, 198, 185, 161, 227, 106, 126, 192, 19, + 220, 180, 33, 10, 71, 96, 39, 62, 175, 24, 75, 223, 87, 2, 224, 122, 126, 192, 93, 104, 223, + 88, 156, 113, 245, 249, 80, 194, 109, 197, 217, 2, 220, 207, 15, 192, 190, 79, 235, 130, 193, 54, + 83, 33, 109, 120, 180, 78, 96, 67, 215, 59, 252, 0, 116, 244, 5, 139, 203, 95, 226, 9, 156, + 149, 64, 0, 188, 197, 15, 192, 22, 80, 129, 48, 154, 10, 84, 45, 3, 143, 103, 38, 16, 0, + 239, 241, 3, 208, 249, 139, 209, 105, 66, 230, 141, 180, 162, 70, 198, 24, 0, 239, 242, 3, 104, + 213, 87, 4, 191, 38, 9, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, + 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 226, 253, 18, 225, 7, 8, + 63, 64, 248, 1, 194, 15, 80, 91, 130, 240, 3, 132, 31, 32, 69, 138, 20, 41, 82, 164, 72, + 137, 99, 142, 22, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 224, 36, 241, 3, + 226, 222, 247, 139, 99, 100, 119, 29, 63, 128, 234, 98, 12, 148, 85, 122, 78, 188, 242, 100, 57, + 239, 58, 126, 128, 97, 84, 23, 218, 189, 46, 51, 120, 33, 96, 0, 182, 213, 110, 55, 186, 89, + 81, 59, 239, 58, 126, 128, 250, 189, 151, 249, 183, 192, 173, 47, 108, 132, 6, 173, 167, 64, 106, + 199, 113, 80, 225, 245, 169, 187, 146, 151, 31, 128, 223, 65, 63, 212, 26, 223, 127, 181, 127, 3, + 220, 145, 185, 28, 170, 249, 242, 160, 97, 211, 17, 208, 241, 157, 25, 80, 187, 16, 162, 144, 100, + 252, 0, 189, 155, 124, 163, 127, 87, 89, 252, 206, 27, 154, 174, 133, 50, 153, 179, 161, 166, 150, + 15, 82, 104, 209, 105, 60, 172, 221, 184, 59, 121, 249, 1, 220, 77, 230, 149, 26, 60, 31, 42, + 127, 56, 31, 26, 180, 202, 133, 84, 95, 142, 126, 135, 249, 147, 45, 115, 96, 244, 196, 149, 176, + 123, 207, 193, 228, 227, 7, 168, 133, 196, 23, 213, 186, 229, 77, 75, 109, 149, 163, 220, 90, 63, + 28, 186, 191, 57, 13, 150, 175, 218, 150, 124, 252, 0, 197, 241, 179, 208, 30, 226, 28, 100, 155, + 212, 123, 139, 27, 54, 205, 130, 236, 220, 31, 146, 143, 31, 160, 56, 127, 115, 134, 22, 72, 199, + 163, 89, 103, 168, 91, 205, 78, 227, 224, 227, 165, 155, 146, 135, 31, 96, 114, 158, 164, 181, 111, + 163, 109, 179, 18, 88, 164, 249, 179, 54, 148, 235, 59, 243, 207, 7, 134, 46, 76, 14, 126, 128, + 226, 248, 121, 104, 181, 210, 155, 12, 95, 134, 125, 125, 151, 141, 158, 136, 36, 120, 67, 49, 0, + 45, 147, 134, 31, 192, 206, 151, 68, 107, 141, 118, 140, 146, 46, 155, 181, 68, 74, 86, 202, 221, + 70, 86, 58, 79, 243, 3, 76, 206, 215, 69, 251, 10, 109, 127, 20, 77, 17, 137, 174, 58, 169, + 89, 233, 60, 203, 15, 160, 130, 77, 253, 42, 166, 66, 108, 209, 147, 178, 107, 129, 104, 217, 232, + 73, 87, 112, 175, 154, 145, 214, 147, 252, 0, 118, 190, 146, 158, 144, 61, 223, 233, 19, 118, 73, + 216, 11, 90, 128, 22, 88, 143, 129, 233, 128, 118, 150, 133, 31, 222, 224, 7, 24, 103, 142, 82, + 107, 162, 35, 83, 245, 254, 174, 69, 76, 193, 171, 142, 1, 243, 73, 136, 105, 183, 108, 118, 61, + 63, 192, 88, 209, 161, 117, 97, 253, 224, 177, 136, 26, 98, 45, 36, 23, 57, 173, 3, 202, 71, + 98, 8, 184, 154, 31, 192, 77, 190, 17, 58, 48, 196, 172, 24, 85, 250, 123, 48, 164, 21, 132, + 182, 140, 215, 209, 174, 138, 50, 157, 187, 144, 31, 160, 233, 236, 128, 127, 162, 209, 200, 189, 212, + 82, 52, 109, 223, 228, 141, 241, 128, 164, 116, 245, 28, 94, 74, 187, 142, 31, 64, 205, 246, 125, + 230, 129, 157, 176, 65, 100, 4, 237, 196, 213, 220, 244, 223, 139, 101, 57, 239, 38, 126, 0, 193, + 19, 198, 219, 136, 164, 131, 209, 6, 61, 14, 194, 2, 106, 65, 49, 4, 192, 61, 252, 0, 14, + 194, 243, 42, 47, 36, 100, 101, 103, 230, 133, 104, 97, 45, 131, 214, 6, 141, 99, 73, 196, 236, + 58, 126, 0, 7, 161, 126, 88, 19, 215, 108, 193, 73, 42, 64, 129, 160, 106, 119, 199, 186, 121, + 226, 86, 126, 192, 163, 188, 134, 55, 186, 66, 208, 110, 169, 203, 143, 247, 225, 241, 201, 56, 54, + 116, 92, 205, 15, 184, 155, 104, 112, 17, 70, 124, 213, 250, 199, 185, 163, 229, 122, 126, 0, 241, + 2, 23, 71, 92, 246, 250, 2, 115, 24, 158, 114, 250, 201, 12, 64, 177, 241, 3, 104, 80, 67, + 155, 100, 195, 12, 220, 134, 175, 245, 193, 199, 23, 198, 217, 2, 188, 195, 15, 64, 39, 135, 155, + 206, 252, 113, 146, 210, 163, 221, 147, 200, 14, 178, 183, 248, 1, 190, 192, 7, 166, 5, 209, 83, + 234, 165, 110, 28, 1, 240, 30, 63, 0, 29, 126, 133, 3, 240, 41, 218, 117, 9, 254, 134, 224, + 77, 126, 0, 58, 78, 187, 191, 79, 23, 193, 143, 40, 222, 229, 7, 196, 51, 234, 199, 125, 49, + 84, 28, 151, 195, 167, 224, 62, 6, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, + 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 64, 162, 95, 32, + 252, 0, 225, 7, 164, 8, 63, 192, 248, 18, 225, 7, 72, 145, 34, 69, 138, 20, 41, 82, 226, + 153, 163, 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 192, 201, 229, + 7, 156, 180, 25, 195, 11, 252, 0, 22, 117, 21, 253, 134, 136, 219, 249, 1, 122, 107, 192, 58, + 209, 238, 53, 213, 145, 234, 90, 181, 199, 132, 244, 162, 60, 243, 174, 229, 7, 168, 193, 184, 186, + 197, 54, 189, 142, 181, 94, 155, 9, 213, 186, 229, 237, 175, 243, 98, 130, 36, 9, 87, 243, 3, + 94, 221, 169, 255, 86, 105, 212, 227, 74, 255, 38, 184, 193, 255, 51, 84, 240, 79, 135, 39, 94, + 24, 13, 169, 157, 115, 161, 244, 199, 115, 86, 37, 37, 63, 224, 126, 94, 101, 210, 119, 83, 29, + 110, 244, 175, 129, 7, 51, 231, 67, 117, 45, 87, 191, 209, 178, 201, 139, 99, 224, 243, 185, 107, + 147, 147, 31, 192, 117, 211, 239, 5, 120, 112, 240, 124, 72, 127, 109, 9, 212, 242, 141, 133, 122, + 190, 145, 186, 243, 255, 107, 51, 10, 190, 94, 176, 30, 126, 251, 253, 112, 114, 241, 3, 212, 146, + 161, 5, 254, 142, 131, 220, 187, 213, 122, 228, 169, 218, 35, 120, 186, 101, 14, 12, 196, 46, 178, + 105, 243, 222, 228, 226, 7, 20, 56, 238, 11, 156, 67, 119, 143, 163, 125, 72, 57, 202, 211, 245, + 187, 202, 57, 0, 153, 1, 104, 213, 53, 23, 150, 44, 223, 146, 92, 252, 0, 197, 249, 59, 208, + 210, 208, 150, 232, 218, 98, 11, 149, 201, 219, 163, 150, 37, 15, 63, 128, 157, 46, 193, 70, 247, + 15, 127, 102, 3, 81, 208, 133, 22, 149, 123, 77, 132, 170, 195, 191, 73, 14, 126, 0, 247, 243, + 115, 209, 177, 255, 160, 245, 102, 102, 200, 31, 54, 114, 122, 10, 192, 132, 10, 111, 78, 253, 50, + 41, 248, 1, 124, 230, 175, 70, 107, 133, 150, 27, 69, 81, 74, 206, 19, 93, 162, 165, 49, 118, + 121, 150, 31, 192, 142, 255, 27, 237, 17, 60, 171, 211, 204, 162, 42, 59, 137, 45, 190, 254, 174, + 105, 157, 224, 61, 126, 0, 59, 79, 114, 186, 151, 208, 14, 132, 101, 164, 213, 108, 3, 65, 137, + 25, 111, 85, 83, 114, 123, 134, 31, 192, 78, 255, 141, 143, 237, 169, 31, 51, 60, 193, 172, 39, + 12, 83, 149, 113, 16, 190, 103, 53, 234, 153, 230, 101, 179, 87, 248, 1, 87, 178, 36, 110, 20, + 218, 94, 43, 217, 172, 69, 74, 94, 35, 8, 199, 56, 29, 247, 153, 54, 221, 217, 221, 252, 0, + 172, 248, 195, 156, 129, 122, 169, 131, 228, 203, 86, 66, 234, 41, 120, 252, 187, 157, 168, 202, 149, + 252, 0, 118, 156, 64, 41, 207, 161, 173, 161, 172, 210, 97, 162, 233, 200, 129, 48, 206, 254, 119, + 104, 149, 209, 254, 17, 229, 82, 218, 61, 252, 0, 26, 164, 208, 26, 162, 125, 196, 78, 253, 25, + 97, 100, 143, 36, 167, 223, 138, 246, 22, 218, 229, 78, 150, 243, 174, 225, 7, 112, 16, 90, 57, + 109, 226, 54, 118, 28, 91, 204, 55, 104, 55, 57, 92, 210, 187, 139, 31, 192, 65, 24, 162, 80, + 34, 130, 150, 138, 113, 251, 0, 172, 231, 65, 211, 233, 53, 141, 251, 248, 1, 28, 132, 108, 171, + 181, 124, 88, 151, 208, 194, 154, 62, 165, 231, 190, 56, 150, 205, 19, 87, 242, 3, 56, 8, 121, + 14, 225, 9, 6, 59, 100, 124, 172, 153, 168, 221, 206, 15, 56, 3, 109, 166, 69, 43, 8, 233, + 30, 220, 34, 104, 113, 84, 45, 142, 93, 35, 215, 243, 3, 254, 197, 252, 0, 203, 254, 79, 83, + 36, 6, 128, 156, 247, 197, 185, 109, 230, 9, 126, 192, 21, 104, 43, 108, 154, 254, 81, 12, 194, + 71, 120, 188, 61, 206, 0, 120, 131, 31, 128, 14, 222, 68, 35, 188, 105, 77, 64, 252, 0, 2, + 41, 166, 38, 178, 131, 236, 25, 126, 0, 51, 69, 118, 40, 253, 159, 2, 208, 54, 193, 157, 99, + 111, 241, 3, 248, 250, 192, 216, 235, 235, 199, 99, 68, 34, 1, 240, 30, 63, 0, 29, 174, 196, + 216, 220, 74, 234, 53, 126, 156, 1, 240, 38, 63, 32, 26, 41, 46, 134, 223, 21, 133, 31, 32, + 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, + 225, 7, 8, 63, 64, 248, 1, 241, 126, 129, 240, 3, 132, 31, 144, 34, 252, 0, 181, 37, 8, + 63, 64, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 56, 230, 104, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 156, 10, 126, 64, 145, 109, + 137, 121, 137, 31, 64, 117, 163, 58, 86, 206, 191, 177, 43, 241, 77, 81, 47, 241, 3, 10, 56, + 2, 45, 242, 131, 64, 117, 46, 253, 209, 156, 85, 169, 173, 114, 106, 36, 242, 197, 158, 224, 7, + 132, 12, 186, 254, 173, 112, 109, 139, 205, 80, 175, 199, 18, 168, 221, 113, 28, 84, 239, 146, 251, + 83, 134, 47, 112, 125, 92, 83, 157, 219, 249, 1, 100, 84, 7, 170, 11, 213, 233, 58, 255, 58, + 184, 35, 115, 57, 60, 234, 155, 6, 13, 155, 142, 128, 103, 59, 140, 133, 71, 223, 157, 145, 124, + 252, 0, 174, 35, 221, 19, 112, 148, 178, 206, 103, 124, 250, 43, 60, 208, 28, 47, 180, 124, 83, + 160, 182, 111, 76, 193, 61, 199, 99, 102, 172, 73, 46, 126, 128, 185, 84, 234, 61, 249, 249, 242, + 239, 206, 56, 84, 19, 155, 122, 122, 147, 236, 2, 197, 137, 214, 118, 52, 140, 155, 188, 26, 246, + 254, 254, 71, 242, 240, 3, 140, 130, 78, 158, 141, 78, 158, 69, 130, 105, 180, 177, 13, 88, 126, + 103, 72, 240, 158, 107, 61, 18, 250, 15, 94, 0, 235, 54, 236, 246, 62, 63, 192, 40, 74, 154, + 238, 123, 57, 111, 233, 68, 206, 78, 31, 34, 191, 163, 99, 243, 46, 121, 240, 211, 186, 223, 188, + 207, 15, 80, 156, 255, 7, 229, 30, 67, 107, 141, 14, 142, 204, 208, 236, 33, 10, 117, 95, 24, + 5, 77, 71, 46, 133, 29, 7, 15, 123, 155, 31, 160, 56, 127, 31, 159, 241, 89, 156, 168, 213, + 78, 83, 172, 139, 169, 43, 188, 49, 117, 77, 153, 97, 11, 189, 203, 15, 96, 167, 207, 66, 135, + 232, 216, 142, 36, 118, 216, 183, 119, 217, 74, 106, 11, 143, 36, 182, 120, 183, 92, 223, 153, 245, + 61, 203, 15, 48, 245, 113, 74, 182, 186, 51, 138, 152, 82, 85, 154, 81, 202, 206, 115, 217, 15, + 239, 240, 3, 216, 105, 234, 223, 103, 51, 67, 32, 192, 0, 133, 232, 82, 218, 66, 205, 97, 54, + 139, 178, 75, 24, 203, 102, 79, 240, 3, 216, 249, 123, 24, 141, 179, 156, 212, 161, 17, 50, 209, + 7, 109, 94, 35, 237, 97, 42, 117, 27, 211, 9, 117, 47, 63, 128, 117, 255, 23, 160, 117, 224, + 51, 126, 204, 98, 112, 11, 154, 130, 17, 180, 248, 219, 111, 52, 51, 216, 140, 103, 174, 229, 7, + 220, 133, 150, 193, 48, 164, 95, 213, 129, 76, 101, 9, 68, 104, 9, 170, 117, 54, 154, 189, 205, + 165, 180, 123, 248, 1, 236, 124, 101, 62, 227, 193, 48, 149, 104, 225, 66, 38, 24, 53, 0, 154, + 206, 19, 122, 27, 237, 218, 72, 170, 50, 55, 242, 3, 206, 98, 240, 9, 56, 144, 205, 7, 173, + 254, 134, 129, 57, 196, 171, 192, 199, 28, 44, 233, 93, 201, 15, 184, 39, 12, 145, 99, 51, 205, + 25, 93, 66, 93, 230, 226, 113, 15, 90, 45, 135, 215, 52, 174, 229, 7, 52, 180, 96, 135, 4, + 89, 44, 29, 30, 144, 194, 150, 176, 25, 31, 215, 117, 154, 146, 215, 237, 252, 128, 110, 106, 255, + 183, 27, 0, 149, 199, 27, 244, 117, 130, 22, 112, 172, 98, 119, 53, 63, 128, 131, 240, 133, 131, + 145, 158, 90, 195, 65, 12, 80, 14, 30, 111, 139, 113, 195, 196, 221, 252, 0, 116, 234, 111, 232, + 212, 55, 14, 166, 188, 5, 52, 226, 199, 177, 99, 228, 9, 126, 192, 237, 104, 191, 91, 0, 210, + 140, 199, 116, 21, 120, 103, 60, 34, 106, 47, 241, 3, 234, 21, 12, 118, 90, 200, 168, 255, 61, + 154, 159, 8, 115, 241, 238, 32, 123, 137, 31, 208, 65, 157, 18, 209, 241, 253, 188, 216, 57, 59, + 129, 77, 83, 207, 241, 3, 6, 43, 93, 128, 160, 169, 103, 36, 184, 107, 236, 73, 126, 192, 92, + 125, 118, 208, 244, 107, 134, 211, 19, 12, 128, 247, 248, 1, 232, 244, 37, 78, 87, 122, 14, 126, + 87, 20, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, + 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 196, 251, 5, 194, 15, 16, 126, 64, 138, 240, + 3, 212, 150, 32, 252, 0, 225, 7, 72, 145, 34, 69, 138, 20, 41, 82, 226, 152, 163, 133, 31, + 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, + 0, 231, 91, 92, 194, 15, 224, 122, 196, 197, 15, 72, 120, 91, 220, 139, 252, 0, 99, 186, 172, + 221, 119, 187, 49, 38, 228, 196, 61, 213, 121, 141, 31, 160, 118, 75, 170, 51, 213, 189, 212, 103, + 11, 160, 210, 171, 147, 72, 87, 120, 73, 76, 139, 28, 175, 240, 3, 168, 14, 84, 23, 53, 8, + 87, 100, 110, 130, 107, 252, 235, 225, 145, 54, 203, 161, 209, 75, 19, 160, 122, 183, 92, 168, 217, + 113, 92, 143, 88, 62, 220, 19, 252, 0, 245, 231, 113, 154, 6, 105, 65, 117, 155, 127, 5, 60, + 144, 185, 16, 42, 249, 38, 67, 70, 102, 22, 60, 243, 194, 72, 104, 244, 246, 116, 106, 9, 139, + 28, 175, 237, 221, 204, 15, 48, 151, 7, 63, 155, 95, 177, 244, 71, 115, 254, 44, 247, 254, 76, + 104, 208, 110, 42, 212, 108, 50, 22, 210, 124, 35, 244, 59, 207, 27, 102, 6, 32, 59, 111, 133, + 247, 249, 1, 230, 66, 25, 167, 89, 133, 86, 142, 210, 240, 166, 182, 202, 249, 198, 156, 201, 182, + 89, 199, 113, 48, 102, 226, 74, 248, 109, 247, 65, 111, 243, 3, 20, 167, 141, 99, 73, 180, 154, + 233, 190, 172, 65, 232, 52, 169, 200, 118, 229, 171, 73, 11, 157, 255, 95, 219, 81, 240, 209, 208, + 69, 176, 113, 243, 30, 111, 243, 3, 20, 231, 47, 66, 103, 111, 64, 123, 51, 189, 201, 240, 92, + 60, 110, 49, 233, 11, 66, 206, 126, 219, 247, 102, 194, 239, 166, 187, 68, 61, 195, 15, 96, 135, + 75, 240, 241, 86, 180, 167, 209, 222, 225, 92, 132, 251, 35, 201, 107, 211, 124, 89, 199, 170, 244, + 156, 8, 13, 71, 45, 133, 13, 123, 15, 121, 143, 31, 160, 59, 173, 233, 82, 120, 178, 46, 44, + 152, 250, 41, 154, 162, 156, 165, 119, 212, 21, 134, 150, 235, 59, 115, 138, 167, 248, 1, 202, 89, + 167, 12, 147, 207, 163, 189, 201, 137, 88, 255, 228, 212, 155, 78, 228, 244, 244, 222, 121, 104, 165, + 188, 198, 15, 184, 20, 207, 120, 41, 74, 173, 139, 246, 3, 218, 207, 138, 60, 62, 24, 53, 43, + 189, 86, 16, 140, 209, 104, 151, 41, 254, 184, 154, 31, 112, 14, 79, 99, 141, 41, 27, 61, 58, + 59, 219, 161, 92, 62, 104, 33, 175, 35, 29, 114, 14, 79, 135, 103, 42, 254, 184, 143, 31, 64, + 57, 68, 209, 30, 213, 7, 53, 45, 48, 141, 25, 33, 7, 237, 68, 211, 14, 16, 26, 199, 89, + 141, 94, 223, 230, 82, 218, 117, 252, 128, 179, 24, 118, 114, 220, 50, 211, 180, 179, 86, 160, 106, + 141, 127, 194, 32, 60, 26, 105, 57, 239, 42, 126, 0, 7, 33, 205, 228, 112, 48, 130, 106, 52, + 212, 233, 80, 81, 245, 44, 116, 190, 108, 36, 105, 157, 43, 249, 1, 28, 132, 15, 45, 251, 183, + 230, 8, 156, 66, 173, 103, 41, 90, 102, 52, 69, 169, 155, 249, 1, 103, 226, 25, 252, 201, 226, + 44, 7, 185, 105, 7, 13, 25, 173, 69, 139, 56, 132, 175, 55, 117, 122, 165, 232, 102, 126, 64, + 53, 43, 118, 136, 229, 153, 47, 108, 25, 107, 49, 64, 141, 156, 42, 74, 189, 192, 15, 120, 211, + 202, 81, 243, 216, 192, 173, 130, 22, 70, 109, 208, 98, 1, 40, 184, 155, 31, 192, 65, 248, 206, + 110, 206, 215, 3, 161, 113, 179, 207, 15, 214, 197, 49, 110, 150, 120, 130, 31, 80, 166, 64, 54, + 111, 51, 255, 227, 235, 221, 226, 220, 45, 242, 6, 63, 0, 207, 116, 247, 144, 190, 95, 216, 21, + 54, 227, 145, 254, 118, 77, 156, 0, 5, 79, 241, 3, 102, 155, 232, 112, 123, 48, 16, 195, 240, + 236, 223, 149, 192, 126, 161, 119, 248, 1, 232, 240, 221, 198, 128, 199, 51, 194, 87, 49, 109, 99, + 199, 176, 16, 114, 45, 63, 128, 71, 249, 227, 120, 230, 7, 232, 87, 137, 54, 156, 176, 88, 118, + 182, 61, 199, 15, 192, 22, 208, 146, 175, 236, 18, 117, 222, 155, 252, 128, 68, 177, 25, 137, 92, + 14, 11, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 20, + 244, 37, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 64, 138, 240, 3, 140, 47, 16, 126, + 128, 240, 3, 82, 132, 31, 32, 69, 138, 20, 41, 82, 164, 72, 137, 123, 142, 22, 126, 128, 240, + 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 156, + 111, 113, 9, 63, 128, 235, 33, 252, 0, 174, 143, 240, 3, 184, 110, 142, 249, 1, 220, 21, 59, + 199, 188, 200, 241, 50, 63, 128, 2, 64, 245, 164, 186, 147, 15, 188, 78, 40, 29, 203, 135, 123, + 143, 31, 128, 117, 162, 5, 149, 185, 155, 210, 58, 129, 124, 41, 215, 119, 230, 202, 12, 95, 224, + 126, 39, 31, 234, 41, 126, 128, 122, 131, 4, 213, 141, 234, 168, 119, 81, 255, 122, 184, 193, 191, + 22, 74, 182, 88, 6, 141, 59, 76, 134, 218, 93, 198, 67, 141, 206, 227, 199, 80, 222, 227, 104, + 87, 117, 174, 231, 7, 88, 117, 135, 82, 159, 47, 132, 10, 159, 127, 11, 143, 118, 95, 7, 165, + 50, 23, 64, 217, 204, 89, 80, 61, 51, 79, 191, 253, 254, 169, 150, 57, 240, 220, 107, 83, 160, + 244, 71, 115, 118, 121, 150, 31, 96, 46, 44, 196, 60, 131, 148, 168, 233, 77, 134, 167, 87, 235, + 150, 55, 13, 207, 48, 16, 71, 160, 126, 102, 78, 129, 224, 226, 169, 22, 35, 32, 107, 204, 114, + 111, 243, 3, 20, 167, 245, 99, 186, 47, 235, 62, 124, 92, 145, 179, 79, 175, 194, 231, 43, 241, + 76, 31, 74, 55, 169, 77, 218, 245, 156, 8, 19, 166, 253, 8, 59, 119, 29, 240, 22, 63, 192, + 92, 240, 12, 95, 142, 14, 93, 199, 106, 179, 65, 104, 185, 104, 191, 176, 220, 230, 184, 89, 115, + 72, 122, 163, 231, 219, 141, 134, 156, 220, 31, 96, 215, 238, 131, 222, 226, 7, 240, 153, 38, 153, + 237, 185, 236, 116, 11, 116, 136, 68, 83, 51, 89, 76, 17, 85, 111, 92, 191, 85, 14, 244, 192, + 230, 238, 25, 126, 128, 194, 7, 161, 76, 180, 149, 208, 72, 31, 56, 128, 249, 32, 155, 236, 28, + 181, 144, 217, 158, 104, 208, 44, 123, 97, 185, 126, 179, 224, 169, 241, 203, 221, 207, 15, 208, 157, + 214, 2, 183, 232, 89, 233, 181, 64, 19, 116, 112, 42, 62, 158, 143, 182, 215, 86, 50, 111, 159, + 165, 158, 164, 244, 223, 146, 44, 255, 129, 207, 23, 78, 119, 51, 63, 224, 223, 108, 15, 163, 3, + 189, 209, 161, 207, 89, 24, 73, 14, 253, 25, 171, 154, 92, 17, 94, 82, 138, 222, 82, 236, 143, + 251, 248, 1, 236, 124, 85, 180, 182, 104, 99, 24, 136, 178, 75, 85, 142, 42, 98, 73, 48, 191, + 110, 40, 74, 11, 228, 182, 133, 45, 129, 242, 22, 15, 164, 204, 212, 212, 133, 148, 75, 105, 119, + 241, 3, 56, 0, 21, 209, 161, 19, 106, 158, 97, 135, 103, 56, 104, 243, 124, 55, 218, 72, 252, + 156, 114, 102, 109, 161, 43, 249, 1, 28, 132, 137, 49, 53, 109, 95, 196, 64, 13, 33, 226, 140, + 205, 180, 238, 90, 126, 64, 169, 16, 8, 82, 184, 100, 54, 104, 215, 255, 149, 46, 178, 145, 23, + 65, 231, 219, 169, 74, 93, 203, 15, 224, 133, 205, 7, 17, 32, 9, 102, 57, 189, 241, 190, 32, + 119, 31, 146, 209, 247, 193, 247, 220, 26, 237, 34, 201, 181, 252, 0, 116, 224, 34, 149, 10, 101, + 35, 154, 182, 154, 9, 142, 242, 153, 63, 203, 193, 5, 146, 235, 249, 1, 109, 84, 189, 48, 143, + 244, 118, 131, 93, 144, 231, 248, 6, 78, 156, 231, 0, 120, 130, 31, 176, 204, 228, 164, 21, 80, + 225, 8, 83, 162, 158, 143, 69, 79, 236, 21, 126, 64, 93, 7, 40, 157, 93, 248, 122, 163, 56, + 246, 8, 188, 193, 15, 64, 7, 71, 69, 152, 251, 199, 17, 81, 46, 222, 157, 34, 79, 240, 3, + 208, 193, 59, 45, 250, 60, 129, 18, 179, 209, 42, 163, 157, 29, 103, 0, 60, 197, 15, 120, 203, + 196, 10, 154, 65, 120, 141, 4, 183, 201, 188, 195, 15, 192, 41, 143, 174, 253, 119, 176, 243, 253, + 104, 47, 160, 8, 246, 9, 189, 197, 15, 64, 167, 51, 208, 94, 71, 251, 47, 237, 247, 37, 232, + 188, 103, 249, 1, 87, 20, 209, 46, 177, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, + 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 32, 209, 47, + 16, 126, 128, 240, 3, 82, 132, 31, 96, 124, 137, 240, 3, 164, 72, 145, 34, 69, 138, 20, 41, + 241, 204, 209, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 243, 45, 46, 225, 7, 112, 61, 132, 31, 192, 245, 17, 126, 0, 215, + 77, 248, 1, 194, 15, 72, 137, 141, 31, 224, 248, 231, 241, 100, 225, 7, 168, 139, 37, 242, 133, + 151, 205, 205, 156, 92, 213, 121, 146, 31, 160, 255, 64, 138, 117, 43, 243, 234, 206, 176, 53, 66, + 201, 110, 219, 243, 175, 29, 62, 158, 243, 91, 131, 102, 217, 37, 35, 125, 144, 103, 248, 1, 86, + 3, 35, 213, 173, 193, 23, 107, 225, 182, 23, 113, 101, 138, 227, 192, 141, 254, 53, 112, 139, 127, + 53, 220, 219, 116, 25, 52, 238, 60, 29, 234, 118, 210, 41, 18, 19, 212, 44, 213, 230, 15, 113, + 53, 63, 192, 170, 24, 55, 86, 147, 126, 176, 108, 191, 89, 63, 148, 31, 48, 23, 210, 94, 94, + 8, 21, 125, 83, 160, 146, 111, 50, 212, 111, 58, 86, 191, 13, 255, 201, 102, 35, 224, 233, 30, + 19, 136, 37, 178, 29, 159, 159, 110, 185, 147, 227, 86, 126, 128, 226, 108, 9, 69, 105, 126, 71, + 134, 22, 40, 137, 199, 90, 104, 253, 73, 93, 130, 205, 123, 89, 154, 47, 95, 135, 152, 230, 203, + 46, 208, 29, 53, 121, 113, 12, 4, 198, 46, 135, 47, 86, 110, 241, 6, 63, 64, 117, 152, 143, + 36, 182, 190, 7, 237, 38, 180, 38, 104, 67, 117, 201, 44, 37, 97, 206, 119, 242, 176, 149, 222, + 232, 137, 230, 57, 240, 242, 219, 51, 96, 246, 130, 95, 96, 207, 222, 67, 238, 229, 7, 176, 147, + 234, 227, 235, 210, 155, 12, 191, 3, 143, 15, 114, 222, 209, 175, 88, 63, 180, 131, 53, 133, 135, + 156, 72, 108, 219, 190, 60, 17, 230, 125, 179, 1, 14, 31, 57, 230, 62, 126, 128, 194, 3, 57, + 3, 141, 24, 2, 87, 177, 138, 236, 93, 180, 207, 88, 14, 15, 76, 140, 56, 102, 163, 37, 12, + 85, 155, 105, 249, 175, 53, 104, 154, 253, 103, 157, 222, 147, 97, 196, 202, 45, 174, 228, 7, 92, + 130, 118, 3, 25, 86, 188, 25, 26, 201, 104, 135, 41, 233, 184, 247, 133, 52, 103, 205, 86, 63, + 108, 165, 55, 36, 101, 233, 55, 56, 210, 7, 202, 12, 158, 15, 29, 191, 94, 227, 58, 126, 0, + 57, 255, 30, 243, 3, 8, 126, 178, 23, 155, 243, 65, 11, 141, 112, 88, 42, 238, 130, 188, 228, + 246, 250, 194, 32, 139, 170, 159, 171, 240, 250, 212, 43, 93, 201, 15, 208, 155, 124, 147, 225, 19, + 28, 242, 2, 130, 17, 4, 214, 230, 215, 72, 135, 252, 49, 218, 213, 234, 114, 222, 149, 252, 0, + 60, 123, 85, 157, 128, 18, 148, 179, 28, 41, 16, 164, 50, 91, 141, 214, 135, 83, 247, 254, 77, + 9, 128, 59, 249, 1, 220, 21, 102, 69, 160, 66, 216, 182, 6, 101, 176, 51, 254, 118, 24, 237, + 5, 154, 38, 45, 166, 117, 247, 242, 3, 176, 194, 169, 97, 103, 92, 165, 195, 216, 228, 33, 87, + 6, 198, 63, 240, 61, 195, 89, 98, 87, 194, 238, 34, 201, 181, 252, 0, 61, 8, 90, 96, 113, + 36, 225, 180, 205, 64, 71, 0, 133, 205, 76, 147, 42, 27, 73, 74, 239, 5, 126, 192, 19, 81, + 48, 25, 86, 127, 163, 245, 64, 15, 180, 139, 29, 92, 28, 121, 130, 31, 176, 34, 10, 36, 5, + 120, 90, 60, 202, 252, 176, 10, 39, 3, 160, 80, 156, 252, 128, 38, 33, 131, 159, 86, 64, 147, + 80, 71, 254, 189, 250, 50, 88, 11, 212, 196, 163, 227, 29, 94, 47, 241, 3, 126, 41, 128, 35, + 89, 244, 123, 124, 237, 61, 28, 240, 174, 138, 103, 167, 200, 43, 252, 128, 23, 194, 150, 189, 90, + 224, 56, 58, 62, 11, 29, 111, 20, 175, 146, 220, 51, 252, 0, 38, 67, 110, 87, 230, 250, 67, + 12, 84, 107, 140, 118, 65, 2, 59, 68, 222, 225, 7, 160, 163, 157, 148, 38, 79, 92, 176, 132, + 127, 220, 244, 20, 63, 0, 155, 60, 1, 20, 102, 161, 243, 45, 241, 120, 249, 95, 146, 31, 192, + 44, 145, 203, 138, 104, 131, 84, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, + 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 144, 232, 23, 8, 63, + 64, 248, 1, 41, 194, 15, 48, 190, 68, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 120, 230, + 104, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, + 7, 8, 63, 192, 249, 22, 151, 240, 3, 184, 30, 194, 15, 224, 250, 8, 63, 128, 235, 38, 252, + 0, 225, 7, 164, 8, 63, 64, 13, 130, 240, 3, 34, 242, 3, 208, 23, 221, 167, 104, 183, 200, + 36, 5, 63, 0, 235, 72, 107, 18, 245, 228, 209, 181, 3, 249, 196, 87, 145, 17, 111, 146, 242, + 28, 63, 192, 60, 118, 81, 29, 169, 174, 161, 179, 194, 246, 2, 138, 68, 169, 33, 11, 54, 100, + 248, 2, 87, 90, 238, 228, 184, 157, 31, 96, 87, 12, 33, 182, 62, 30, 124, 190, 16, 106, 14, + 91, 1, 247, 116, 222, 1, 215, 250, 127, 129, 255, 248, 87, 229, 83, 36, 154, 45, 131, 39, 122, + 204, 134, 58, 93, 114, 161, 102, 167, 113, 159, 90, 46, 122, 188, 112, 171, 172, 9, 162, 112, 53, + 218, 245, 104, 231, 161, 85, 71, 107, 89, 165, 231, 196, 239, 170, 247, 154, 4, 25, 109, 243, 160, + 186, 47, 23, 170, 249, 242, 32, 163, 233, 200, 124, 138, 68, 203, 28, 120, 188, 203, 120, 168, 248, + 218, 148, 197, 33, 153, 44, 221, 192, 15, 176, 113, 246, 28, 194, 93, 80, 101, 57, 99, 53, 105, + 5, 31, 225, 4, 238, 164, 21, 254, 132, 185, 2, 187, 210, 155, 12, 39, 182, 192, 166, 124, 209, + 69, 120, 222, 226, 150, 120, 230, 191, 26, 183, 28, 122, 127, 253, 179, 187, 248, 1, 252, 248, 92, + 230, 7, 156, 149, 161, 5, 254, 195, 201, 23, 159, 69, 107, 129, 142, 12, 38, 73, 60, 218, 52, + 180, 229, 236, 16, 233, 7, 15, 24, 66, 43, 179, 220, 62, 93, 17, 93, 249, 59, 140, 133, 126, + 131, 230, 195, 210, 31, 182, 192, 254, 3, 135, 93, 193, 15, 184, 6, 173, 36, 55, 221, 58, 76, + 136, 120, 19, 29, 253, 16, 143, 139, 208, 126, 67, 251, 145, 143, 134, 90, 244, 184, 34, 167, 13, + 70, 208, 25, 134, 234, 143, 253, 89, 240, 121, 246, 18, 248, 69, 145, 205, 20, 43, 63, 128, 3, + 240, 56, 218, 20, 180, 157, 104, 127, 80, 243, 101, 150, 64, 88, 234, 109, 147, 140, 62, 90, 102, + 122, 69, 100, 29, 216, 89, 189, 107, 46, 52, 30, 254, 45, 108, 216, 123, 200, 93, 252, 0, 37, + 251, 188, 21, 43, 32, 81, 35, 133, 233, 74, 180, 94, 101, 6, 204, 254, 190, 206, 200, 37, 238, + 227, 7, 112, 16, 22, 89, 52, 227, 160, 85, 2, 246, 40, 108, 1, 48, 41, 79, 71, 227, 177, + 18, 159, 88, 87, 243, 3, 90, 68, 232, 191, 193, 144, 129, 77, 179, 5, 42, 128, 2, 89, 249, + 26, 173, 41, 37, 109, 53, 228, 244, 110, 231, 7, 252, 59, 18, 254, 38, 66, 203, 8, 90, 140, + 1, 148, 164, 57, 13, 237, 124, 243, 69, 146, 219, 249, 1, 195, 204, 205, 93, 17, 77, 91, 82, + 36, 76, 77, 127, 33, 154, 207, 78, 99, 232, 5, 126, 64, 21, 101, 74, 11, 154, 30, 23, 206, + 233, 161, 244, 24, 154, 18, 183, 162, 17, 61, 162, 54, 218, 133, 234, 122, 194, 20, 0, 79, 240, + 3, 126, 138, 101, 6, 192, 96, 108, 193, 160, 116, 82, 105, 49, 17, 46, 140, 60, 193, 15, 232, + 234, 96, 126, 39, 199, 215, 225, 241, 3, 102, 134, 156, 225, 240, 202, 208, 253, 252, 0, 98, 134, + 69, 33, 71, 0, 51, 67, 122, 176, 180, 182, 68, 44, 59, 69, 94, 225, 7, 140, 47, 232, 6, + 161, 4, 169, 61, 104, 164, 36, 191, 45, 206, 189, 1, 207, 240, 3, 26, 154, 206, 60, 93, 228, + 204, 161, 75, 91, 198, 231, 157, 22, 103, 0, 60, 197, 15, 216, 105, 90, 205, 85, 181, 164, 62, + 198, 184, 59, 228, 29, 126, 64, 62, 10, 107, 42, 83, 35, 18, 254, 121, 219, 115, 252, 0, 134, + 38, 212, 54, 175, 230, 18, 8, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 32, 209, 47, 16, + 126, 128, 240, 3, 82, 132, 31, 96, 124, 137, 240, 3, 164, 72, 145, 34, 69, 138, 20, 41, 241, + 204, 209, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, + 194, 15, 16, 126, 128, 243, 45, 46, 225, 7, 112, 61, 132, 31, 192, 245, 17, 126, 0, 215, 77, + 248, 1, 194, 15, 72, 17, 126, 128, 26, 4, 225, 7, 8, 63, 64, 248, 1, 17, 248, 1, 161, + 173, 224, 23, 219, 157, 28, 175, 242, 3, 212, 241, 128, 234, 72, 43, 70, 243, 178, 153, 124, 34, + 223, 120, 83, 165, 190, 229, 162, 39, 89, 110, 149, 165, 186, 82, 157, 205, 20, 137, 71, 123, 239, + 208, 119, 150, 200, 215, 16, 237, 145, 91, 249, 1, 118, 69, 213, 14, 49, 99, 224, 159, 198, 107, + 21, 95, 159, 82, 239, 225, 143, 231, 66, 157, 207, 87, 192, 253, 29, 54, 234, 4, 137, 219, 50, + 87, 192, 77, 254, 159, 160, 100, 171, 21, 240, 68, 159, 5, 80, 249, 149, 73, 144, 218, 50, 167, + 110, 72, 243, 47, 142, 219, 229, 85, 57, 12, 157, 17, 114, 196, 120, 78, 137, 86, 209, 136, 37, + 112, 17, 233, 8, 50, 180, 64, 57, 214, 20, 148, 197, 199, 21, 210, 125, 89, 45, 88, 122, 223, + 158, 69, 85, 195, 240, 181, 233, 120, 28, 81, 187, 253, 152, 157, 245, 91, 143, 130, 244, 166, 57, + 80, 211, 55, 14, 82, 155, 141, 131, 134, 254, 108, 104, 228, 207, 130, 103, 219, 141, 134, 122, 93, + 115, 161, 122, 151, 220, 254, 106, 0, 78, 37, 63, 128, 96, 9, 213, 208, 202, 48, 13, 226, 127, + 232, 104, 71, 60, 182, 65, 235, 128, 78, 124, 202, 57, 134, 73, 28, 153, 69, 25, 170, 241, 181, + 93, 12, 79, 88, 134, 182, 155, 229, 53, 251, 136, 49, 192, 106, 210, 32, 39, 98, 255, 51, 95, + 119, 108, 173, 68, 107, 211, 35, 15, 250, 6, 190, 133, 39, 243, 245, 66, 197, 198, 15, 32, 6, + 200, 92, 146, 195, 113, 229, 247, 114, 210, 213, 19, 38, 25, 124, 36, 101, 121, 208, 78, 115, 108, + 38, 74, 60, 217, 98, 4, 116, 234, 61, 5, 178, 198, 126, 175, 131, 20, 214, 237, 58, 80, 188, + 252, 0, 61, 8, 90, 96, 84, 204, 108, 0, 205, 86, 104, 109, 150, 220, 134, 240, 8, 94, 122, + 119, 6, 44, 248, 118, 35, 236, 229, 219, 231, 221, 194, 15, 120, 62, 66, 130, 117, 176, 57, 219, + 249, 142, 106, 81, 161, 11, 127, 162, 253, 92, 171, 195, 216, 21, 213, 62, 153, 11, 131, 191, 255, + 213, 149, 252, 128, 43, 84, 86, 128, 169, 9, 7, 99, 164, 74, 168, 239, 163, 113, 96, 45, 13, + 140, 143, 190, 51, 189, 251, 35, 95, 45, 118, 39, 63, 128, 131, 48, 223, 166, 79, 7, 29, 1, + 82, 66, 187, 199, 239, 24, 56, 162, 204, 248, 141, 228, 236, 238, 231, 7, 68, 18, 78, 59, 85, + 147, 231, 183, 30, 66, 99, 13, 32, 116, 6, 101, 170, 86, 168, 82, 174, 231, 7, 220, 111, 69, + 136, 176, 76, 192, 30, 202, 18, 49, 222, 71, 142, 19, 122, 167, 180, 205, 138, 208, 19, 252, 128, + 245, 81, 16, 26, 193, 144, 209, 93, 211, 251, 248, 22, 90, 236, 176, 230, 248, 74, 59, 69, 185, + 39, 248, 1, 120, 22, 7, 56, 236, 235, 198, 107, 11, 208, 154, 227, 255, 93, 231, 224, 154, 192, + 19, 252, 128, 90, 81, 70, 123, 106, 234, 180, 242, 155, 78, 75, 95, 236, 10, 215, 57, 21, 85, + 123, 130, 31, 128, 206, 252, 13, 237, 136, 178, 156, 5, 245, 177, 30, 128, 38, 195, 63, 69, 163, + 1, 238, 188, 24, 175, 10, 61, 195, 15, 24, 109, 58, 235, 71, 176, 175, 211, 186, 255, 237, 120, + 225, 9, 28, 0, 111, 240, 3, 208, 201, 38, 33, 206, 227, 250, 0, 91, 1, 189, 118, 51, 181, + 144, 68, 118, 135, 60, 193, 15, 32, 140, 30, 58, 76, 228, 184, 49, 140, 209, 43, 145, 232, 103, + 122, 145, 31, 240, 52, 218, 67, 124, 169, 92, 20, 187, 66, 194, 15, 16, 126, 128, 240, 3, 132, + 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, + 252, 128, 68, 191, 64, 248, 1, 194, 15, 72, 17, 126, 128, 241, 37, 194, 15, 144, 34, 69, 138, + 20, 41, 82, 164, 196, 51, 71, 11, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, + 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 206, 183, 184, 132, 31, 192, 245, 16, 126, 0, 215, + 71, 248, 1, 92, 55, 225, 7, 8, 63, 32, 69, 248, 1, 106, 16, 132, 31, 32, 252, 0, 225, + 7, 8, 63, 64, 248, 1, 236, 135, 53, 63, 128, 239, 108, 49, 248, 1, 225, 75, 94, 15, 241, + 3, 162, 46, 155, 177, 174, 84, 103, 170, 187, 121, 103, 137, 124, 100, 95, 254, 47, 164, 249, 23, + 19, 63, 224, 116, 179, 64, 194, 42, 197, 166, 249, 53, 86, 160, 219, 190, 135, 234, 74, 117, 174, + 215, 63, 220, 31, 242, 145, 253, 9, 185, 93, 254, 148, 10, 38, 176, 178, 15, 178, 58, 228, 17, + 102, 9, 60, 133, 246, 12, 90, 77, 254, 91, 77, 126, 173, 17, 115, 6, 74, 163, 165, 162, 61, + 137, 86, 15, 237, 62, 253, 127, 181, 64, 26, 26, 189, 167, 50, 218, 221, 104, 21, 232, 115, 171, + 244, 156, 56, 160, 220, 135, 115, 32, 227, 227, 149, 112, 155, 127, 37, 220, 158, 185, 28, 110, 197, + 227, 181, 254, 95, 160, 74, 207, 117, 144, 218, 127, 33, 84, 124, 109, 202, 196, 130, 236, 150, 167, + 90, 50, 131, 95, 252, 56, 218, 54, 214, 9, 253, 193, 169, 117, 215, 49, 28, 129, 20, 99, 135, + 248, 249, 47, 244, 56, 189, 201, 240, 63, 209, 209, 29, 248, 120, 13, 218, 70, 254, 191, 19, 44, + 155, 253, 49, 221, 151, 181, 149, 255, 143, 160, 10, 107, 210, 125, 129, 159, 27, 248, 179, 117, 29, + 82, 125, 255, 40, 168, 157, 57, 14, 26, 182, 200, 167, 72, 52, 110, 62, 2, 158, 236, 52, 14, + 170, 117, 203, 219, 169, 139, 178, 138, 133, 31, 144, 127, 150, 99, 209, 9, 231, 75, 106, 13, 249, + 188, 230, 80, 92, 109, 241, 190, 182, 61, 38, 64, 171, 1, 95, 67, 153, 15, 102, 67, 237, 246, + 99, 46, 40, 46, 126, 192, 105, 172, 243, 15, 169, 184, 67, 158, 64, 44, 114, 122, 200, 192, 179, + 222, 188, 115, 46, 188, 251, 201, 60, 152, 189, 224, 23, 216, 179, 247, 80, 241, 243, 3, 56, 8, + 243, 148, 172, 243, 5, 73, 151, 67, 130, 160, 69, 62, 155, 102, 103, 241, 179, 148, 96, 6, 32, + 205, 159, 5, 207, 247, 156, 8, 147, 103, 174, 129, 245, 155, 246, 192, 225, 35, 199, 92, 197, 15, + 120, 87, 81, 134, 135, 180, 2, 147, 90, 60, 104, 165, 34, 183, 105, 45, 198, 248, 177, 164, 222, + 11, 35, 71, 150, 197, 102, 222, 102, 250, 143, 174, 229, 7, 52, 42, 130, 230, 174, 183, 12, 35, + 24, 120, 92, 203, 44, 129, 90, 21, 222, 152, 90, 214, 237, 252, 128, 27, 34, 64, 17, 204, 205, + 62, 168, 18, 98, 10, 72, 49, 249, 127, 39, 209, 37, 241, 131, 58, 112, 130, 118, 131, 32, 225, + 9, 126, 192, 214, 2, 231, 76, 93, 193, 129, 81, 83, 255, 154, 29, 47, 73, 68, 41, 211, 138, + 208, 253, 252, 0, 150, 203, 198, 194, 11, 57, 198, 235, 128, 108, 94, 20, 93, 19, 1, 160, 224, + 9, 126, 64, 39, 171, 1, 205, 102, 128, 163, 69, 207, 23, 52, 118, 224, 223, 175, 117, 114, 101, + 232, 5, 126, 64, 69, 203, 49, 64, 83, 176, 56, 90, 224, 7, 60, 246, 227, 140, 244, 23, 56, + 213, 22, 123, 130, 31, 192, 20, 184, 160, 201, 105, 125, 121, 140, 175, 45, 194, 215, 122, 51, 60, + 45, 230, 31, 55, 188, 196, 15, 88, 174, 52, 251, 125, 232, 236, 28, 60, 118, 70, 123, 0, 237, + 236, 68, 118, 135, 188, 194, 15, 24, 200, 100, 24, 26, 16, 125, 104, 55, 21, 193, 158, 128, 119, + 248, 1, 204, 13, 120, 14, 237, 22, 180, 51, 139, 104, 83, 196, 59, 252, 0, 171, 13, 144, 34, + 8, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, + 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 160, 40, 190, 64, 248, 1, 194, 15, 72, 17, + 126, 128, 20, 41, 82, 164, 72, 145, 34, 37, 238, 57, 90, 248, 1, 194, 15, 16, 126, 128, 240, + 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 112, 190, 197, 37, 252, 0, + 174, 135, 240, 3, 184, 62, 194, 15, 224, 186, 9, 63, 64, 248, 1, 41, 194, 15, 80, 131, 32, + 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 96, 63, 132, 31, 224, 29, 126, 128, 22, 72, + 113, 154, 68, 53, 198, 32, 228, 243, 3, 62, 216, 225, 90, 126, 0, 129, 19, 94, 68, 171, 203, + 74, 242, 71, 88, 64, 85, 134, 83, 109, 150, 224, 0, 157, 203, 66, 169, 115, 45, 64, 11, 41, + 118, 2, 42, 170, 43, 213, 153, 234, 126, 89, 179, 208, 147, 74, 62, 234, 45, 218, 36, 152, 56, + 229, 146, 25, 172, 252, 10, 116, 240, 4, 139, 164, 72, 253, 185, 20, 159, 207, 194, 35, 217, 18, + 52, 210, 14, 13, 73, 111, 50, 188, 39, 30, 219, 226, 223, 218, 225, 177, 13, 218, 179, 104, 143, + 161, 93, 202, 234, 211, 187, 208, 254, 203, 73, 152, 207, 52, 46, 165, 31, 24, 182, 16, 106, 127, + 185, 10, 238, 233, 108, 26, 208, 241, 57, 249, 90, 32, 153, 41, 46, 209, 20, 86, 118, 182, 141, + 72, 210, 72, 191, 125, 92, 73, 198, 30, 100, 208, 194, 86, 124, 109, 3, 6, 101, 7, 43, 205, + 54, 243, 231, 140, 196, 231, 195, 241, 152, 131, 175, 209, 241, 237, 74, 125, 38, 255, 82, 243, 189, + 185, 80, 251, 229, 31, 224, 142, 204, 229, 112, 155, 127, 5, 220, 224, 255, 25, 174, 107, 177, 25, + 7, 200, 13, 80, 106, 208, 124, 168, 219, 102, 212, 229, 197, 194, 15, 160, 130, 78, 12, 86, 69, + 146, 138, 88, 50, 24, 3, 39, 192, 252, 222, 227, 28, 168, 99, 249, 162, 106, 52, 127, 54, 212, + 201, 28, 3, 13, 90, 228, 66, 227, 214, 185, 240, 84, 235, 49, 240, 84, 155, 49, 80, 171, 91, + 30, 1, 20, 218, 21, 39, 63, 160, 75, 132, 228, 234, 102, 33, 101, 48, 74, 6, 122, 199, 127, + 107, 228, 207, 130, 103, 219, 141, 134, 122, 175, 76, 130, 170, 47, 79, 232, 94, 156, 252, 128, 70, + 49, 211, 32, 204, 103, 63, 66, 96, 210, 116, 133, 121, 254, 227, 38, 232, 240, 203, 111, 207, 128, + 175, 70, 46, 131, 21, 63, 110, 119, 13, 63, 160, 20, 159, 165, 96, 188, 236, 128, 200, 103, 63, + 112, 52, 181, 85, 14, 60, 217, 115, 34, 140, 158, 180, 74, 119, 252, 183, 221, 7, 225, 232, 177, + 63, 93, 195, 15, 184, 48, 74, 23, 8, 154, 1, 9, 42, 64, 133, 73, 19, 65, 243, 255, 226, + 235, 68, 146, 153, 89, 191, 197, 136, 55, 202, 12, 248, 26, 124, 19, 87, 184, 154, 31, 176, 39, + 66, 16, 130, 166, 51, 28, 180, 57, 235, 135, 48, 40, 68, 142, 24, 79, 227, 10, 254, 189, 44, + 30, 207, 247, 10, 63, 224, 219, 144, 209, 95, 139, 136, 193, 9, 42, 173, 64, 63, 243, 104, 191, + 225, 255, 143, 196, 215, 155, 179, 2, 245, 34, 99, 101, 233, 9, 126, 0, 58, 144, 165, 76, 103, + 65, 139, 179, 14, 42, 105, 6, 237, 48, 62, 255, 137, 57, 2, 180, 32, 186, 42, 2, 64, 193, + 19, 252, 128, 62, 150, 103, 62, 116, 6, 32, 84, 134, 209, 196, 187, 50, 54, 235, 106, 180, 51, + 162, 92, 15, 120, 130, 31, 240, 188, 29, 48, 133, 109, 55, 190, 62, 132, 185, 98, 183, 199, 34, + 167, 247, 4, 63, 128, 215, 244, 234, 42, 110, 23, 218, 2, 38, 70, 212, 53, 136, 48, 241, 238, + 14, 185, 158, 31, 192, 16, 148, 32, 243, 3, 166, 161, 245, 208, 233, 114, 90, 224, 122, 60, 254, + 35, 1, 231, 61, 197, 15, 232, 141, 14, 87, 137, 133, 15, 226, 32, 0, 158, 226, 7, 148, 56, + 25, 27, 34, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 144, 232, 23, 8, 63, 64, 248, 1, 41, 194, 15, + 48, 190, 68, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 120, 230, 104, 225, 7, 8, 63, 64, + 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 192, 249, 22, + 151, 240, 3, 184, 30, 194, 15, 224, 250, 8, 63, 128, 235, 38, 252, 0, 225, 7, 164, 8, 63, + 64, 13, 130, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 128, 253, 16, 126, 128, 103, 248, + 1, 84, 56, 15, 121, 137, 34, 14, 130, 187, 249, 1, 236, 120, 169, 116, 95, 86, 203, 12, 45, + 80, 30, 31, 223, 200, 234, 145, 107, 88, 22, 127, 118, 130, 1, 112, 63, 63, 0, 157, 127, 146, + 117, 66, 164, 23, 58, 136, 207, 41, 187, 108, 54, 30, 251, 96, 80, 154, 163, 53, 198, 231, 117, + 8, 170, 128, 175, 221, 100, 78, 172, 236, 224, 82, 90, 175, 123, 152, 79, 46, 226, 7, 164, 42, + 74, 208, 2, 189, 160, 162, 32, 61, 136, 182, 18, 109, 6, 241, 1, 208, 62, 231, 132, 237, 109, + 57, 48, 55, 25, 34, 73, 38, 82, 156, 161, 226, 56, 168, 206, 84, 119, 242, 193, 60, 27, 144, + 175, 60, 163, 253, 189, 216, 248, 1, 88, 217, 10, 54, 82, 88, 59, 49, 245, 113, 124, 239, 126, + 22, 88, 173, 231, 68, 204, 164, 39, 156, 136, 246, 58, 218, 19, 104, 15, 163, 221, 129, 118, 219, + 35, 31, 126, 61, 248, 177, 207, 105, 77, 96, 45, 7, 124, 36, 127, 105, 124, 71, 113, 242, 3, + 238, 143, 146, 116, 217, 137, 106, 60, 104, 116, 35, 126, 237, 79, 162, 76, 96, 43, 154, 81, 183, + 205, 168, 57, 85, 95, 157, 2, 25, 125, 22, 193, 157, 153, 203, 224, 86, 255, 74, 157, 32, 113, + 133, 159, 127, 176, 201, 223, 48, 169, 81, 156, 252, 128, 155, 194, 156, 211, 18, 144, 209, 107, 182, + 50, 122, 72, 205, 28, 5, 245, 91, 228, 65, 70, 155, 41, 240, 116, 199, 233, 160, 245, 156, 1, + 169, 125, 166, 66, 181, 238, 121, 131, 139, 147, 31, 112, 113, 188, 220, 128, 16, 149, 169, 102, 33, + 174, 142, 242, 255, 79, 189, 48, 18, 50, 186, 230, 66, 141, 206, 227, 39, 20, 27, 63, 0, 155, + 233, 89, 81, 156, 139, 55, 40, 60, 168, 22, 190, 246, 124, 219, 81, 208, 169, 207, 20, 232, 63, + 120, 1, 140, 155, 188, 26, 190, 156, 253, 51, 52, 10, 124, 11, 15, 127, 58, 183, 119, 113, 243, + 3, 14, 71, 66, 103, 176, 98, 220, 30, 175, 17, 174, 55, 38, 89, 253, 159, 248, 63, 187, 210, + 51, 3, 107, 107, 117, 24, 11, 79, 190, 54, 5, 70, 78, 90, 9, 75, 150, 111, 129, 77, 91, + 246, 194, 129, 131, 71, 92, 197, 15, 216, 110, 57, 234, 107, 17, 206, 178, 102, 137, 207, 56, 129, + 207, 15, 224, 241, 87, 60, 18, 135, 232, 205, 58, 47, 142, 110, 87, 250, 227, 57, 208, 116, 242, + 42, 247, 242, 3, 176, 162, 107, 172, 166, 63, 229, 204, 7, 237, 88, 67, 74, 144, 86, 226, 227, + 79, 240, 248, 36, 167, 239, 254, 187, 113, 101, 232, 5, 126, 192, 55, 198, 34, 168, 192, 84, 148, + 70, 225, 25, 55, 22, 74, 191, 19, 117, 2, 109, 16, 39, 106, 191, 205, 110, 133, 232, 21, 126, + 192, 52, 181, 89, 91, 112, 4, 118, 235, 136, 45, 95, 96, 4, 6, 128, 148, 229, 105, 104, 37, + 153, 28, 113, 46, 95, 72, 217, 45, 135, 61, 193, 15, 24, 105, 48, 65, 120, 65, 67, 103, 120, + 61, 182, 130, 249, 120, 28, 206, 176, 165, 84, 230, 133, 93, 16, 11, 121, 206, 19, 252, 0, 198, + 105, 145, 227, 123, 49, 8, 223, 225, 241, 99, 166, 74, 208, 89, 142, 251, 39, 45, 207, 240, 3, + 176, 89, 215, 213, 251, 178, 166, 211, 224, 254, 81, 84, 159, 235, 25, 126, 192, 201, 128, 41, 26, + 27, 34, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, + 194, 15, 16, 126, 128, 240, 3, 132, 31, 144, 232, 23, 8, 63, 64, 248, 1, 41, 194, 15, 48, + 190, 68, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 120, 230, 104, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 192, 249, 22, 151, + 240, 3, 184, 30, 194, 15, 224, 250, 8, 63, 128, 235, 38, 252, 0, 225, 7, 164, 8, 63, 64, + 13, 130, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 128, 253, 16, 126, 128, 103, 248, 1, + 39, 35, 213, 30, 251, 227, 126, 126, 0, 7, 160, 61, 90, 67, 180, 107, 139, 82, 59, 224, 9, + 126, 0, 7, 224, 23, 210, 252, 161, 29, 225, 116, 186, 195, 56, 135, 240, 131, 209, 178, 202, 58, + 184, 148, 118, 55, 63, 128, 3, 240, 171, 162, 249, 165, 64, 28, 69, 35, 1, 228, 78, 150, 200, + 143, 66, 235, 140, 127, 171, 129, 199, 43, 73, 37, 22, 203, 148, 232, 106, 126, 0, 7, 96, 123, + 72, 54, 121, 83, 26, 110, 60, 30, 70, 231, 119, 51, 43, 96, 57, 90, 46, 231, 42, 126, 28, + 255, 70, 74, 178, 51, 35, 201, 230, 168, 238, 228, 131, 43, 249, 1, 28, 128, 221, 170, 102, 176, + 64, 52, 169, 217, 38, 97, 62, 202, 169, 121, 127, 70, 91, 148, 238, 203, 202, 195, 227, 91, 104, + 255, 67, 123, 132, 249, 35, 37, 140, 113, 128, 234, 78, 27, 173, 86, 3, 97, 177, 243, 3, 56, + 0, 251, 11, 180, 192, 228, 188, 73, 12, 109, 82, 145, 6, 109, 216, 1, 123, 57, 32, 243, 24, + 181, 241, 14, 154, 191, 90, 247, 188, 118, 15, 125, 58, 15, 210, 134, 173, 13, 243, 139, 124, 37, + 159, 13, 233, 108, 113, 138, 167, 15, 43, 142, 4, 45, 20, 164, 193, 8, 146, 249, 160, 254, 63, + 161, 226, 106, 122, 237, 15, 26, 63, 210, 124, 89, 75, 171, 119, 206, 133, 180, 94, 51, 225, 254, + 102, 75, 116, 130, 196, 117, 254, 117, 112, 121, 230, 38, 221, 87, 242, 217, 16, 79, 23, 11, 63, + 128, 3, 112, 60, 146, 124, 222, 82, 66, 175, 133, 80, 35, 130, 10, 61, 66, 77, 220, 30, 146, + 196, 61, 205, 63, 10, 82, 91, 228, 65, 106, 155, 153, 144, 250, 210, 66, 120, 246, 77, 92, 243, + 124, 184, 0, 202, 191, 55, 227, 131, 98, 227, 7, 208, 136, 94, 48, 216, 229, 59, 25, 140, 17, + 154, 16, 140, 151, 64, 241, 84, 171, 28, 131, 32, 49, 189, 56, 17, 26, 103, 218, 194, 17, 180, + 24, 29, 143, 66, 157, 208, 218, 141, 134, 151, 94, 157, 12, 125, 250, 206, 134, 15, 134, 44, 132, + 62, 159, 45, 132, 140, 126, 179, 160, 82, 159, 201, 131, 138, 109, 16, 196, 179, 127, 110, 152, 84, + 222, 217, 25, 14, 70, 9, 206, 62, 180, 101, 245, 155, 143, 152, 95, 173, 199, 4, 208, 250, 207, + 134, 41, 179, 127, 134, 197, 75, 127, 133, 181, 235, 127, 131, 61, 123, 15, 185, 131, 31, 128, 149, + 252, 183, 169, 5, 4, 99, 101, 136, 224, 255, 31, 231, 21, 100, 30, 254, 95, 63, 12, 104, 59, + 52, 34, 80, 85, 125, 236, 173, 105, 61, 203, 226, 217, 238, 189, 224, 23, 119, 242, 3, 176, 242, + 23, 199, 216, 143, 79, 160, 147, 219, 240, 56, 29, 237, 3, 180, 23, 208, 106, 163, 149, 67, 187, + 27, 255, 70, 28, 178, 115, 213, 133, 144, 171, 249, 1, 88, 217, 43, 194, 206, 184, 22, 194, 6, + 57, 138, 143, 23, 225, 241, 83, 118, 182, 42, 218, 61, 248, 158, 255, 224, 89, 38, 128, 194, 121, + 145, 174, 38, 93, 207, 15, 32, 114, 28, 58, 114, 130, 157, 37, 2, 212, 26, 116, 120, 56, 95, + 33, 86, 209, 201, 114, 154, 190, 254, 191, 24, 95, 255, 167, 193, 6, 137, 229, 98, 200, 213, 252, + 0, 38, 66, 116, 71, 171, 206, 23, 58, 103, 19, 15, 132, 56, 2, 177, 56, 107, 119, 57, 236, + 9, 126, 0, 7, 34, 197, 238, 130, 38, 145, 13, 17, 225, 7, 8, 63, 64, 248, 1, 194, 15, + 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 72, 244, + 11, 132, 31, 32, 252, 128, 20, 225, 7, 24, 95, 34, 252, 0, 41, 82, 164, 72, 145, 34, 69, + 74, 60, 115, 180, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, + 126, 128, 240, 3, 132, 31, 224, 124, 139, 75, 248, 1, 92, 15, 225, 7, 112, 125, 132, 31, 192, + 117, 19, 126, 128, 240, 3, 82, 132, 31, 160, 6, 65, 248, 1, 194, 15, 16, 126, 128, 240, 3, + 132, 31, 192, 126, 8, 63, 192, 75, 252, 128, 211, 78, 82, 16, 60, 195, 15, 32, 249, 203, 75, + 156, 52, 253, 159, 69, 24, 0, 207, 240, 3, 158, 102, 245, 232, 118, 12, 196, 28, 60, 190, 205, + 64, 133, 59, 19, 9, 136, 151, 248, 1, 29, 20, 145, 212, 159, 156, 101, 246, 123, 180, 108, 52, + 106, 25, 149, 48, 48, 55, 100, 104, 122, 134, 217, 152, 167, 68, 215, 243, 3, 208, 185, 247, 76, + 58, 192, 160, 73, 56, 189, 3, 109, 50, 107, 139, 30, 142, 37, 17, 171, 39, 248, 1, 24, 128, + 172, 24, 181, 194, 63, 113, 210, 229, 231, 48, 72, 247, 81, 55, 137, 0, 80, 240, 4, 63, 96, + 54, 201, 103, 21, 225, 116, 161, 132, 94, 179, 212, 20, 159, 208, 153, 3, 249, 188, 128, 201, 60, + 102, 144, 82, 244, 126, 134, 39, 156, 97, 4, 68, 191, 72, 194, 186, 147, 15, 110, 230, 7, 172, + 177, 200, 39, 30, 140, 144, 113, 222, 202, 182, 225, 255, 145, 154, 244, 53, 18, 87, 226, 255, 254, + 31, 7, 128, 54, 90, 117, 31, 66, 46, 147, 253, 219, 92, 197, 15, 56, 96, 202, 36, 31, 52, + 1, 18, 28, 137, 167, 85, 134, 0, 15, 164, 35, 82, 91, 229, 116, 41, 253, 225, 28, 72, 29, + 250, 163, 238, 139, 234, 27, 249, 74, 62, 235, 59, 69, 197, 200, 15, 248, 103, 136, 19, 170, 146, + 188, 240, 53, 115, 246, 249, 130, 99, 8, 114, 67, 203, 255, 127, 37, 99, 253, 145, 116, 95, 96, + 103, 221, 182, 163, 32, 173, 235, 20, 120, 168, 197, 119, 112, 139, 127, 53, 92, 237, 223, 0, 151, + 250, 183, 134, 5, 160, 184, 248, 1, 55, 71, 109, 222, 90, 236, 192, 4, 181, 213, 164, 27, 199, + 166, 163, 32, 181, 229, 36, 168, 214, 122, 46, 60, 252, 194, 114, 168, 251, 214, 70, 168, 241, 229, + 15, 5, 93, 160, 88, 6, 65, 86, 125, 7, 163, 142, 252, 90, 108, 12, 1, 35, 0, 233, 54, + 50, 252, 134, 153, 1, 120, 186, 245, 72, 108, 25, 185, 80, 163, 203, 248, 175, 138, 147, 31, 208, + 48, 226, 96, 167, 133, 114, 68, 226, 197, 106, 52, 244, 103, 65, 179, 142, 227, 160, 125, 175, 73, + 208, 233, 213, 41, 208, 177, 247, 100, 104, 218, 99, 2, 164, 118, 207, 131, 154, 29, 199, 245, 43, + 54, 126, 0, 75, 226, 99, 117, 204, 76, 141, 57, 196, 52, 42, 154, 77, 214, 242, 227, 213, 104, + 89, 120, 118, 23, 215, 124, 123, 58, 244, 202, 254, 14, 102, 205, 255, 5, 22, 45, 217, 4, 107, + 214, 229, 19, 36, 12, 126, 192, 67, 131, 230, 221, 83, 108, 75, 97, 28, 180, 94, 143, 114, 102, + 15, 225, 223, 119, 240, 106, 240, 32, 207, 255, 180, 76, 206, 65, 235, 139, 246, 50, 90, 39, 180, + 150, 248, 89, 25, 120, 44, 73, 84, 10, 186, 194, 68, 251, 215, 131, 131, 231, 47, 174, 147, 179, + 4, 166, 111, 216, 229, 90, 126, 192, 199, 10, 70, 135, 96, 72, 75, 209, 198, 161, 13, 225, 191, + 145, 147, 93, 208, 158, 196, 64, 220, 139, 199, 75, 72, 102, 143, 206, 94, 199, 114, 251, 115, 237, + 0, 10, 94, 225, 7, 52, 66, 123, 10, 29, 122, 152, 184, 96, 232, 36, 45, 109, 137, 15, 118, + 61, 49, 4, 44, 222, 31, 211, 229, 176, 151, 248, 1, 167, 11, 63, 160, 136, 183, 196, 132, 31, + 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, + 0, 225, 7, 8, 63, 160, 168, 190, 64, 248, 1, 194, 15, 72, 17, 126, 128, 20, 41, 82, 164, + 72, 145, 34, 37, 238, 57, 90, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, + 225, 7, 8, 63, 64, 248, 1, 194, 15, 112, 190, 197, 37, 252, 0, 174, 135, 240, 3, 184, 62, + 194, 15, 224, 186, 9, 63, 64, 248, 1, 41, 194, 15, 80, 131, 32, 252, 0, 225, 7, 8, 63, + 64, 248, 1, 194, 15, 96, 63, 132, 31, 224, 25, 126, 0, 149, 147, 193, 16, 240, 12, 63, 128, + 74, 186, 47, 235, 25, 12, 66, 3, 52, 74, 163, 249, 183, 34, 10, 128, 55, 248, 1, 220, 2, + 72, 236, 180, 25, 3, 145, 155, 222, 100, 120, 91, 124, 252, 32, 105, 127, 18, 12, 128, 103, 248, + 1, 255, 103, 82, 134, 237, 65, 35, 144, 194, 43, 28, 136, 211, 19, 153, 18, 93, 207, 15, 64, + 7, 31, 178, 148, 199, 229, 139, 30, 73, 37, 54, 3, 31, 183, 225, 247, 93, 24, 203, 120, 225, + 9, 126, 0, 58, 164, 69, 209, 11, 18, 85, 98, 3, 218, 120, 52, 10, 196, 141, 177, 140, 3, + 238, 231, 7, 104, 129, 247, 99, 145, 202, 227, 24, 65, 66, 201, 92, 52, 63, 218, 189, 81, 0, + 10, 158, 224, 7, 204, 80, 208, 25, 65, 135, 2, 233, 63, 24, 160, 144, 131, 1, 108, 74, 34, + 108, 155, 0, 120, 130, 31, 176, 211, 224, 4, 152, 91, 130, 89, 78, 207, 140, 128, 160, 89, 87, + 140, 182, 17, 237, 115, 166, 207, 220, 64, 20, 9, 14, 0, 237, 54, 67, 234, 151, 63, 187, 150, + 31, 112, 69, 132, 179, 174, 106, 133, 195, 64, 10, 22, 221, 230, 24, 218, 58, 150, 213, 54, 195, + 191, 223, 83, 246, 253, 89, 55, 59, 13, 64, 113, 241, 3, 170, 90, 65, 19, 20, 18, 68, 180, + 140, 244, 36, 173, 183, 162, 76, 28, 192, 207, 93, 144, 218, 60, 231, 147, 50, 31, 204, 134, 250, + 67, 127, 138, 218, 5, 138, 139, 31, 208, 46, 66, 159, 15, 90, 62, 214, 2, 177, 176, 69, 160, + 126, 243, 17, 144, 222, 97, 34, 220, 235, 251, 22, 110, 240, 255, 12, 151, 251, 55, 235, 126, 213, + 234, 187, 61, 100, 16, 44, 46, 126, 192, 16, 171, 25, 64, 193, 102, 128, 173, 227, 90, 97, 75, + 177, 108, 69, 5, 153, 236, 241, 152, 137, 214, 124, 12, 212, 105, 53, 13, 30, 106, 241, 173, 142, + 209, 48, 79, 131, 197, 197, 15, 248, 46, 130, 163, 97, 228, 8, 59, 208, 138, 93, 43, 178, 35, + 72, 60, 129, 173, 194, 215, 107, 10, 84, 125, 123, 58, 84, 126, 101, 82, 237, 98, 91, 10, 99, + 101, 142, 42, 253, 61, 24, 66, 142, 209, 10, 193, 40, 138, 163, 39, 44, 167, 68, 77, 31, 0, + 141, 224, 29, 198, 227, 78, 50, 131, 31, 242, 100, 203, 17, 208, 170, 107, 46, 180, 233, 49, 1, + 50, 219, 143, 197, 0, 100, 195, 51, 47, 141, 133, 106, 221, 243, 32, 173, 105, 86, 229, 98, 185, + 24, 226, 233, 106, 167, 178, 218, 35, 22, 208, 15, 108, 91, 153, 43, 64, 176, 132, 41, 104, 19, + 209, 230, 163, 253, 160, 63, 214, 2, 3, 241, 248, 1, 79, 125, 95, 160, 245, 103, 238, 88, 11, + 252, 91, 123, 60, 118, 110, 208, 44, 251, 165, 114, 239, 207, 132, 199, 135, 46, 132, 9, 243, 127, + 129, 57, 139, 214, 195, 226, 165, 191, 194, 186, 13, 187, 96, 197, 175, 123, 160, 213, 216, 239, 161, + 76, 255, 217, 68, 165, 186, 172, 88, 46, 135, 241, 139, 175, 225, 101, 112, 69, 60, 203, 4, 83, + 169, 141, 103, 190, 62, 30, 43, 51, 93, 230, 95, 104, 255, 69, 123, 128, 143, 23, 240, 165, 243, + 249, 252, 191, 151, 169, 215, 5, 124, 81, 117, 78, 200, 50, 248, 171, 197, 208, 241, 235, 53, 238, + 230, 7, 48, 238, 226, 164, 108, 136, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, + 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 64, 162, 95, 32, 252, 0, + 225, 7, 164, 8, 63, 192, 248, 18, 225, 7, 72, 145, 34, 69, 138, 20, 41, 82, 226, 153, 163, + 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, + 32, 252, 0, 231, 91, 92, 194, 15, 224, 122, 8, 63, 128, 235, 35, 252, 0, 174, 155, 240, 3, + 132, 31, 144, 34, 252, 0, 53, 8, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 246, + 67, 248, 1, 158, 226, 7, 156, 164, 32, 120, 135, 31, 192, 105, 50, 219, 112, 178, 197, 179, 138, + 34, 247, 160, 215, 248, 1, 79, 232, 146, 25, 95, 96, 180, 158, 129, 210, 151, 245, 239, 34, 8, + 128, 55, 248, 1, 28, 128, 129, 170, 232, 49, 67, 11, 44, 224, 22, 113, 55, 218, 217, 137, 76, + 137, 174, 231, 7, 112, 0, 126, 14, 75, 154, 236, 203, 34, 61, 112, 0, 173, 126, 188, 88, 13, + 175, 240, 3, 110, 12, 19, 73, 135, 74, 229, 232, 248, 45, 182, 138, 23, 120, 140, 56, 59, 150, + 113, 192, 253, 252, 0, 95, 224, 121, 7, 146, 249, 227, 204, 25, 25, 138, 86, 211, 233, 32, 233, + 9, 126, 0, 158, 237, 97, 78, 84, 160, 138, 29, 69, 91, 72, 201, 150, 209, 238, 180, 74, 207, + 171, 4, 192, 19, 252, 128, 95, 163, 50, 3, 194, 229, 181, 20, 4, 18, 61, 14, 198, 191, 87, + 177, 147, 221, 121, 129, 31, 112, 123, 212, 228, 233, 154, 125, 118, 121, 69, 95, 252, 53, 35, 53, + 110, 199, 231, 103, 198, 19, 128, 226, 226, 7, 52, 115, 152, 69, 62, 24, 229, 239, 135, 177, 229, + 172, 194, 227, 39, 120, 124, 140, 22, 83, 33, 93, 224, 203, 181, 174, 229, 7, 140, 136, 0, 78, + 176, 151, 205, 71, 2, 43, 228, 163, 53, 166, 147, 44, 183, 194, 27, 83, 159, 44, 57, 108, 145, + 190, 232, 49, 251, 229, 22, 126, 192, 78, 139, 62, 30, 140, 7, 148, 96, 146, 219, 31, 70, 91, + 89, 167, 237, 232, 89, 143, 246, 159, 3, 25, 159, 109, 140, 58, 13, 158, 114, 126, 0, 86, 240, + 62, 91, 118, 128, 2, 71, 80, 30, 135, 181, 16, 211, 128, 25, 52, 15, 166, 233, 190, 124, 128, + 66, 90, 251, 41, 112, 103, 230, 50, 29, 156, 80, 112, 121, 111, 90, 8, 157, 242, 165, 48, 45, + 117, 109, 201, 15, 118, 12, 161, 208, 214, 226, 20, 185, 3, 13, 253, 217, 208, 176, 213, 120, 168, + 210, 106, 14, 220, 156, 249, 35, 92, 219, 98, 115, 232, 82, 184, 56, 46, 134, 176, 98, 239, 17, + 64, 141, 207, 236, 126, 253, 177, 86, 192, 19, 160, 51, 120, 16, 237, 119, 149, 40, 133, 207, 15, + 155, 156, 59, 97, 213, 125, 212, 64, 153, 187, 216, 83, 45, 115, 160, 221, 235, 179, 224, 153, 143, + 22, 195, 67, 3, 231, 253, 80, 108, 151, 195, 88, 153, 58, 104, 213, 24, 152, 214, 0, 157, 111, + 142, 71, 130, 34, 53, 98, 174, 64, 47, 180, 215, 209, 58, 161, 117, 65, 71, 6, 224, 49, 27, + 143, 3, 25, 152, 64, 215, 9, 11, 120, 192, 35, 116, 206, 4, 12, 208, 106, 60, 174, 71, 155, + 155, 218, 34, 103, 109, 125, 116, 54, 195, 159, 5, 205, 59, 141, 215, 233, 17, 77, 218, 141, 46, + 8, 66, 163, 46, 227, 161, 70, 151, 241, 223, 16, 137, 170, 88, 55, 68, 204, 180, 56, 124, 126, + 166, 186, 212, 69, 167, 206, 101, 92, 214, 233, 202, 222, 193, 85, 252, 90, 9, 12, 200, 197, 188, + 158, 184, 137, 95, 35, 54, 209, 67, 229, 222, 159, 249, 77, 181, 207, 23, 194, 176, 197, 235, 97, + 201, 242, 45, 48, 239, 155, 13, 176, 244, 135, 45, 176, 122, 205, 14, 120, 111, 252, 114, 168, 245, + 206, 116, 168, 222, 53, 247, 125, 135, 91, 98, 201, 195, 15, 216, 117, 240, 136, 240, 3, 132, 31, + 32, 252, 0, 225, 7, 8, 63, 192, 209, 207, 227, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, + 252, 0, 225, 7, 8, 63, 192, 248, 2, 225, 7, 8, 63, 32, 69, 248, 1, 82, 164, 72, 145, + 34, 69, 138, 148, 184, 231, 104, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, + 132, 31, 32, 252, 0, 225, 7, 8, 63, 192, 249, 22, 151, 240, 3, 184, 30, 194, 15, 224, 250, + 8, 63, 128, 235, 38, 252, 0, 225, 7, 164, 8, 63, 64, 13, 130, 240, 3, 132, 31, 32, 252, + 0, 225, 7, 8, 63, 128, 253, 16, 126, 128, 240, 3, 188, 196, 15, 160, 146, 225, 11, 148, 65, + 35, 201, 219, 191, 138, 40, 0, 222, 225, 7, 112, 0, 72, 252, 152, 135, 150, 65, 162, 137, 34, + 8, 128, 167, 248, 1, 247, 168, 186, 159, 244, 38, 195, 167, 113, 26, 237, 139, 19, 157, 18, 189, + 194, 15, 232, 101, 210, 255, 29, 100, 129, 52, 233, 133, 110, 73, 32, 0, 238, 231, 7, 112, 0, + 86, 69, 144, 204, 46, 98, 17, 213, 133, 241, 140, 3, 94, 224, 7, 148, 178, 202, 40, 173, 100, + 163, 167, 32, 108, 193, 215, 190, 164, 204, 180, 212, 45, 72, 44, 229, 248, 34, 201, 237, 252, 0, + 116, 230, 141, 24, 164, 177, 139, 153, 45, 114, 174, 195, 0, 120, 130, 31, 176, 214, 46, 111, 184, + 77, 32, 40, 73, 243, 106, 61, 201, 114, 190, 84, 46, 82, 0, 92, 207, 15, 40, 99, 238, 247, + 145, 18, 169, 43, 74, 80, 10, 194, 10, 124, 239, 91, 120, 252, 79, 81, 0, 20, 138, 139, 31, + 240, 94, 84, 128, 130, 141, 66, 220, 8, 20, 30, 183, 227, 177, 39, 137, 39, 109, 187, 128, 139, + 249, 1, 155, 76, 243, 191, 245, 153, 143, 46, 165, 167, 32, 140, 226, 181, 195, 229, 70, 139, 48, + 6, 65, 87, 242, 3, 40, 231, 184, 165, 240, 185, 201, 240, 160, 169, 37, 4, 237, 90, 136, 69, + 192, 54, 160, 189, 67, 221, 34, 214, 105, 176, 56, 248, 1, 3, 28, 194, 16, 34, 33, 52, 130, + 22, 45, 230, 24, 6, 134, 192, 76, 61, 202, 245, 157, 249, 213, 163, 95, 44, 129, 180, 79, 194, + 253, 114, 3, 63, 96, 71, 76, 116, 136, 104, 51, 69, 120, 55, 217, 84, 179, 227, 184, 157, 213, + 63, 92, 4, 53, 222, 217, 22, 178, 187, 117, 85, 203, 237, 197, 203, 15, 192, 202, 222, 197, 23, + 63, 91, 121, 68, 223, 141, 182, 207, 82, 243, 239, 4, 165, 161, 41, 173, 193, 226, 253, 85, 91, + 205, 134, 155, 253, 63, 194, 165, 254, 173, 5, 62, 133, 92, 12, 157, 234, 203, 97, 125, 53, 167, + 5, 202, 18, 44, 13, 157, 173, 200, 139, 155, 87, 208, 94, 198, 230, 75, 124, 128, 229, 104, 180, + 232, 153, 199, 3, 37, 57, 71, 144, 133, 125, 220, 239, 131, 78, 121, 35, 244, 254, 198, 205, 71, + 66, 70, 219, 169, 112, 119, 211, 101, 112, 149, 127, 35, 84, 124, 125, 135, 229, 229, 112, 113, 241, + 3, 74, 40, 143, 47, 52, 128, 6, 120, 188, 21, 173, 2, 218, 195, 122, 144, 52, 61, 72, 111, + 162, 245, 97, 250, 220, 114, 116, 142, 214, 2, 11, 25, 154, 64, 14, 255, 129, 246, 187, 93, 48, + 30, 111, 154, 5, 29, 222, 248, 26, 90, 13, 90, 5, 21, 134, 135, 111, 136, 184, 150, 31, 160, + 66, 22, 240, 241, 69, 108, 167, 161, 221, 140, 86, 153, 102, 20, 12, 68, 37, 110, 73, 125, 83, + 91, 229, 124, 85, 189, 91, 30, 52, 108, 63, 6, 252, 47, 141, 131, 246, 189, 38, 66, 139, 206, + 227, 245, 32, 60, 209, 124, 4, 60, 211, 113, 28, 84, 237, 145, 7, 117, 219, 142, 42, 149, 52, + 155, 162, 42, 114, 239, 161, 65, 243, 70, 150, 255, 124, 33, 124, 240, 221, 70, 216, 186, 227, 119, + 248, 110, 249, 102, 88, 188, 116, 19, 30, 183, 192, 71, 163, 150, 193, 19, 175, 79, 33, 122, 4, + 161, 184, 174, 142, 109, 91, 220, 227, 252, 128, 253, 135, 143, 22, 240, 3, 74, 127, 60, 231, 153, + 136, 63, 140, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, + 7, 8, 63, 32, 69, 248, 1, 198, 23, 8, 63, 64, 248, 1, 41, 194, 15, 144, 34, 69, 138, + 20, 41, 82, 164, 196, 61, 71, 11, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, + 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 206, 183, 184, 132, 31, 192, 245, 16, 126, 0, 215, + 71, 248, 1, 92, 55, 225, 7, 8, 63, 32, 69, 248, 1, 106, 16, 132, 31, 32, 252, 0, 225, + 7, 8, 63, 64, 248, 1, 236, 135, 240, 3, 132, 31, 224, 53, 126, 0, 21, 190, 245, 253, 162, + 34, 10, 128, 231, 248, 1, 213, 209, 230, 160, 253, 143, 147, 42, 38, 26, 0, 239, 240, 3, 56, + 0, 227, 12, 209, 52, 62, 38, 161, 116, 169, 34, 104, 5, 158, 225, 7, 252, 215, 148, 93, 246, + 32, 183, 134, 38, 104, 215, 196, 219, 26, 188, 196, 15, 120, 63, 76, 15, 152, 175, 24, 93, 129, + 214, 94, 85, 131, 196, 58, 14, 120, 129, 31, 112, 174, 158, 59, 188, 144, 21, 96, 37, 120, 154, + 143, 86, 15, 237, 140, 152, 47, 146, 60, 192, 15, 104, 29, 69, 38, 75, 118, 136, 187, 4, 37, + 107, 191, 222, 156, 169, 54, 66, 0, 60, 193, 15, 88, 165, 166, 214, 182, 200, 69, 174, 218, 111, + 233, 77, 134, 191, 75, 169, 117, 29, 6, 192, 245, 252, 128, 122, 86, 162, 71, 12, 66, 48, 76, + 28, 173, 133, 0, 20, 72, 27, 220, 4, 223, 119, 89, 81, 5, 160, 184, 248, 1, 19, 116, 39, + 237, 149, 159, 65, 149, 37, 162, 168, 70, 143, 161, 81, 214, 232, 182, 60, 75, 148, 136, 216, 5, + 220, 200, 15, 8, 99, 7, 21, 54, 253, 96, 148, 110, 96, 180, 16, 99, 188, 32, 110, 64, 233, + 72, 131, 160, 91, 249, 1, 3, 248, 204, 7, 45, 84, 226, 65, 187, 228, 233, 38, 148, 6, 217, + 113, 124, 141, 180, 198, 237, 136, 41, 18, 239, 52, 120, 74, 249, 1, 188, 212, 253, 83, 119, 66, + 179, 103, 135, 88, 188, 102, 213, 58, 140, 96, 253, 200, 40, 141, 203, 194, 22, 66, 110, 227, 7, + 96, 37, 219, 219, 52, 109, 39, 252, 128, 96, 20, 105, 253, 55, 140, 210, 248, 187, 177, 20, 174, + 241, 238, 14, 151, 241, 3, 124, 129, 87, 208, 217, 92, 180, 223, 89, 22, 191, 39, 66, 127, 47, + 4, 166, 240, 236, 16, 5, 180, 112, 20, 3, 179, 172, 65, 211, 236, 62, 165, 63, 153, 11, 181, + 191, 92, 109, 233, 83, 113, 243, 3, 174, 64, 187, 131, 143, 79, 232, 178, 120, 45, 208, 159, 105, + 114, 52, 218, 239, 100, 168, 130, 10, 72, 176, 166, 71, 216, 204, 32, 105, 153, 89, 7, 171, 119, + 207, 131, 140, 65, 63, 134, 221, 99, 96, 119, 57, 124, 202, 55, 68, 76, 236, 128, 43, 209, 174, + 101, 134, 192, 83, 232, 88, 15, 166, 76, 77, 226, 22, 178, 190, 160, 165, 104, 14, 187, 9, 45, + 172, 154, 142, 132, 123, 51, 191, 213, 193, 9, 209, 54, 68, 92, 199, 15, 160, 1, 13, 237, 2, + 14, 204, 147, 10, 105, 98, 6, 19, 99, 8, 194, 118, 160, 96, 54, 176, 104, 9, 13, 155, 100, + 193, 211, 109, 115, 161, 90, 219, 69, 112, 157, 127, 29, 220, 210, 118, 187, 245, 150, 152, 23, 54, + 69, 169, 197, 224, 56, 64, 45, 228, 116, 60, 187, 68, 152, 104, 132, 246, 12, 181, 22, 236, 54, + 51, 169, 239, 227, 243, 239, 211, 185, 5, 52, 204, 12, 157, 81, 58, 188, 62, 11, 218, 12, 250, + 25, 82, 3, 107, 172, 127, 49, 246, 34, 63, 128, 70, 123, 180, 255, 227, 199, 119, 213, 232, 50, + 190, 107, 249, 247, 102, 64, 195, 119, 166, 67, 167, 55, 166, 65, 247, 55, 167, 65, 251, 94, 147, + 242, 17, 26, 205, 70, 192, 179, 237, 198, 64, 205, 62, 147, 225, 209, 119, 166, 183, 140, 248, 195, + 136, 167, 249, 1, 95, 45, 134, 33, 203, 55, 235, 2, 202, 117, 27, 118, 193, 146, 229, 91, 116, + 251, 40, 235, 59, 120, 186, 199, 4, 168, 214, 45, 143, 102, 158, 135, 163, 255, 52, 150, 68, 252, + 128, 131, 127, 28, 45, 224, 7, 148, 127, 119, 198, 59, 182, 123, 11, 194, 15, 72, 17, 126, 64, + 65, 95, 18, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 164, 8, 63, 192, 248, 2, 225, + 7, 8, 63, 32, 69, 248, 1, 82, 164, 72, 145, 34, 69, 138, 148, 184, 231, 104, 225, 7, 8, + 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 192, + 249, 22, 151, 240, 3, 184, 30, 194, 15, 224, 250, 8, 63, 128, 235, 38, 252, 0, 225, 7, 164, + 8, 63, 64, 13, 130, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 128, 253, 16, 126, 128, + 240, 3, 60, 202, 15, 184, 48, 44, 3, 92, 252, 1, 240, 22, 63, 128, 3, 240, 86, 186, 47, + 139, 50, 206, 223, 232, 84, 30, 27, 229, 82, 218, 83, 252, 128, 10, 5, 26, 33, 95, 214, 128, + 120, 178, 205, 91, 77, 137, 158, 224, 7, 112, 0, 166, 40, 18, 186, 35, 120, 92, 128, 86, 203, + 46, 173, 182, 195, 0, 120, 134, 31, 80, 199, 66, 51, 120, 0, 109, 28, 62, 174, 137, 118, 94, + 188, 227, 128, 235, 249, 1, 28, 128, 249, 118, 234, 48, 12, 2, 169, 203, 27, 198, 125, 145, 228, + 1, 126, 64, 99, 7, 73, 149, 247, 177, 130, 236, 230, 24, 3, 224, 9, 126, 192, 242, 8, 9, + 149, 213, 231, 107, 241, 121, 111, 60, 222, 230, 20, 169, 225, 5, 126, 128, 79, 21, 67, 59, 200, + 56, 77, 93, 100, 24, 254, 237, 218, 162, 14, 64, 113, 241, 3, 126, 137, 114, 214, 173, 236, 40, + 235, 5, 73, 31, 124, 150, 163, 46, 224, 82, 126, 64, 91, 199, 34, 233, 252, 193, 176, 48, 205, + 182, 22, 56, 140, 143, 167, 98, 139, 73, 99, 97, 101, 196, 65, 208, 117, 252, 0, 58, 115, 104, + 59, 11, 152, 33, 90, 192, 137, 116, 62, 84, 70, 159, 31, 144, 13, 248, 255, 190, 8, 4, 9, + 215, 242, 3, 186, 219, 241, 1, 28, 168, 195, 205, 175, 237, 199, 207, 249, 8, 143, 119, 218, 46, + 132, 220, 196, 15, 192, 138, 254, 139, 146, 164, 27, 3, 94, 20, 92, 70, 208, 52, 56, 6, 195, + 120, 3, 249, 173, 135, 50, 212, 247, 197, 231, 247, 168, 122, 64, 87, 242, 3, 176, 130, 119, 99, + 165, 95, 198, 227, 104, 30, 208, 72, 38, 127, 194, 209, 25, 143, 62, 64, 142, 198, 247, 252, 55, + 234, 197, 80, 113, 243, 3, 88, 0, 125, 25, 51, 196, 58, 162, 125, 194, 50, 121, 67, 5, 126, + 92, 33, 198, 152, 161, 74, 133, 156, 145, 240, 128, 16, 98, 103, 29, 17, 233, 30, 26, 56, 175, + 153, 113, 57, 236, 90, 126, 128, 210, 42, 46, 165, 62, 140, 129, 185, 31, 143, 175, 161, 141, 71, + 251, 154, 157, 58, 129, 142, 158, 136, 114, 230, 65, 69, 236, 224, 231, 204, 173, 248, 250, 148, 21, + 21, 134, 124, 27, 211, 134, 136, 107, 248, 1, 232, 204, 149, 232, 116, 121, 116, 166, 26, 62, 238, + 135, 199, 37, 108, 39, 34, 49, 71, 212, 65, 181, 65, 243, 108, 72, 125, 109, 54, 220, 213, 113, + 135, 179, 45, 49, 55, 110, 138, 26, 3, 26, 6, 227, 106, 180, 122, 232, 48, 177, 2, 6, 225, + 235, 219, 137, 38, 133, 207, 255, 140, 220, 50, 178, 161, 124, 230, 12, 184, 209, 191, 38, 250, 166, + 168, 163, 109, 241, 98, 230, 7, 160, 243, 231, 232, 240, 132, 124, 122, 212, 179, 248, 252, 37, 60, + 126, 197, 176, 181, 157, 76, 152, 10, 105, 29, 207, 180, 30, 13, 79, 116, 158, 163, 7, 225, 154, + 22, 91, 35, 111, 139, 171, 63, 140, 120, 129, 31, 128, 77, 158, 166, 212, 139, 25, 177, 241, 2, + 113, 9, 107, 181, 31, 187, 39, 29, 7, 199, 70, 77, 179, 225, 113, 127, 97, 23, 233, 248, 218, + 116, 104, 55, 232, 23, 200, 248, 50, 194, 15, 35, 97, 63, 141, 121, 140, 31, 128, 35, 255, 59, + 15, 127, 58, 23, 154, 141, 249, 30, 62, 31, 185, 12, 222, 250, 112, 14, 244, 124, 107, 134, 30, + 136, 199, 155, 102, 193, 179, 237, 70, 67, 141, 183, 167, 3, 190, 167, 91, 204, 63, 142, 122, 137, + 31, 64, 234, 81, 146, 208, 238, 217, 119, 8, 86, 175, 217, 1, 43, 126, 220, 14, 253, 241, 164, + 61, 217, 105, 28, 84, 235, 158, 247, 71, 90, 211, 172, 202, 142, 126, 30, 79, 22, 126, 192, 222, + 67, 71, 116, 126, 64, 185, 161, 11, 161, 82, 239, 201, 31, 96, 215, 57, 211, 209, 142, 170, 240, + 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 32, 69, 248, 1, 198, 23, 8, 63, 64, 248, 1, + 41, 194, 15, 144, 34, 69, 138, 20, 41, 82, 164, 196, 61, 71, 11, 63, 64, 248, 1, 194, 15, + 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 206, 183, 184, 132, + 31, 192, 245, 16, 126, 0, 215, 71, 248, 1, 92, 55, 225, 7, 8, 63, 32, 69, 248, 1, 106, + 16, 132, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 236, 135, 240, 3, 132, 31, 224, 69, + 126, 0, 149, 12, 95, 224, 252, 34, 10, 128, 39, 249, 1, 215, 166, 251, 178, 72, 0, 245, 0, + 30, 79, 75, 48, 0, 222, 226, 7, 112, 0, 70, 178, 100, 134, 68, 18, 255, 46, 138, 41, 209, + 75, 252, 128, 167, 66, 180, 65, 90, 96, 44, 30, 111, 79, 48, 0, 158, 225, 7, 252, 19, 109, + 171, 73, 250, 178, 11, 187, 193, 112, 60, 150, 177, 83, 134, 58, 25, 7, 188, 194, 15, 24, 96, + 163, 33, 62, 136, 246, 65, 188, 96, 21, 175, 240, 3, 42, 219, 200, 226, 84, 203, 69, 187, 33, + 142, 0, 120, 130, 31, 176, 196, 82, 16, 169, 133, 232, 5, 41, 179, 236, 68, 180, 242, 49, 6, + 192, 229, 252, 0, 45, 208, 205, 228, 104, 36, 109, 224, 62, 124, 47, 9, 44, 239, 164, 68, 171, + 69, 29, 128, 83, 222, 5, 112, 160, 187, 43, 70, 145, 180, 97, 57, 148, 177, 58, 166, 46, 224, + 82, 126, 192, 68, 75, 217, 124, 132, 4, 236, 44, 150, 62, 140, 54, 7, 159, 63, 230, 116, 16, + 116, 35, 63, 192, 103, 98, 1, 88, 183, 2, 205, 22, 170, 178, 7, 255, 103, 40, 62, 46, 109, + 155, 61, 54, 197, 165, 252, 0, 172, 240, 37, 104, 123, 45, 29, 215, 66, 52, 192, 224, 192, 2, + 196, 24, 138, 64, 144, 112, 37, 63, 160, 125, 65, 158, 112, 45, 228, 108, 7, 29, 142, 7, 65, + 83, 203, 89, 132, 93, 163, 124, 164, 165, 176, 107, 248, 1, 132, 197, 98, 60, 86, 38, 86, 62, + 143, 217, 1, 123, 204, 142, 69, 99, 5, 152, 198, 137, 125, 248, 156, 6, 198, 74, 104, 103, 58, + 186, 24, 42, 110, 126, 0, 47, 125, 207, 197, 138, 215, 64, 71, 251, 226, 227, 81, 68, 149, 136, + 2, 76, 8, 218, 5, 130, 143, 164, 33, 190, 202, 232, 14, 234, 229, 176, 171, 249, 1, 156, 60, + 157, 208, 23, 101, 209, 145, 193, 216, 50, 150, 225, 113, 131, 58, 8, 70, 146, 201, 155, 108, 33, + 254, 173, 116, 188, 27, 34, 197, 202, 15, 48, 168, 113, 120, 44, 69, 93, 132, 90, 6, 58, 190, + 27, 109, 63, 90, 208, 225, 154, 129, 174, 29, 38, 215, 236, 52, 174, 77, 201, 161, 139, 98, 219, + 18, 115, 211, 166, 40, 97, 113, 152, 42, 113, 53, 90, 87, 180, 17, 104, 203, 44, 150, 200, 86, + 129, 56, 81, 187, 253, 152, 141, 229, 63, 153, 15, 25, 3, 55, 59, 223, 20, 117, 180, 45, 94, + 76, 252, 0, 130, 168, 209, 194, 7, 173, 13, 6, 96, 37, 193, 19, 136, 23, 98, 181, 104, 74, + 231, 99, 253, 118, 99, 225, 161, 14, 107, 99, 219, 22, 87, 127, 24, 113, 35, 63, 128, 186, 9, + 58, 125, 46, 30, 159, 70, 235, 133, 54, 131, 161, 9, 135, 205, 129, 104, 232, 207, 130, 186, 205, + 243, 224, 54, 255, 10, 92, 250, 110, 119, 246, 195, 72, 216, 79, 99, 46, 230, 7, 96, 75, 184, + 68, 31, 245, 181, 64, 58, 95, 31, 204, 74, 207, 212, 41, 50, 5, 131, 102, 99, 127, 54, 52, + 237, 57, 11, 238, 126, 97, 13, 92, 213, 124, 171, 227, 159, 198, 60, 199, 15, 160, 153, 164, 242, + 171, 147, 94, 41, 255, 206, 116, 168, 215, 45, 23, 158, 109, 61, 18, 180, 182, 163, 11, 90, 67, + 215, 55, 166, 65, 199, 97, 27, 157, 253, 56, 170, 254, 60, 238, 53, 126, 192, 3, 67, 23, 194, + 168, 31, 183, 193, 178, 149, 91, 97, 248, 232, 239, 161, 239, 167, 243, 224, 217, 23, 114, 160, 113, + 179, 108, 120, 230, 197, 209, 80, 241, 253, 89, 224, 248, 214, 218, 100, 224, 7, 108, 220, 188, 7, + 214, 174, 223, 5, 111, 15, 89, 8, 13, 59, 143, 135, 234, 221, 114, 105, 223, 241, 14, 167, 31, + 156, 20, 252, 128, 205, 251, 254, 208, 249, 1, 143, 124, 50, 7, 106, 116, 25, 223, 33, 214, 47, + 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 227, 11, 132, 31, 32, 252, 128, 20, 225, 7, 72, + 145, 34, 69, 138, 20, 41, 82, 226, 158, 163, 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 231, 91, 92, 194, 15, 224, 122, 8, + 63, 128, 235, 35, 252, 0, 174, 155, 240, 3, 132, 31, 144, 34, 252, 0, 53, 8, 194, 15, 16, + 126, 128, 240, 3, 132, 31, 32, 252, 0, 246, 67, 248, 1, 194, 15, 240, 42, 63, 160, 8, 3, + 224, 61, 126, 0, 149, 12, 95, 160, 98, 186, 47, 171, 122, 134, 22, 72, 52, 0, 158, 228, 7, + 60, 200, 98, 168, 129, 120, 188, 209, 16, 81, 36, 50, 37, 122, 137, 31, 240, 55, 22, 69, 24, + 106, 176, 65, 120, 44, 145, 96, 0, 188, 193, 15, 160, 162, 159, 245, 66, 141, 80, 144, 181, 133, + 31, 199, 43, 157, 55, 198, 1, 175, 240, 3, 50, 205, 50, 24, 110, 5, 63, 162, 181, 198, 192, + 92, 17, 103, 0, 60, 193, 15, 184, 207, 82, 51, 92, 168, 13, 162, 4, 237, 245, 226, 12, 128, + 39, 248, 1, 223, 218, 74, 226, 10, 131, 64, 217, 102, 27, 198, 17, 0, 151, 243, 3, 124, 129, + 15, 45, 242, 6, 135, 235, 4, 125, 89, 71, 208, 102, 161, 213, 195, 247, 157, 118, 50, 2, 112, + 202, 187, 0, 101, 135, 14, 147, 194, 106, 81, 21, 163, 95, 160, 221, 70, 51, 70, 76, 93, 192, + 109, 252, 0, 116, 224, 46, 78, 147, 29, 180, 21, 66, 106, 182, 82, 122, 34, 203, 252, 35, 150, + 65, 208, 125, 252, 0, 45, 176, 192, 177, 74, 92, 51, 205, 14, 190, 44, 226, 137, 12, 163, 133, + 82, 81, 78, 131, 167, 146, 31, 208, 79, 117, 80, 233, 239, 78, 153, 1, 192, 9, 214, 59, 17, + 122, 203, 209, 66, 200, 45, 252, 0, 172, 240, 173, 104, 227, 34, 56, 27, 140, 32, 142, 54, 255, + 109, 11, 218, 19, 145, 150, 203, 110, 228, 7, 24, 235, 253, 70, 232, 200, 92, 166, 71, 157, 176, + 232, 231, 78, 91, 3, 189, 239, 217, 152, 47, 134, 92, 192, 15, 56, 139, 131, 209, 148, 214, 252, + 172, 5, 118, 162, 14, 55, 243, 5, 72, 54, 75, 227, 73, 35, 51, 90, 199, 51, 252, 0, 108, + 9, 183, 112, 171, 248, 8, 31, 255, 74, 146, 121, 243, 90, 192, 252, 24, 223, 23, 52, 253, 45, + 7, 131, 113, 143, 26, 4, 79, 241, 3, 140, 121, 157, 177, 90, 111, 160, 51, 83, 217, 193, 227, + 81, 208, 58, 106, 171, 24, 131, 246, 175, 184, 183, 196, 220, 178, 41, 138, 206, 158, 207, 68, 137, + 166, 120, 166, 215, 226, 243, 245, 78, 198, 2, 124, 239, 31, 140, 226, 184, 61, 174, 77, 81, 71, + 219, 226, 167, 144, 31, 128, 142, 156, 206, 0, 133, 54, 104, 131, 209, 14, 57, 8, 196, 166, 186, + 109, 70, 13, 123, 240, 211, 121, 241, 109, 139, 171, 63, 140, 184, 137, 31, 128, 142, 253, 135, 185, + 130, 163, 209, 86, 210, 181, 129, 221, 76, 209, 160, 105, 246, 159, 149, 222, 156, 14, 105, 31, 111, + 137, 253, 135, 145, 176, 159, 198, 92, 196, 15, 96, 172, 6, 29, 211, 48, 0, 175, 98, 147, 255, + 142, 199, 136, 19, 97, 123, 9, 56, 102, 60, 210, 106, 73, 216, 109, 117, 73, 195, 15, 64, 39, + 47, 199, 129, 242, 58, 60, 190, 140, 182, 20, 31, 255, 170, 14, 150, 68, 143, 120, 162, 77, 30, + 220, 153, 185, 12, 46, 245, 111, 141, 237, 199, 81, 245, 231, 113, 55, 243, 3, 168, 69, 240, 241, + 129, 218, 47, 142, 233, 95, 163, 243, 120, 189, 37, 52, 242, 23, 206, 24, 237, 250, 204, 132, 138, + 93, 215, 194, 13, 47, 108, 211, 125, 113, 244, 243, 184, 186, 140, 244, 10, 63, 224, 129, 161, 11, + 247, 61, 52, 104, 30, 188, 58, 122, 25, 116, 127, 107, 58, 180, 125, 121, 98, 193, 101, 117, 175, + 119, 103, 64, 247, 156, 109, 177, 221, 32, 193, 31, 236, 57, 126, 192, 129, 35, 71, 225, 247, 253, + 135, 97, 246, 130, 95, 224, 139, 145, 75, 117, 140, 198, 83, 45, 115, 224, 185, 30, 19, 160, 204, + 199, 115, 32, 230, 251, 137, 189, 204, 15, 216, 181, 231, 32, 172, 217, 184, 27, 218, 246, 155, 5, + 53, 59, 143, 7, 236, 34, 211, 9, 223, 21, 207, 151, 120, 158, 31, 80, 254, 221, 25, 95, 163, + 243, 247, 198, 251, 5, 194, 15, 16, 126, 64, 138, 240, 3, 212, 150, 32, 252, 0, 225, 7, 72, + 145, 34, 69, 138, 20, 41, 82, 226, 152, 163, 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, + 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 231, 91, 92, 194, 15, 224, 122, 8, + 63, 128, 235, 35, 252, 0, 174, 155, 240, 3, 132, 31, 144, 34, 252, 0, 53, 8, 194, 15, 16, + 126, 128, 240, 3, 132, 31, 32, 252, 0, 246, 67, 248, 1, 194, 15, 16, 126, 128, 71, 249, 1, + 84, 232, 238, 207, 34, 8, 128, 247, 248, 1, 236, 252, 43, 124, 223, 255, 195, 116, 203, 124, 162, + 83, 162, 103, 248, 1, 236, 124, 115, 69, 74, 75, 242, 186, 115, 18, 12, 128, 119, 248, 1, 232, + 108, 125, 211, 189, 255, 164, 0, 233, 145, 232, 56, 224, 13, 126, 128, 166, 103, 144, 14, 205, 32, + 153, 47, 161, 91, 194, 201, 21, 79, 139, 51, 0, 30, 224, 7, 104, 129, 235, 209, 193, 95, 35, + 228, 16, 205, 98, 173, 241, 105, 113, 4, 192, 221, 252, 0, 116, 234, 108, 180, 197, 97, 57, 132, + 195, 147, 46, 247, 139, 179, 5, 184, 155, 31, 128, 3, 221, 56, 203, 68, 235, 225, 70, 130, 169, + 174, 39, 51, 0, 167, 158, 31, 160, 233, 128, 148, 48, 117, 184, 165, 229, 191, 103, 30, 6, 44, + 221, 172, 16, 117, 212, 5, 92, 200, 15, 120, 217, 46, 197, 182, 174, 12, 181, 76, 167, 171, 191, + 246, 37, 218, 117, 78, 199, 3, 87, 242, 3, 240, 44, 102, 90, 157, 225, 16, 132, 134, 125, 106, + 109, 178, 247, 79, 198, 52, 120, 74, 248, 1, 172, 253, 139, 230, 96, 164, 108, 243, 20, 160, 237, + 104, 47, 25, 162, 41, 71, 11, 33, 55, 240, 3, 168, 217, 162, 61, 135, 206, 127, 198, 65, 56, + 110, 211, 215, 195, 3, 16, 30, 48, 74, 184, 92, 11, 3, 115, 134, 147, 165, 176, 107, 248, 1, + 28, 8, 74, 163, 253, 56, 218, 26, 134, 32, 24, 103, 56, 24, 73, 58, 175, 42, 198, 249, 245, + 97, 172, 51, 142, 253, 98, 200, 5, 252, 0, 106, 13, 36, 155, 239, 137, 54, 201, 82, 12, 29, + 73, 49, 174, 69, 95, 31, 120, 130, 31, 128, 142, 80, 30, 241, 82, 156, 97, 158, 224, 40, 251, + 44, 97, 9, 154, 53, 71, 128, 89, 34, 36, 174, 62, 163, 40, 54, 68, 138, 133, 31, 96, 52, + 97, 116, 178, 2, 62, 30, 162, 75, 97, 35, 140, 7, 33, 65, 201, 127, 15, 181, 160, 71, 77, + 240, 4, 111, 242, 3, 244, 100, 234, 190, 0, 193, 20, 7, 99, 159, 167, 22, 113, 212, 50, 24, + 225, 193, 25, 138, 246, 127, 9, 109, 138, 58, 218, 22, 63, 5, 252, 0, 106, 206, 228, 12, 205, + 24, 124, 118, 55, 217, 180, 6, 117, 80, 60, 140, 246, 30, 181, 166, 132, 182, 197, 213, 31, 70, + 220, 192, 15, 208, 73, 18, 26, 6, 66, 11, 76, 139, 66, 151, 161, 215, 87, 16, 117, 226, 129, + 161, 11, 23, 198, 253, 195, 72, 216, 79, 99, 46, 224, 7, 224, 96, 119, 30, 58, 70, 214, 139, + 175, 28, 247, 219, 205, 10, 117, 219, 140, 250, 185, 244, 135, 115, 160, 193, 23, 107, 195, 110, 161, + 241, 52, 63, 160, 96, 160, 244, 5, 170, 160, 117, 103, 208, 226, 113, 171, 214, 80, 167, 235, 4, + 40, 243, 202, 246, 72, 103, 223, 219, 252, 0, 116, 242, 10, 70, 106, 124, 101, 240, 135, 210, 141, + 169, 17, 91, 66, 195, 166, 35, 224, 97, 255, 60, 184, 132, 110, 220, 226, 155, 36, 200, 135, 164, + 226, 7, 24, 87, 131, 68, 145, 74, 109, 149, 147, 155, 214, 52, 116, 209, 148, 217, 121, 34, 148, + 106, 249, 61, 92, 154, 185, 89, 175, 111, 204, 55, 72, 112, 0, 60, 193, 15, 120, 248, 147, 185, + 199, 202, 189, 63, 19, 254, 215, 101, 60, 104, 237, 70, 23, 4, 161, 251, 219, 179, 160, 222, 235, + 191, 194, 127, 95, 218, 17, 223, 45, 50, 252, 5, 222, 224, 7, 124, 177, 8, 166, 172, 217, 14, + 89, 99, 151, 195, 107, 253, 102, 23, 4, 225, 157, 79, 230, 193, 139, 227, 183, 196, 119, 147, 148, + 186, 56, 242, 18, 63, 96, 213, 154, 29, 144, 59, 117, 53, 60, 223, 118, 20, 60, 247, 226, 24, + 168, 254, 234, 36, 40, 245, 249, 130, 49, 137, 124, 129, 231, 248, 1, 123, 15, 28, 134, 113, 75, + 54, 65, 106, 207, 9, 80, 173, 107, 222, 78, 28, 39, 42, 37, 250, 5, 158, 228, 7, 224, 153, + 223, 80, 179, 227, 184, 199, 139, 234, 11, 132, 31, 32, 252, 128, 20, 225, 7, 72, 145, 34, 69, + 138, 20, 41, 82, 226, 158, 163, 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, + 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 231, 91, 92, 194, 15, 224, 122, 8, 63, 128, 235, + 35, 252, 0, 174, 155, 240, 3, 132, 31, 144, 34, 252, 0, 53, 8, 194, 15, 16, 126, 128, 240, + 3, 132, 31, 32, 252, 0, 246, 67, 248, 1, 194, 15, 16, 126, 128, 183, 249, 1, 37, 226, 213, + 11, 155, 46, 165, 189, 199, 15, 72, 111, 50, 156, 110, 151, 127, 141, 242, 141, 219, 233, 129, 98, + 153, 18, 189, 198, 15, 248, 7, 154, 113, 107, 252, 0, 180, 75, 19, 12, 128, 167, 248, 1, 36, + 146, 152, 105, 82, 143, 250, 18, 29, 7, 188, 193, 15, 240, 5, 254, 133, 54, 199, 36, 164, 166, + 212, 220, 171, 208, 42, 36, 116, 145, 228, 122, 126, 128, 47, 112, 33, 218, 124, 27, 41, 204, 30, + 28, 19, 222, 198, 227, 245, 113, 6, 192, 245, 252, 128, 75, 204, 252, 0, 155, 52, 219, 21, 156, + 102, 156, 55, 5, 192, 189, 252, 0, 22, 62, 124, 199, 103, 60, 24, 69, 70, 191, 136, 100, 51, + 39, 51, 0, 167, 180, 11, 112, 54, 217, 239, 35, 102, 157, 15, 181, 189, 104, 239, 160, 157, 25, + 87, 23, 112, 19, 63, 128, 250, 51, 43, 188, 192, 1, 52, 65, 53, 202, 85, 94, 55, 150, 5, + 146, 235, 248, 1, 88, 249, 155, 88, 240, 20, 116, 226, 180, 105, 92, 160, 255, 249, 8, 237, 34, + 167, 11, 36, 87, 241, 3, 168, 15, 227, 136, 62, 36, 234, 153, 142, 44, 153, 165, 196, 170, 205, + 98, 104, 1, 174, 226, 7, 144, 213, 64, 235, 143, 103, 54, 200, 178, 183, 160, 19, 199, 21, 126, + 0, 253, 207, 247, 248, 255, 149, 28, 6, 192, 93, 252, 0, 190, 200, 57, 31, 29, 104, 129, 199, + 229, 232, 244, 1, 7, 78, 135, 50, 70, 154, 12, 39, 133, 249, 155, 209, 214, 6, 174, 229, 7, + 40, 34, 200, 26, 24, 136, 247, 241, 184, 43, 14, 156, 198, 70, 94, 27, 148, 112, 114, 57, 236, + 94, 126, 64, 62, 68, 161, 1, 58, 181, 13, 143, 191, 71, 232, 14, 86, 131, 38, 201, 236, 47, + 46, 202, 13, 145, 226, 226, 7, 80, 183, 184, 3, 237, 11, 108, 222, 43, 108, 17, 26, 161, 152, + 141, 32, 190, 119, 151, 190, 54, 208, 194, 83, 233, 122, 146, 31, 192, 139, 163, 39, 209, 166, 24, + 186, 96, 179, 211, 5, 143, 181, 144, 181, 65, 109, 243, 50, 217, 179, 252, 0, 190, 36, 190, 4, + 29, 255, 164, 64, 23, 92, 56, 24, 6, 205, 72, 29, 158, 77, 232, 189, 23, 24, 99, 139, 231, + 249, 1, 202, 32, 217, 20, 109, 132, 10, 84, 209, 247, 9, 194, 145, 58, 199, 208, 90, 21, 201, + 15, 35, 97, 63, 141, 21, 51, 63, 0, 29, 43, 201, 128, 149, 99, 76, 137, 176, 154, 25, 142, + 243, 69, 85, 21, 245, 167, 177, 164, 225, 7, 16, 87, 20, 251, 124, 35, 60, 78, 142, 48, 101, + 238, 175, 211, 110, 244, 184, 135, 6, 206, 11, 249, 113, 52, 41, 248, 1, 74, 151, 120, 8, 173, + 61, 118, 129, 77, 10, 133, 170, 96, 150, 168, 223, 98, 4, 148, 237, 63, 11, 234, 246, 219, 94, + 248, 243, 120, 178, 241, 3, 104, 147, 148, 105, 179, 139, 177, 85, 108, 9, 107, 9, 173, 199, 195, + 141, 45, 55, 132, 221, 189, 150, 116, 252, 0, 116, 254, 90, 108, 5, 125, 26, 52, 203, 94, 173, + 6, 224, 233, 86, 163, 160, 98, 139, 121, 112, 153, 127, 75, 200, 13, 148, 73, 201, 15, 160, 189, + 196, 234, 221, 114, 155, 213, 232, 60, 62, 152, 230, 47, 188, 108, 110, 247, 202, 20, 168, 210, 121, + 149, 142, 208, 184, 190, 245, 246, 248, 111, 146, 82, 23, 71, 110, 229, 7, 16, 115, 168, 212, 144, + 5, 143, 87, 126, 117, 18, 212, 107, 91, 72, 143, 120, 111, 224, 2, 200, 120, 111, 135, 94, 231, + 132, 110, 147, 43, 88, 34, 187, 152, 31, 192, 240, 164, 23, 203, 191, 55, 3, 106, 247, 154, 168, + 7, 224, 137, 102, 217, 208, 47, 123, 9, 84, 206, 90, 150, 248, 141, 146, 28, 4, 79, 240, 3, + 104, 26, 172, 137, 129, 120, 166, 237, 40, 104, 208, 105, 28, 148, 235, 59, 115, 123, 145, 221, 52, + 237, 37, 126, 64, 57, 156, 14, 171, 117, 203, 219, 95, 187, 253, 152, 118, 69, 253, 5, 158, 225, + 7, 224, 184, 240, 232, 201, 250, 18, 225, 7, 72, 145, 34, 69, 138, 20, 41, 82, 226, 153, 163, + 133, 31, 32, 252, 0, 225, 7, 8, 63, 64, 248, 1, 194, 15, 16, 126, 128, 240, 3, 132, 31, + 32, 252, 0, 231, 91, 92, 194, 15, 224, 122, 8, 63, 128, 235, 35, 252, 0, 174, 155, 240, 3, + 132, 31, 144, 34, 252, 0, 53, 8, 194, 15, 16, 126, 128, 240, 3, 132, 31, 32, 252, 0, 246, + 67, 248, 1, 194, 15, 16, 126, 128, 135, 249, 1, 84, 138, 64, 58, 239, 77, 126, 0, 59, 127, + 3, 41, 73, 138, 98, 74, 244, 20, 63, 128, 157, 175, 69, 242, 153, 116, 95, 214, 48, 60, 222, + 150, 96, 0, 188, 195, 15, 96, 231, 155, 155, 84, 99, 25, 241, 136, 166, 213, 113, 192, 19, 252, + 0, 221, 121, 45, 240, 150, 73, 43, 116, 2, 91, 193, 15, 148, 134, 51, 161, 139, 36, 15, 240, + 3, 206, 100, 101, 72, 126, 10, 110, 69, 29, 130, 199, 205, 44, 152, 56, 45, 206, 0, 184, 158, + 31, 112, 51, 58, 189, 56, 138, 60, 110, 52, 41, 71, 226, 12, 128, 171, 249, 1, 149, 209, 118, + 154, 84, 97, 230, 64, 24, 92, 129, 204, 120, 166, 69, 55, 243, 3, 52, 91, 37, 152, 146, 133, + 94, 49, 146, 202, 220, 18, 119, 23, 112, 25, 63, 224, 85, 199, 204, 128, 194, 231, 212, 82, 104, + 144, 60, 43, 158, 65, 208, 21, 252, 0, 106, 194, 120, 118, 191, 136, 146, 62, 55, 146, 136, 154, + 4, 149, 229, 78, 214, 52, 120, 42, 248, 1, 143, 161, 101, 57, 164, 69, 216, 89, 7, 180, 115, + 98, 94, 8, 185, 129, 31, 192, 65, 40, 137, 103, 148, 248, 1, 7, 45, 232, 16, 209, 178, 75, + 7, 89, 96, 125, 111, 172, 75, 97, 215, 240, 3, 208, 233, 211, 25, 151, 213, 167, 32, 201, 186, + 189, 133, 234, 132, 243, 143, 196, 14, 24, 136, 246, 207, 132, 46, 134, 138, 147, 31, 160, 206, 4, + 24, 144, 220, 40, 1, 176, 34, 76, 204, 35, 246, 64, 44, 151, 195, 174, 228, 7, 160, 19, 103, + 163, 221, 133, 150, 131, 118, 64, 119, 80, 179, 108, 254, 86, 221, 163, 183, 154, 109, 190, 168, 54, + 68, 78, 57, 63, 128, 150, 184, 108, 31, 224, 216, 240, 125, 132, 5, 145, 121, 208, 12, 70, 154, + 17, 60, 199, 15, 96, 158, 88, 19, 5, 168, 6, 81, 200, 82, 135, 208, 70, 18, 82, 167, 200, + 54, 69, 29, 109, 139, 159, 68, 126, 0, 193, 85, 104, 181, 135, 227, 194, 44, 130, 168, 69, 224, + 9, 25, 182, 132, 47, 151, 75, 20, 217, 182, 184, 250, 195, 72, 113, 240, 3, 116, 165, 120, 62, + 73, 226, 51, 180, 159, 108, 0, 42, 170, 245, 55, 115, 68, 146, 130, 31, 64, 91, 98, 232, 44, + 205, 18, 63, 90, 45, 152, 244, 64, 228, 143, 3, 196, 28, 172, 101, 245, 211, 152, 231, 249, 1, + 60, 46, 60, 128, 70, 2, 233, 221, 54, 3, 227, 81, 166, 80, 94, 109, 245, 227, 168, 231, 249, + 1, 76, 152, 121, 0, 207, 56, 33, 184, 182, 216, 12, 136, 171, 107, 117, 24, 251, 86, 201, 97, + 139, 142, 133, 253, 60, 158, 44, 252, 0, 116, 242, 191, 196, 16, 195, 169, 242, 119, 110, 246, 33, + 65, 168, 222, 37, 247, 216, 67, 131, 230, 235, 117, 162, 155, 55, 146, 146, 31, 160, 207, 18, 90, + 160, 14, 211, 101, 246, 135, 181, 132, 215, 151, 194, 53, 173, 172, 111, 160, 76, 42, 126, 0, 58, + 91, 149, 103, 137, 195, 133, 179, 67, 0, 26, 180, 24, 15, 215, 100, 174, 11, 57, 243, 73, 203, + 15, 64, 167, 239, 227, 75, 99, 114, 94, 31, 20, 235, 183, 204, 129, 234, 47, 125, 11, 151, 250, + 183, 22, 204, 249, 73, 205, 15, 160, 125, 1, 94, 61, 46, 79, 215, 2, 39, 104, 138, 172, 211, + 41, 23, 30, 235, 177, 190, 104, 111, 148, 228, 32, 184, 142, 31, 160, 208, 101, 210, 113, 112, 28, + 86, 48, 32, 190, 57, 83, 175, 99, 145, 221, 42, 171, 4, 193, 181, 252, 0, 116, 252, 126, 218, + 99, 72, 203, 204, 58, 158, 138, 93, 225, 145, 143, 231, 20, 237, 205, 210, 166, 150, 224, 58, 126, + 0, 175, 23, 206, 108, 208, 52, 187, 87, 237, 246, 99, 14, 84, 237, 57, 97, 91, 153, 15, 102, + 223, 121, 82, 190, 204, 237, 252, 128, 122, 173, 71, 182, 171, 223, 98, 68, 157, 148, 191, 106, 73, + 52, 47, 129, 20, 41, 82, 164, 72, 249, 171, 149, 255, 7, 209, 226, 81, 116, 250, 197, 13, 118, + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,}; +const char* Artwork::knobData = (const char*)temp_knob_3; + diff --git a/examples/CairoUI/Artwork.hpp b/examples/CairoUI/Artwork.hpp new file mode 100644 index 00000000..95ab9d59 --- /dev/null +++ b/examples/CairoUI/Artwork.hpp @@ -0,0 +1,19 @@ +/* (Auto-generated binary data file). */ + +#ifndef BINARY_ARTWORK_HPP +#define BINARY_ARTWORK_HPP + +namespace Artwork +{ + extern const char* buttonOffData; + const unsigned int buttonOffDataSize = 970; + + extern const char* buttonOnData; + const unsigned int buttonOnDataSize = 983; + + extern const char* knobData; + const unsigned int knobDataSize = 52344; +} + +#endif // BINARY_ARTWORK_HPP + diff --git a/examples/CairoUI/CairoExamplePlugin.cpp b/examples/CairoUI/CairoExamplePlugin.cpp index 161399e5..b43065aa 100644 --- a/examples/CairoUI/CairoExamplePlugin.cpp +++ b/examples/CairoUI/CairoExamplePlugin.cpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 diff --git a/examples/CairoUI/CairoExampleUI.cpp b/examples/CairoUI/CairoExampleUI.cpp index 9cf206f1..f02810ab 100644 --- a/examples/CairoUI/CairoExampleUI.cpp +++ b/examples/CairoUI/CairoExampleUI.cpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 @@ -15,6 +16,8 @@ */ #include "DistrhoUI.hpp" + +#include "Artwork.hpp" #include "DemoWidgetBanner.hpp" #include "DemoWidgetClickable.hpp" @@ -35,17 +38,29 @@ public: fWidgetBanner = widgetBanner; widgetBanner->setSize(180, 80); widgetBanner->setAbsolutePos(10, 10); - } - ~CairoExampleUI() - { + CairoImage knobSkin; + knobSkin.loadFromPNG(Artwork::knobData, Artwork::knobDataSize); + + CairoImageKnob* knob = new CairoImageKnob(this, knobSkin); + fKnob = knob; + knob->setSize(80, 80); + knob->setAbsolutePos(10, 100); + + CairoImage buttonOn, buttonOff; + buttonOn.loadFromPNG(Artwork::buttonOnData, Artwork::buttonOnDataSize); + buttonOff.loadFromPNG(Artwork::buttonOffData, Artwork::buttonOffDataSize); + + CairoImageButton* button = new CairoImageButton(this, buttonOff, buttonOn); + fButton = button; + button->setSize(60, 35); + button->setAbsolutePos(100, 160); } protected: void onCairoDisplay(const CairoGraphicsContext& context) { - cairo_t* cr = context.handle; - + cairo_t* const cr = context.handle; cairo_set_source_rgb(cr, 1.0, 0.8, 0.5); cairo_paint(cr); } @@ -60,6 +75,8 @@ protected: private: ScopedPointer fWidgetClickable; ScopedPointer fWidgetBanner; + ScopedPointer fKnob; + ScopedPointer fButton; }; UI* createUI() diff --git a/examples/CairoUI/DemoWidgetBanner.cpp b/examples/CairoUI/DemoWidgetBanner.cpp index f1c7a516..422378c8 100644 --- a/examples/CairoUI/DemoWidgetBanner.cpp +++ b/examples/CairoUI/DemoWidgetBanner.cpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 diff --git a/examples/CairoUI/DemoWidgetBanner.hpp b/examples/CairoUI/DemoWidgetBanner.hpp index 029092c8..c05df37b 100644 --- a/examples/CairoUI/DemoWidgetBanner.hpp +++ b/examples/CairoUI/DemoWidgetBanner.hpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 diff --git a/examples/CairoUI/DemoWidgetClickable.cpp b/examples/CairoUI/DemoWidgetClickable.cpp index c2bfc5d4..6720c7ed 100644 --- a/examples/CairoUI/DemoWidgetClickable.cpp +++ b/examples/CairoUI/DemoWidgetClickable.cpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 diff --git a/examples/CairoUI/DemoWidgetClickable.hpp b/examples/CairoUI/DemoWidgetClickable.hpp index 91d7b6c5..19454724 100644 --- a/examples/CairoUI/DemoWidgetClickable.hpp +++ b/examples/CairoUI/DemoWidgetClickable.hpp @@ -1,6 +1,7 @@ /* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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 diff --git a/examples/CairoUI/Makefile b/examples/CairoUI/Makefile index 13c684b6..7ebf2994 100644 --- a/examples/CairoUI/Makefile +++ b/examples/CairoUI/Makefile @@ -16,6 +16,7 @@ FILES_DSP = \ CairoExamplePlugin.cpp FILES_UI = \ + Artwork.cpp \ DemoWidgetBanner.cpp \ DemoWidgetClickable.cpp \ CairoExampleUI.cpp diff --git a/examples/CairoUI/artwork/buttonOff.png b/examples/CairoUI/artwork/buttonOff.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d6efdcd9507822a6135d2bac90371adef132d4 GIT binary patch literal 970 zcmV;*12z1KP)C78x8F93UGXBOD+kBq=T?D={W4GAKb=Dlj%GFgGhPI4m+bEHgPSG&?ReJTf^z zGD>4KJVZ4-L^nT2IYCM}LP|VEPCZ6XKS)tPNmD^eR6$EsLrqslQCmn-T}e}3OJsaa zSY%FSeo$LyQCw(IU1?NcY*b-wT4!`zYIj``6JBe1U~PP0Zhc~Kerb4!YI%ule2i>` zoo|1SZ-<|7fRJ#ApmBnea$9F|gOqZLqjQ9nbcL36hM0DTnRkhrc#)}ji=BFmo_ma+ ze2$=fkD-2$qk)sAhnug6o3M$Tv5KCui=MNLptX&asE(qykcpa*q`Hu$yOO88lBd0t zslSw}z?F%bmMSfmTW6Q8!dO=q%O4-h-09BS>d@Wl(dz!ye~ZPkxMIy~+E{_}x@`2PO={rzu~tYQEF01R|ePE!B{1qB6GR#sM%lapmY zbawy%0eeYAK~y-)V_;z6Sh{oxkzmmjc4h_!7Vf302^mR3HF)?r~ozz4EC<0B@i1ZHW9a;$Vvt`H3cf$mjwWE1B#Z`IkO;OdLu-Ow}e#%kcv~&N^SDg z^af*8hb^831GQj&jfG+~nBgSj=`6y-D=fsrYg~Y=rF|9xRD%Sv^mH=7S_0%9n%p(K z!5GDs9waTW5qkPr)$vMdEs3F^9;IJ7j+fa;h~0*y&qQJt_@R|UDqCLCItTKd5{s*+%_ zUlXb>$S-c5+=Nq0Q&VnzZ;sC}N&G}>S=2|G7B^{zDP5##F_C6qZz>|qU91euTp=EQ sUPJ;n6KPIH1_mZJGZkr~0V^W|06E32jevpA4FCWD07*qoM6N<$f)UKC7ytkO literal 0 HcmV?d00001 diff --git a/examples/CairoUI/artwork/buttonOn.png b/examples/CairoUI/artwork/buttonOn.png new file mode 100644 index 0000000000000000000000000000000000000000..b51acfb1505dbe8996430f9e4ad0c8a1992a8cad GIT binary patch literal 983 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 Date: Sat, 22 May 2021 03:00:31 +0100 Subject: [PATCH 127/159] Implement CairoImageKnob rotation --- dgl/src/Cairo.cpp | 48 +++++++++++++++++++++++++----------- dgl/src/Common.hpp | 2 +- dgl/src/ImageBaseWidgets.cpp | 11 ++++++--- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 64021fb9..a298335f 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -413,8 +413,26 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co } break; case kImageFormatBGRA: - // RGB8 to CAIRO_FORMAT_ARGB32 - // TODO + // BGRA8 to CAIRO_FORMAT_ARGB32 + // FIXME something is wrong here... + for (uint h = 0, t; h < height; ++h) + { + for (uint w = 0; w < width; ++w) + { + if ((t = rdata[h*width*4+w*4+3]) != 0) + { + newdata[h*width*4+w*4+0] = rdata[h*width*4+w*4+0]; + newdata[h*width*4+w*4+1] = rdata[h*width*4+w*4+1]; + newdata[h*width*4+w*4+2] = rdata[h*width*4+w*4+2]; + newdata[h*width*4+w*4+3] = t; + } + else + { + // make all pixels zero, cairo does not render full transparency otherwise + memset(&newdata[h*width*4+w*4], 0, 4); + } + } + } break; case kImageFormatRGB: // RGB8 to CAIRO_FORMAT_RGB24 @@ -564,14 +582,13 @@ template class ImageBaseButton; template <> void ImageBaseKnob::PrivateData::init() { - // new (&cairoDisplayImage) CairoImage(); + alwaysRepaint = true; cairoSurface = nullptr; } template <> void ImageBaseKnob::PrivateData::cleanup() { - // cairoDisplayImage.~CairoImage(); cairo_surface_destroy((cairo_surface_t*)cairoSurface); cairoSurface = nullptr; } @@ -644,24 +661,25 @@ void ImageBaseKnob::onDisplay() const uint layerX = pData->isImgVertical ? 0 : layerNum * layerW; const uint layerY = !pData->isImgVertical ? 0 : layerNum * layerH; - cairo_surface_t* const newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); - DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + cairo_surface_t* newsurface; - if (pData->rotationAngle != 0) + if (pData->rotationAngle == 0) + { + newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); + } + else { - // TODO - /* - CairoImage rotated(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH), false); - cairo_t* cr = cairo_create(rotated.getSurface()); + newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH); + cairo_t* const cr = cairo_create(newsurface); cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); - cairo_rotate(cr, normValue * angle * (float)(M_PI / 180)); - cairo_set_source_surface(cr, displayImage.getSurface(), -0.5f * layerW, -0.5f * layerH); + cairo_rotate(cr, normValue * pData->rotationAngle * (float)(M_PI / 180)); + cairo_set_source_surface(cr, pData->image.getSurface(), -0.5f * layerW, -0.5f * layerH); cairo_paint(cr); cairo_destroy(cr); - pData->cairoDisplayImage = rotated; - */ } + DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + cairo_surface_destroy(surface); pData->cairoSurface = surface = newsurface; pData->isReady = true; diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index 87934a21..ebb599a7 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -140,6 +140,7 @@ struct ImageBaseKnob::PrivateData { Callback* callback; + bool alwaysRepaint; bool isImgVertical; uint imgLayerWidth; uint imgLayerHeight; @@ -148,7 +149,6 @@ struct ImageBaseKnob::PrivateData { union { uint glTextureId; - // ImageType cairoDisplayImage; void* cairoSurface; }; diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 78bbffc3..236c0ab5 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -204,6 +204,7 @@ ImageBaseKnob::PrivateData::PrivateData(const ImageType& img, const O lastX(0), lastY(0), callback(nullptr), + alwaysRepaint(false), isImgVertical(img.getHeight() > img.getWidth()), imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), imgLayerHeight(imgLayerWidth), @@ -230,6 +231,7 @@ ImageBaseKnob::PrivateData::PrivateData(PrivateData* const other) lastX(0), lastY(0), callback(other->callback), + alwaysRepaint(other->alwaysRepaint), isImgVertical(other->isImgVertical), imgLayerWidth(other->imgLayerWidth), imgLayerHeight(other->imgLayerHeight), @@ -258,11 +260,12 @@ void ImageBaseKnob::PrivateData::assignFrom(PrivateData* const other) lastX = 0; lastY = 0; callback = other->callback; + alwaysRepaint = other->alwaysRepaint; isImgVertical = other->isImgVertical; imgLayerWidth = other->imgLayerWidth; imgLayerHeight = other->imgLayerHeight; imgLayerCount = other->imgLayerCount; - isReady = false; + isReady = false; init(); } @@ -364,7 +367,7 @@ void ImageBaseKnob::setValue(float value, bool sendCallback) noexcept if (d_isZero(pData->step)) pData->valueTmp = value; - if (pData->rotationAngle == 0) + if (pData->rotationAngle == 0 || pData->alwaysRepaint) pData->isReady = false; repaint(); @@ -755,8 +758,8 @@ void ImageBaseSlider::onDisplay() { const GraphicsContext& context(getGraphicsContext()); -#if 1 // DEBUG, paints slider area - Color(0.4f, 0.5f, 0.1f).setFor(context); +#if 0 // DEBUG, paints slider area + Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true); Rectangle(pData->sliderArea.getX(), pData->sliderArea.getY(), pData->sliderArea.getX()+pData->sliderArea.getWidth(), From 5a6335c21dbb338e320a1793d78d13b28637e1ab Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 11:05:51 +0100 Subject: [PATCH 128/159] Ignore TopLevelWidget events when invisible Signed-off-by: falkTX --- dgl/src/Cairo.cpp | 3 +++ dgl/src/OpenGL.cpp | 3 +++ dgl/src/TopLevelWidgetPrivateData.cpp | 24 ++++++++++++++++++++++++ dgl/src/Vulkan.cpp | 12 ++++-------- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index a298335f..f92e3bd2 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -757,6 +757,9 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const void TopLevelWidget::PrivateData::display() { + if (! selfw->pData->visible) + return; + const Size size(window.getSize()); const uint width = size.getWidth(); const uint height = size.getHeight(); diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 925b5abc..ad179532 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -611,6 +611,9 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const void TopLevelWidget::PrivateData::display() { + if (! selfw->pData->visible) + return; + const Size size(window.getSize()); const uint width = size.getWidth(); const uint height = size.getHeight(); diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 540d0149..5adbca95 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -39,6 +39,10 @@ TopLevelWidget::PrivateData::~PrivateData() bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + // give top-level widget chance to catch this event first if (self->onKeyboard(ev)) return true; @@ -49,6 +53,10 @@ bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + // give top-level widget chance to catch this event first if (self->onSpecial(ev)) return true; @@ -59,6 +67,10 @@ bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev) bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + // give top-level widget chance to catch this event first if (self->onCharacterInput(ev)) return true; @@ -69,6 +81,10 @@ bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + MouseEvent rev = ev; if (window.pData->autoScaling) @@ -89,6 +105,10 @@ bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev) bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + MotionEvent rev = ev; if (window.pData->autoScaling) @@ -109,6 +129,10 @@ bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev) bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev) { + // ignore event if we are not visible + if (! selfw->pData->visible) + return false; + ScrollEvent rev = ev; if (window.pData->autoScaling) diff --git a/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp index 7ec5db67..5f15d4b2 100644 --- a/dgl/src/Vulkan.cpp +++ b/dgl/src/Vulkan.cpp @@ -211,20 +211,16 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const void TopLevelWidget::PrivateData::display() { + if (! selfw->pData->visible) + return; + const Size size(window.getSize()); const uint width = size.getWidth(); const uint height = size.getHeight(); const double autoScaleFactor = window.pData->autoScaleFactor; - // FIXME anything needed here? -#if 0 - // full viewport size - if (window.pData->autoScaling) - glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); - else - glViewport(0, 0, width, height); -#endif + // TODO // main widget drawing self->onDisplay(); From 990dd7d9ebc457978cfdf8ee8b252d958a473bd5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 11:48:23 +0100 Subject: [PATCH 129/159] Update base macros, add d_custom_safe_assert Signed-off-by: falkTX --- distrho/DistrhoUtils.hpp | 12 +++++- distrho/src/DistrhoDefines.h | 77 ++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp index 9a2e0248..7f65f523 100644 --- a/distrho/DistrhoUtils.hpp +++ b/distrho/DistrhoUtils.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2018 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -198,6 +198,16 @@ void d_safe_assert_uint2(const char* const assertion, const char* const file, d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %u, v2 %u", assertion, file, line, v1, v2); } +/* + * Print a safe assertion error message, with a custom error message. + */ +static inline +void d_custom_safe_assert(const char* const message, const char* const assertion, const char* const file, + const int line) noexcept +{ + d_stderr2("assertion failure: %s, condition \"%s\" in file %s, line %i", message, assertion, file, line); +} + /* * Print a safe exception error message. */ diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index a30bc296..583c4468 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -39,6 +39,10 @@ # define DISTRHO_OS_HAIKU 1 # elif defined(__linux__) || defined(__linux) # define DISTRHO_OS_LINUX 1 +# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# define DISTRHO_OS_BSD 1 +# elif defined(__GNU__) +# define DISTRHO_OS_GNU_HURD 1 # endif #endif @@ -85,11 +89,25 @@ #endif /* Define DISTRHO_SAFE_ASSERT* */ -#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__); +#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert (#cond, __FILE__, __LINE__); +#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (! (cond)) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast(value)); +#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (! (cond)) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); +#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (! (cond)) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast(value)); +#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (! (cond)) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); + #define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; } #define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } #define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } +#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (! (cond)) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); +#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; } + +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } break; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; } + #define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value); break; } #define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); continue; } #define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } @@ -112,39 +130,23 @@ #define DISTRHO_SAFE_EXCEPTION_CONTINUE(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); continue; } #define DISTRHO_SAFE_EXCEPTION_RETURN(msg, ret) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); return ret; } -/* Define DISTRHO_DECLARE_NON_COPY_CLASS */ +/* Define DISTRHO_DECLARE_NON_COPYABLE */ #ifdef DISTRHO_PROPER_CPP11_SUPPORT -# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ -private: \ - ClassName(ClassName&) = delete; \ - ClassName(const ClassName&) = delete; \ - ClassName& operator=(ClassName&) = delete ; \ +# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \ +private: \ + ClassName(ClassName&) = delete; \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(ClassName&) = delete; \ ClassName& operator=(const ClassName&) = delete; #else -# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ -private: \ - ClassName(ClassName&); \ - ClassName(const ClassName&); \ - ClassName& operator=(ClassName&); \ +# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \ +private: \ + ClassName(ClassName&); \ + ClassName(const ClassName&); \ + ClassName& operator=(ClassName&); \ ClassName& operator=(const ClassName&); #endif -/* Define DISTRHO_DECLARE_NON_COPY_STRUCT */ -#ifdef DISTRHO_PROPER_CPP11_SUPPORT -# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \ - StructName(StructName&) = delete; \ - StructName(const StructName&) = delete; \ - StructName& operator=(StructName&) = delete; \ - StructName& operator=(const StructName&) = delete; -#else -# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \ -private: \ - StructName(StructName&); \ - StructName(const StructName&); \ - StructName& operator=(StructName&); \ - StructName& operator=(const StructName&); -#endif - /* Define DISTRHO_PREVENT_HEAP_ALLOCATION */ #ifdef DISTRHO_PROPER_CPP11_SUPPORT # define DISTRHO_PREVENT_HEAP_ALLOCATION \ @@ -166,10 +168,27 @@ private: \ #define END_NAMESPACE_DISTRHO } #define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE; +/* Define DISTRHO_OS_SEP and DISTRHO_OS_SPLIT */ +#ifdef DISTRHO_OS_WINDOWS +# define DISTRHO_OS_SEP '\\' +# define DISTRHO_OS_SEP_STR "\\" +# define DISTRHO_OS_SPLIT ';' +# define DISTRHO_OS_SPLIT_STR ";" +#else +# define DISTRHO_OS_SEP '/' +# define DISTRHO_OS_SEP_STR "/" +# define DISTRHO_OS_SPLIT ':' +# define DISTRHO_OS_SPLIT_STR ":" +#endif + /* Useful typedefs */ typedef unsigned char uchar; typedef unsigned short int ushort; typedef unsigned int uint; typedef unsigned long int ulong; +/* Deprecated macros */ +#define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName) +#define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) DISTRHO_DECLARE_NON_COPYABLE(StructName) + #endif // DISTRHO_DEFINES_H_INCLUDED From e8ef196b3c740b81f4e93c9e569ce1c5eb296875 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 11:52:41 +0100 Subject: [PATCH 130/159] Allow Thread to start with RT prio, cleanup Signed-off-by: falkTX --- distrho/extra/Thread.hpp | 83 ++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/distrho/extra/Thread.hpp b/distrho/extra/Thread.hpp index b3d92c98..c888cf8b 100644 --- a/distrho/extra/Thread.hpp +++ b/distrho/extra/Thread.hpp @@ -88,33 +88,70 @@ public: /* * Start the thread. */ - bool startThread() noexcept + bool startThread(const bool withRealtimePriority = false) noexcept { // check if already running DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); + pthread_t handle; + + pthread_attr_t attr; + pthread_attr_init(&attr); + + struct sched_param sched_param; + std::memset(&sched_param, 0, sizeof(sched_param)); + + if (withRealtimePriority) + { + sched_param.sched_priority = 80; + +#ifndef DISTRHO_OS_HAIKU + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 && + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 && +# ifndef DISTRHO_OS_WINDOWS + (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 || + pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) && +# endif + pthread_attr_setschedparam(&attr, &sched_param) == 0) + { + d_stdout("Thread setup with realtime priority successful"); + } + else +#endif + { + d_stdout("Thread setup with realtime priority failed, going with normal priority instead"); + pthread_attr_destroy(&attr); + pthread_attr_init(&attr); + } + } + const MutexLocker ml(fLock); fShouldExit = false; - pthread_t handle; + bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; + pthread_attr_destroy(&attr); - if (pthread_create(&handle, nullptr, _entryPoint, this) == 0) - { + if (withRealtimePriority && !ok) + { + d_stdout("Thread with realtime priority failed on creation, going with normal priority instead"); + pthread_attr_init(&attr); + ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; + pthread_attr_destroy(&attr); + } + + DISTRHO_SAFE_ASSERT_RETURN(ok, false); #ifdef PTW32_DLLPORT - DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); #else - DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); + DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); #endif - pthread_detach(handle); - _copyFrom(handle); + pthread_detach(handle); + _copyFrom(handle); - // wait for thread to start - fSignal.wait(); - return true; - } - - return false; + // wait for thread to start + fSignal.wait(); + return true; } /* @@ -161,10 +198,7 @@ public: _copyTo(threadId); _init(); - try { - pthread_cancel(threadId); - } DISTRHO_SAFE_EXCEPTION("pthread_cancel"); - + pthread_detach(threadId); return false; } } @@ -191,6 +225,14 @@ public: return fName; } + /* + * Returns the Id/handle of the thread. + */ + pthread_t getThreadId() const noexcept + { + return fHandle; + } + /* * Changes the name of the caller thread. */ @@ -201,7 +243,7 @@ public: #ifdef DISTRHO_OS_LINUX prctl(PR_SET_NAME, name, 0, 0, 0); #endif -#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 +#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD) pthread_setname_np(pthread_self(), name); #endif } @@ -259,7 +301,8 @@ private: */ void _runEntryPoint() noexcept { - setCurrentThreadName(fName); + if (fName.isNotEmpty()) + setCurrentThreadName(fName); // report ready fSignal.signal(); From 9f74634dde2625a6d0b8f9530bd4f684daca9f33 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 11:59:20 +0100 Subject: [PATCH 131/159] ScopeTryLocker: allow to force lock Signed-off-by: falkTX --- distrho/extra/Mutex.hpp | 19 +++++++++++-------- distrho/extra/Thread.hpp | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/distrho/extra/Mutex.hpp b/distrho/extra/Mutex.hpp index 2f80515a..90332c30 100644 --- a/distrho/extra/Mutex.hpp +++ b/distrho/extra/Mutex.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -39,7 +39,7 @@ public: /* * Constructor. */ - Mutex(bool inheritPriority = true) noexcept + Mutex(const bool inheritPriority = true) noexcept : fMutex() { pthread_mutexattr_t attr; @@ -61,9 +61,9 @@ public: /* * Lock the mutex. */ - void lock() const noexcept + bool lock() const noexcept { - pthread_mutex_lock(&fMutex); + return (pthread_mutex_lock(&fMutex) == 0); } /* @@ -86,7 +86,6 @@ public: private: mutable pthread_mutex_t fMutex; - DISTRHO_PREVENT_HEAP_ALLOCATION DISTRHO_DECLARE_NON_COPY_CLASS(Mutex) }; @@ -133,12 +132,13 @@ public: /* * Lock the mutex. */ - void lock() const noexcept + bool lock() const noexcept { #ifdef DISTRHO_OS_WINDOWS EnterCriticalSection(&fSection); + return true; #else - pthread_mutex_lock(&fMutex); + return (pthread_mutex_lock(&fMutex) == 0); #endif } @@ -174,7 +174,6 @@ private: mutable pthread_mutex_t fMutex; #endif - DISTRHO_PREVENT_HEAP_ALLOCATION DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex) }; @@ -295,6 +294,10 @@ public: : fMutex(mutex), fLocked(mutex.tryLock()) {} + ScopeTryLocker(const Mutex& mutex, const bool forceLock) noexcept + : fMutex(mutex), + fLocked(forceLock ? mutex.lock() : mutex.tryLock()) {} + ~ScopeTryLocker() noexcept { if (fLocked) diff --git a/distrho/extra/Thread.hpp b/distrho/extra/Thread.hpp index c888cf8b..5a4ecffa 100644 --- a/distrho/extra/Thread.hpp +++ b/distrho/extra/Thread.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 From 858b4fe7a91f8fdea7db2ee22706d74da6167f61 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 12:14:36 +0100 Subject: [PATCH 132/159] Rework String class to remove VLA use Signed-off-by: falkTX --- distrho/extra/String.hpp | 199 ++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 64 deletions(-) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index 72d2ca20..7cf85bf4 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -37,14 +37,16 @@ public: */ explicit String() noexcept : fBuffer(_null()), - fBufferLen(0) {} + fBufferLen(0), + fBufferAlloc(false) {} /* * Simple character. */ explicit String(const char c) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char ch[2]; ch[0] = c; @@ -58,7 +60,8 @@ public: */ explicit String(char* const strBuf, const bool copyData = true) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { if (copyData || strBuf == nullptr) { @@ -66,10 +69,10 @@ public: } else { - fBuffer = strBuf; - fBufferLen = std::strlen(strBuf); + fBuffer = strBuf; + fBufferLen = std::strlen(strBuf); + fBufferAlloc = true; } - } /* @@ -77,7 +80,8 @@ public: */ explicit String(const char* const strBuf) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { _dup(strBuf); } @@ -87,7 +91,8 @@ public: */ explicit String(const int value) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, "%d", value); @@ -101,7 +106,8 @@ public: */ explicit String(const unsigned int value, const bool hexadecimal = false) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); @@ -115,7 +121,8 @@ public: */ explicit String(const long value) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, "%ld", value); @@ -129,7 +136,8 @@ public: */ explicit String(const unsigned long value, const bool hexadecimal = false) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); @@ -143,7 +151,8 @@ public: */ explicit String(const long long value) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, "%lld", value); @@ -157,7 +166,8 @@ public: */ explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value); @@ -171,10 +181,17 @@ public: */ explicit String(const float value) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; - std::snprintf(strBuf, 0xff, "%.12g", static_cast(value)); + + { + // TODO + // const ScopedLocale csl; + std::snprintf(strBuf, 0xff, "%.12g", static_cast(value)); + } + strBuf[0xff] = '\0'; _dup(strBuf); @@ -185,10 +202,17 @@ public: */ explicit String(const double value) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { char strBuf[0xff+1]; - std::snprintf(strBuf, 0xff, "%.24g", value); + + { + // TODO + // const ScopedLocale csl; + std::snprintf(strBuf, 0xff, "%.24g", value); + } + strBuf[0xff] = '\0'; _dup(strBuf); @@ -202,7 +226,8 @@ public: */ String(const String& str) noexcept : fBuffer(_null()), - fBufferLen(0) + fBufferLen(0), + fBufferAlloc(false) { _dup(str.fBuffer); } @@ -217,13 +242,12 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,); - if (fBuffer == _null()) - return; - - std::free(fBuffer); + if (fBufferAlloc) + std::free(fBuffer); - fBuffer = nullptr; - fBufferLen = 0; + fBuffer = nullptr; + fBufferLen = 0; + fBufferAlloc = false; } // ------------------------------------------------------------------- @@ -253,6 +277,20 @@ public: return (fBufferLen != 0); } + /* + * Check if the string contains a specific character, case-sensitive. + */ + bool contains(const char c) const noexcept + { + for (std::size_t i=0; i= 0", __FILE__, __LINE__); + d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret)); if (found != nullptr) *found = false; @@ -498,9 +536,7 @@ public: if (n >= fBufferLen) return *this; - for (std::size_t i=n; i < fBufferLen; ++i) - fBuffer[i] = '\0'; - + fBuffer[n] = '\0'; fBufferLen = n; return *this; @@ -529,7 +565,7 @@ public: } /* - * Convert to all ascii characters to lowercase. + * Convert all ascii characters to lowercase. */ String& toLower() noexcept { @@ -545,7 +581,7 @@ public: } /* - * Convert to all ascii characters to uppercase. + * Convert all ascii characters to uppercase. */ String& toUpper() noexcept { @@ -570,13 +606,15 @@ public: /* * Get and release the string buffer, while also clearing this string. + * This allows to keep a pointer to the buffer after this object is deleted. * Result must be freed. */ char* getAndReleaseBuffer() noexcept { - char* const ret = fBuffer; + char* ret = fBufferLen > 0 ? fBuffer : nullptr; fBuffer = _null(); fBufferLen = 0; + fBufferAlloc = false; return ret; } @@ -591,7 +629,7 @@ public: "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; - const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U); + const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast(dataSize/3)), 65536U); const uchar* bytesToEncode((const uchar*)data); @@ -723,16 +761,26 @@ public: String& operator+=(const char* const strBuf) noexcept { - if (strBuf == nullptr) + if (strBuf == nullptr || strBuf[0] == '\0') + return *this; + + const std::size_t strBufLen = std::strlen(strBuf); + + // for empty strings, we can just take the appended string as our entire data + if (isEmpty()) + { + _dup(strBuf, strBufLen); return *this; + } - const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1; - char newBuf[newBufSize]; + // we have some data ourselves, reallocate to add the new stuff + char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1); + DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); - std::strcpy(newBuf, fBuffer); - std::strcat(newBuf, strBuf); + std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); - _dup(newBuf, newBufSize-1); + fBuffer = newBuf; + fBufferLen += strBufLen; return *this; } @@ -744,13 +792,18 @@ public: String operator+(const char* const strBuf) noexcept { - const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1; - char newBuf[newBufSize]; + if (strBuf == nullptr || strBuf[0] == '\0') + return *this; + if (isEmpty()) + return String(strBuf); - std::strcpy(newBuf, fBuffer); + const std::size_t strBufLen = std::strlen(strBuf); + const std::size_t newBufSize = fBufferLen + strBufLen; + char* const newBuf = (char*)malloc(newBufSize + 1); + DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); - if (strBuf != nullptr) - std::strcat(newBuf, strBuf); + std::memcpy(newBuf, fBuffer, fBufferLen); + std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); return String(newBuf); } @@ -763,8 +816,9 @@ public: // ------------------------------------------------------------------- private: - char* fBuffer; // the actual string buffer - std::size_t fBufferLen; // string length + char* fBuffer; // the actual string buffer + std::size_t fBufferLen; // string length + bool fBufferAlloc; // wherever the buffer is allocated, not using _null() /* * Static null string. @@ -792,7 +846,7 @@ private: if (std::strcmp(fBuffer, strBuf) == 0) return; - if (fBuffer != _null()) + if (fBufferAlloc) std::free(fBuffer); fBufferLen = (size > 0) ? size : std::strlen(strBuf); @@ -800,28 +854,31 @@ private: if (fBuffer == nullptr) { - fBuffer = _null(); - fBufferLen = 0; + fBuffer = _null(); + fBufferLen = 0; + fBufferAlloc = false; return; } - std::strcpy(fBuffer, strBuf); + fBufferAlloc = true; + std::strcpy(fBuffer, strBuf); fBuffer[fBufferLen] = '\0'; } else { - DISTRHO_SAFE_ASSERT(size == 0); + DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast(size)); // don't recreate null string - if (fBuffer == _null()) + if (! fBufferAlloc) return; DISTRHO_SAFE_ASSERT(fBuffer != nullptr); std::free(fBuffer); - fBuffer = _null(); - fBufferLen = 0; + fBuffer = _null(); + fBufferLen = 0; + fBufferAlloc = false; } } @@ -833,12 +890,19 @@ private: static inline String operator+(const String& strBefore, const char* const strBufAfter) noexcept { - const char* const strBufBefore = strBefore.buffer(); - const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; - char newBuf[newBufSize]; + if (strBufAfter == nullptr || strBufAfter[0] == '\0') + return strBefore; + if (strBefore.isEmpty()) + return String(strBufAfter); - std::strcpy(newBuf, strBufBefore); - std::strcat(newBuf, strBufAfter); + const std::size_t strBeforeLen = strBefore.length(); + const std::size_t strBufAfterLen = std::strlen(strBufAfter); + const std::size_t newBufSize = strBeforeLen + strBufAfterLen; + char* const newBuf = (char*)malloc(newBufSize + 1); + DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); + + std::memcpy(newBuf, strBefore.buffer(), strBeforeLen); + std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1); return String(newBuf); } @@ -846,12 +910,19 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep static inline String operator+(const char* const strBufBefore, const String& strAfter) noexcept { - const char* const strBufAfter = strAfter.buffer(); - const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; - char newBuf[newBufSize]; - - std::strcpy(newBuf, strBufBefore); - std::strcat(newBuf, strBufAfter); + if (strAfter.isEmpty()) + return String(strBufBefore); + if (strBufBefore == nullptr || strBufBefore[0] == '\0') + return strAfter; + + const std::size_t strBufBeforeLen = std::strlen(strBufBefore); + const std::size_t strAfterLen = strAfter.length(); + const std::size_t newBufSize = strBufBeforeLen + strAfterLen; + char* const newBuf = (char*)malloc(newBufSize + 1); + DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); + + std::memcpy(newBuf, strBufBefore, strBufBeforeLen); + std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1); return String(newBuf); } From 284460d2692e48476156ebf51ed9417d0d2fbdcb Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 12:39:35 +0100 Subject: [PATCH 133/159] Introduce ScopedSafeLocale class, use it in a few places Signed-off-by: falkTX --- distrho/extra/ScopedSafeLocale.hpp | 136 +++++++++++++++++++++++++++++ distrho/extra/String.hpp | 9 +- distrho/src/DistrhoPluginVST.cpp | 32 +------ 3 files changed, 142 insertions(+), 35 deletions(-) create mode 100644 distrho/extra/ScopedSafeLocale.hpp diff --git a/distrho/extra/ScopedSafeLocale.hpp b/distrho/extra/ScopedSafeLocale.hpp new file mode 100644 index 00000000..9c6293c2 --- /dev/null +++ b/distrho/extra/ScopedSafeLocale.hpp @@ -0,0 +1,136 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED +#define DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +#include + +#if ! (defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) +# define DISTRHO_USE_NEWLOCALE +#endif + +#if defined(DISTRHO_OS_WINDOWS) && __MINGW64_VERSION_MAJOR >= 5 +# define DISTRHO_USE_CONFIGTHREADLOCALE +#endif + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// ScopedSafeLocale class definition + +/** + ScopedSafeLocale is a handy class for setting current locale to C on constructor, and revert back on destructor. + It tries to be thread-safe, but it is not always possible. + + Put it inside a scope of code where string conversions happen to ensure they are consistent across many systems. + For example: + + ``` + // stack buffer to put converted float value in + char strbuf[0xff]; + + { + // safe locale operations during this scope + const ScopedSafeLocale sl; + snprintf(strbuf, 0xff, "%f", value); + } + + // do something with `strbuf` now, locale is reverted and left just as it was before + ``` + */ +class ScopedSafeLocale { +public: + /* + * Constructor. + * Current system locale will saved, while "C" is set as the next one to use. + */ + inline ScopedSafeLocale() noexcept; + + /* + * Destructor. + * System locale will revert back to the one saved during constructor. + */ + inline ~ScopedSafeLocale() noexcept; + +private: +#ifdef DISTRHO_USE_NEWLOCALE + locale_t newloc, oldloc; +#else +# ifdef DISTRHO_USE_CONFIGTHREADLOCALE + const int oldthreadloc; +# endif + char* const oldloc; +#endif + + DISTRHO_DECLARE_NON_COPYABLE(ScopedSafeLocale) + DISTRHO_PREVENT_HEAP_ALLOCATION +}; + +// ----------------------------------------------------------------------- +// ScopedSafeLocale class implementation + +#ifdef DISTRHO_USE_NEWLOCALE +static constexpr const locale_t kNullLocale = (locale_t)nullptr; +#endif + +inline ScopedSafeLocale::ScopedSafeLocale() noexcept +#ifdef DISTRHO_USE_NEWLOCALE + : newloc(::newlocale(LC_NUMERIC_MASK, "C", kNullLocale)), + oldloc(newloc != kNullLocale ? ::uselocale(newloc) : kNullLocale) {} +#else +# ifdef DISTRHO_USE_CONFIGTHREADLOCALE + : oldthreadloc(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)), +# else + : +# endif + oldloc(strdup(::setlocale(LC_NUMERIC, nullptr))) +{ + ::setlocale(LC_NUMERIC, "C"); +} +#endif + +inline ScopedSafeLocale::~ScopedSafeLocale() noexcept +{ +#ifdef DISTRHO_USE_NEWLOCALE + if (oldloc != kNullLocale) + ::uselocale(oldloc); + if (newloc != kNullLocale) + ::freelocale(newloc); +#else // DISTRHO_USE_NEWLOCALE + if (oldloc != nullptr) + { + ::setlocale(LC_NUMERIC, oldloc); + std::free(oldloc); + } + +# ifdef DISTRHO_USE_CONFIGTHREADLOCALE + if (oldthreadloc != -1) + _configthreadlocale(oldthreadloc); +# endif +#endif // DISTRHO_USE_NEWLOCALE +} + +// ----------------------------------------------------------------------- + +#undef DISTRHO_USE_CONFIGTHREADLOCALE +#undef DISTRHO_USE_NEWLOCALE + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index 7cf85bf4..f74c430e 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,6 +18,7 @@ #define DISTRHO_STRING_HPP_INCLUDED #include "../DistrhoUtils.hpp" +#include "../extra/ScopedSafeLocale.hpp" #include @@ -187,8 +188,7 @@ public: char strBuf[0xff+1]; { - // TODO - // const ScopedLocale csl; + const ScopedSafeLocale ssl; std::snprintf(strBuf, 0xff, "%.12g", static_cast(value)); } @@ -208,8 +208,7 @@ public: char strBuf[0xff+1]; { - // TODO - // const ScopedLocale csl; + const ScopedSafeLocale ssl; std::snprintf(strBuf, 0xff, "%.24g", value); } diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index e5bc91c8..a2fd36ca 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -15,6 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" +#include "../extra/ScopedSafeLocale.hpp" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI # undef DISTRHO_PLUGIN_HAS_UI @@ -104,32 +105,6 @@ void snprintf_iparam(char* const dst, const int32_t value, const size_t size) // ----------------------------------------------------------------------- -class ScopedSafeLocale { -public: - ScopedSafeLocale() noexcept - : locale(::strdup(::setlocale(LC_NUMERIC, nullptr))) - { - ::setlocale(LC_NUMERIC, "C"); - } - - ~ScopedSafeLocale() noexcept - { - if (locale != nullptr) - { - ::setlocale(LC_NUMERIC, locale); - std::free(locale); - } - } - -private: - char* const locale; - - DISTRHO_DECLARE_NON_COPY_CLASS(ScopedSafeLocale) - DISTRHO_PREVENT_HEAP_ALLOCATION -}; - -// ----------------------------------------------------------------------- - struct ParameterCheckHelper { bool* parameterChecks; @@ -778,9 +753,6 @@ public: // add another separator chunkStr += "\xff"; - // temporarily set locale to "C" while converting floats - const ScopedSafeLocale ssl; - for (uint32_t i=0; i Date: Sat, 22 May 2021 12:44:41 +0100 Subject: [PATCH 134/159] Use DISTRHO_DECLARE_NON_COPYABLE Signed-off-by: falkTX --- dgl/src/Common.hpp | 4 ++-- dgl/src/ImageBaseWidgets.cpp | 6 +++--- distrho/DistrhoPlugin.hpp | 2 +- distrho/extra/ExternalWindow.hpp | 2 +- distrho/extra/LeakDetector.hpp | 4 ++-- distrho/extra/Mutex.hpp | 12 ++++++------ distrho/extra/Thread.hpp | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index ebb599a7..b55a62ac 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -115,7 +115,7 @@ struct ButtonImpl { } DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl) + DISTRHO_DECLARE_NON_COPYABLE(ButtonImpl) }; // ----------------------------------------------------------------------- @@ -178,7 +178,7 @@ struct ImageBaseKnob::PrivateData { return std::log(value/a)/b; } - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) }; // ----------------------------------------------------------------------- diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 236c0ab5..fcf04b1c 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -109,7 +109,7 @@ struct ImageBaseButton::PrivateData { imageHover(hover), imageDown(down) {} - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) }; // -------------------------------------------------------------------------------------------------------------------- @@ -617,7 +617,7 @@ struct ImageBaseSlider::PrivateData { } } - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) }; // -------------------------------------------------------------------------------------------------------------------- @@ -976,7 +976,7 @@ struct ImageBaseSwitch::PrivateData { DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); } - DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) + DISTRHO_DECLARE_NON_COPYABLE(PrivateData) }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 9e55c87f..83315665 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -369,7 +369,7 @@ struct ParameterEnumerationValues { } } - DISTRHO_DECLARE_NON_COPY_STRUCT(ParameterEnumerationValues) + DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues) }; /** diff --git a/distrho/extra/ExternalWindow.hpp b/distrho/extra/ExternalWindow.hpp index c39f55c7..68c35c41 100644 --- a/distrho/extra/ExternalWindow.hpp +++ b/distrho/extra/ExternalWindow.hpp @@ -192,7 +192,7 @@ private: friend class UIExporter; - DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow) + DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) }; // ----------------------------------------------------------------------- diff --git a/distrho/extra/LeakDetector.hpp b/distrho/extra/LeakDetector.hpp index b146efc7..765e4676 100644 --- a/distrho/extra/LeakDetector.hpp +++ b/distrho/extra/LeakDetector.hpp @@ -54,13 +54,13 @@ START_NAMESPACE_DISTRHO DISTRHO_NAMESPACE::LeakedObjectDetector DISTRHO_JOIN_MACRO(leakDetector_, ClassName); # define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \ - DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ + DISTRHO_DECLARE_NON_COPYABLE(ClassName) \ DISTRHO_LEAK_DETECTOR(ClassName) #else /** Don't use leak detection on release builds. */ # define DISTRHO_LEAK_DETECTOR(ClassName) # define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \ - DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) + DISTRHO_DECLARE_NON_COPYABLE(ClassName) #endif //============================================================================== diff --git a/distrho/extra/Mutex.hpp b/distrho/extra/Mutex.hpp index 90332c30..9843bfcb 100644 --- a/distrho/extra/Mutex.hpp +++ b/distrho/extra/Mutex.hpp @@ -86,7 +86,7 @@ public: private: mutable pthread_mutex_t fMutex; - DISTRHO_DECLARE_NON_COPY_CLASS(Mutex) + DISTRHO_DECLARE_NON_COPYABLE(Mutex) }; // ----------------------------------------------------------------------- @@ -174,7 +174,7 @@ private: mutable pthread_mutex_t fMutex; #endif - DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex) + DISTRHO_DECLARE_NON_COPYABLE(RecursiveMutex) }; // ----------------------------------------------------------------------- @@ -255,7 +255,7 @@ private: volatile bool fTriggered; DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPY_CLASS(Signal) + DISTRHO_DECLARE_NON_COPYABLE(Signal) }; // ----------------------------------------------------------------------- @@ -280,7 +280,7 @@ private: const Mutex& fMutex; DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker) + DISTRHO_DECLARE_NON_COPYABLE(ScopeLocker) }; // ----------------------------------------------------------------------- @@ -319,7 +319,7 @@ private: const bool fLocked; DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker) + DISTRHO_DECLARE_NON_COPYABLE(ScopeTryLocker) }; // ----------------------------------------------------------------------- @@ -344,7 +344,7 @@ private: const Mutex& fMutex; DISTRHO_PREVENT_HEAP_ALLOCATION - DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker) + DISTRHO_DECLARE_NON_COPYABLE(ScopeUnlocker) }; // ----------------------------------------------------------------------- diff --git a/distrho/extra/Thread.hpp b/distrho/extra/Thread.hpp index 5a4ecffa..ec6f13d9 100644 --- a/distrho/extra/Thread.hpp +++ b/distrho/extra/Thread.hpp @@ -324,7 +324,7 @@ private: return nullptr; } - DISTRHO_DECLARE_NON_COPY_CLASS(Thread) + DISTRHO_DECLARE_NON_COPYABLE(Thread) }; // ----------------------------------------------------------------------- From c073735cc3b8feddf27cce40fc6976b0350bb062 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 13:19:32 +0100 Subject: [PATCH 135/159] Fix DISTRHO_MACRO_AS_STRING Signed-off-by: falkTX --- distrho/DistrhoUtils.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp index 7f65f523..9741d82b 100644 --- a/distrho/DistrhoUtils.hpp +++ b/distrho/DistrhoUtils.hpp @@ -50,7 +50,8 @@ inline float round(float __x) # define M_PI 3.14159265358979323846 #endif -#define DISTRHO_MACRO_AS_STRING(MACRO) #MACRO +#define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO +#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) // ----------------------------------------------------------------------- // misc functions From afbac6a2829b01b0d55d41ee9678d1eb79393411 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 13:25:49 +0100 Subject: [PATCH 136/159] Update pugl Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 9cab7cdc..13cd61b4 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 9cab7cdcba159aec5f3df4d5841b83c12ef64185 +Subproject commit 13cd61b4915d3314cc8e43e12a3baccef6650c94 From c4be63627713c10b13ca43d8e4d118f9c5e9414c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 13:27:05 +0100 Subject: [PATCH 137/159] Ignore focus-out events on closed windows Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 4 ++++ tests/Makefile | 6 +++++- tests/tests.hpp | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 5ed30ba6..302d3ac4 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -303,6 +303,7 @@ void Window::PrivateData::hide() void Window::PrivateData::close() { DGL_DBG("Window close\n"); + // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData); if (isEmbed || isClosed) return; @@ -531,6 +532,9 @@ void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) { DGL_DBGp("onPuglFocus : %i %i\n", focus, mode); + if (isClosed) + return; + if (modal.child != nullptr) return modal.child->focus(); diff --git a/tests/Makefile b/tests/Makefile index 7c6e2d30..66d5f84e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -33,7 +33,7 @@ ifeq ($(HAVE_STUB),true) UNIT_TESTS += Window.stub endif ifeq ($(HAVE_VULKAN),true) -UNIT_TESTS += Window.vulkan +UNIT_TESTS += Window.vulkan endif MANUAL_TARGETS = $(MANUAL_TESTS:%=../build/tests/%$(APP_EXT)) @@ -144,5 +144,9 @@ clean: -include ../build/tests/Demo.cpp.cairo.d -include ../build/tests/Demo.cpp.opengl.d -include ../build/tests/Demo.cpp.vulkan.d +-include ../build/tests/Window.cpp.cairo.d +-include ../build/tests/Window.cpp.opengl.d +-include ../build/tests/Window.cpp.stub.d +-include ../build/tests/Window.cpp.vulkan.d # --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/tests.hpp b/tests/tests.hpp index c9b8b0cc..334dd1fe 100644 --- a/tests/tests.hpp +++ b/tests/tests.hpp @@ -49,6 +49,7 @@ private: void run() override { d_sleep(numSecondsToWait); + d_stdout("About to quit now..."); app.quit(); } }; From d592c24dc2112a6dee45983ae8e859afc6fa6d78 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 14:20:45 +0100 Subject: [PATCH 138/159] Fix some strict compiler warnings Signed-off-by: falkTX --- Makefile.base.mk | 2 +- dgl/Cairo.hpp | 4 +- dgl/OpenGL.hpp | 4 +- dgl/Vulkan.hpp | 4 +- dgl/src/Cairo.cpp | 81 +++++++++++++++++++---------------- dgl/src/Common.hpp | 14 +++--- dgl/src/Geometry.cpp | 49 +++++++++++++-------- dgl/src/ImageBase.cpp | 10 ++--- dgl/src/ImageBaseWidgets.cpp | 61 +++++++++++++------------- dgl/src/OpenGL.cpp | 65 ++++++++++++++++------------ dgl/src/SubWidget.cpp | 4 +- dgl/src/Vulkan.cpp | 8 ++-- dgl/src/Window.cpp | 23 ++++++---- dgl/src/WindowPrivateData.cpp | 20 +++++---- dgl/src/WindowPrivateData.hpp | 5 ++- dgl/src/pugl.cpp | 12 +++--- 16 files changed, 204 insertions(+), 162 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 83d64e65..59433f83 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -197,7 +197,7 @@ endif ifeq ($(TESTBUILD),true) BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings -BASE_FLAGS += -Wpointer-arith -Wabi -Winit-self -Wuninitialized -Wstrict-overflow=5 +BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5 # BASE_FLAGS += -Wfloat-equal ifeq ($(CC),clang) BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 8857e126..567aa63f 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -106,8 +106,8 @@ public: CairoImage& operator=(const CairoImage& image) noexcept; // FIXME this should not be needed - inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) - { loadFromMemory(rawData, Size(w, h), format); }; + inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) + { loadFromMemory(rdata, Size(w, h), fmt); }; inline void draw(const GraphicsContext& context) { drawAt(context, Point(0, 0)); }; inline void drawAt(const GraphicsContext& context, int x, int y) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index f28f2277..deb677fc 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -222,8 +222,8 @@ public: OpenGLImage& operator=(const OpenGLImage& image) noexcept; // FIXME this should not be needed - inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) - { loadFromMemory(rawData, Size(w, h), format); }; + inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) + { loadFromMemory(rdata, Size(w, h), fmt); }; inline void draw(const GraphicsContext& context) { drawAt(context, Point(0, 0)); }; inline void drawAt(const GraphicsContext& context, int x, int y) diff --git a/dgl/Vulkan.hpp b/dgl/Vulkan.hpp index d7b73056..4fd7c45a 100644 --- a/dgl/Vulkan.hpp +++ b/dgl/Vulkan.hpp @@ -88,8 +88,8 @@ public: VulkanImage& operator=(const VulkanImage& image) noexcept; // FIXME this should not be needed - inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format = kImageFormatBGRA) - { loadFromMemory(rawData, Size(w, h), format); }; + inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) + { loadFromMemory(rdata, Size(w, h), fmt); }; inline void draw(const GraphicsContext& context) { drawAt(context, Point(0, 0)); }; inline void drawAt(const GraphicsContext& context, int x, int y) diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index f92e3bd2..c505099f 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -326,22 +326,22 @@ CairoImage::CairoImage() surfacedata(nullptr), datarefcount(nullptr) {} -CairoImage::CairoImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) - : ImageBase(rawData, width, height, format), +CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) + : ImageBase(rdata, w, h, fmt), surface(nullptr), surfacedata(nullptr), datarefcount(nullptr) { - loadFromMemory(rawData, width, height, format); + loadFromMemory(rdata, w, h, fmt); } -CairoImage::CairoImage(const char* const rawData, const Size& size, const ImageFormat format) - : ImageBase(rawData, size, format), +CairoImage::CairoImage(const char* const rdata, const Size& s, const ImageFormat fmt) + : ImageBase(rdata, s, fmt), surface(nullptr), surfacedata(nullptr), datarefcount(nullptr) { - loadFromMemory(rawData, size, format); + loadFromMemory(rdata, s, fmt); } CairoImage::CairoImage(const CairoImage& image) @@ -368,11 +368,11 @@ CairoImage::~CairoImage() void CairoImage::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept { const cairo_format_t cairoformat = asCairoImageFormat(fmt); - const uint width = s.getWidth(); - const uint height = s.getHeight(); - const int stride = cairo_format_stride_for_width(cairoformat, width); + const int width = static_cast(s.getWidth()); + const int height = static_cast(s.getHeight()); + const int stride = cairo_format_stride_for_width(cairoformat, width); - uchar* const newdata = (uchar*)std::malloc(width * height * stride * 4); + uchar* const newdata = (uchar*)std::malloc(static_cast(width * height * stride * 4)); DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,); cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); @@ -401,13 +401,13 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co break; case kImageFormatBGR: // BGR8 to CAIRO_FORMAT_RGB24 - for (uint h = 0; h < height; ++h) + for (int h = 0; h < height; ++h) { - for (uint w = 0; w < width; ++w) + for (int w = 0; w < width; ++w) { - newdata[h*width*4+w*4+0] = rdata[h*width*3+w*3+0]; - newdata[h*width*4+w*4+1] = rdata[h*width*3+w*3+1]; - newdata[h*width*4+w*4+2] = rdata[h*width*3+w*3+2]; + newdata[h*width*4+w*4+0] = static_cast(rdata[h*width*3+w*3+0]); + newdata[h*width*4+w*4+1] = static_cast(rdata[h*width*3+w*3+1]); + newdata[h*width*4+w*4+2] = static_cast(rdata[h*width*3+w*3+2]); newdata[h*width*4+w*4+3] = 0; } } @@ -415,16 +415,16 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co case kImageFormatBGRA: // BGRA8 to CAIRO_FORMAT_ARGB32 // FIXME something is wrong here... - for (uint h = 0, t; h < height; ++h) + for (int h = 0, t; h < height; ++h) { - for (uint w = 0; w < width; ++w) + for (int w = 0; w < width; ++w) { if ((t = rdata[h*width*4+w*4+3]) != 0) { - newdata[h*width*4+w*4+0] = rdata[h*width*4+w*4+0]; - newdata[h*width*4+w*4+1] = rdata[h*width*4+w*4+1]; - newdata[h*width*4+w*4+2] = rdata[h*width*4+w*4+2]; - newdata[h*width*4+w*4+3] = t; + newdata[h*width*4+w*4+0] = static_cast(rdata[h*width*4+w*4+0]); + newdata[h*width*4+w*4+1] = static_cast(rdata[h*width*4+w*4+1]); + newdata[h*width*4+w*4+2] = static_cast(rdata[h*width*4+w*4+2]); + newdata[h*width*4+w*4+3] = static_cast(t); } else { @@ -476,6 +476,11 @@ void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noex cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData); DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); + const int newwidth = cairo_image_surface_get_width(newsurface); + const int newheight = cairo_image_surface_get_height(newsurface); + DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,); + DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,); + cairo_surface_destroy(surface); if (datarefcount != nullptr && --(*datarefcount) == 0) @@ -489,7 +494,7 @@ void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noex rawData = nullptr; format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface)); - size = Size(cairo_image_surface_get_width(newsurface), cairo_image_surface_get_height(newsurface)); + size = Size(static_cast(newwidth), static_cast(newheight)); } void CairoImage::drawAt(const GraphicsContext& context, const Point& pos) @@ -597,7 +602,7 @@ void ImageBaseKnob::PrivateData::cleanup() Get the pixel size in bytes. @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes. */ -static uint getBytesPerPixel(cairo_format_t format) noexcept +static int getBytesPerPixel(const cairo_format_t format) noexcept { switch (format) { @@ -617,17 +622,17 @@ static uint getBytesPerPixel(cairo_format_t format) noexcept } } -static cairo_surface_t* getRegion(cairo_surface_t* origsurface, uint x, uint y, uint width, uint height) noexcept +static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept { const cairo_format_t format = cairo_image_surface_get_format(origsurface); - const uint bpp = getBytesPerPixel(format); + const int bpp = getBytesPerPixel(format); if (bpp == 0) return nullptr; - const uint fullWidth = cairo_image_surface_get_width(origsurface); - const uint fullHeight = cairo_image_surface_get_height(origsurface); - const uint stride = cairo_image_surface_get_stride(origsurface); + const int fullWidth = cairo_image_surface_get_width(origsurface); + const int fullHeight = cairo_image_surface_get_height(origsurface); + const int stride = cairo_image_surface_get_stride(origsurface); uchar* const fullData = cairo_image_surface_get_data(origsurface); x = (x < fullWidth) ? x : fullWidth; @@ -635,7 +640,7 @@ static cairo_surface_t* getRegion(cairo_surface_t* origsurface, uint x, uint y, width = (x + width < fullWidth) ? width : (fullWidth - x); height = (x + height < fullHeight) ? height : (fullHeight - x); - uchar* const data = fullData + x * bpp + y * stride; + uchar* const data = fullData + (x * bpp + y * stride); return cairo_image_surface_create_for_data(data, format, width, height, stride); } @@ -644,22 +649,22 @@ void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; - const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) + const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) / (pData->maximum - pData->minimum); cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; if (! pData->isReady) { - const uint layerW = pData->imgLayerWidth; - const uint layerH = pData->imgLayerHeight; - uint layerNum = 0; + const int layerW = static_cast(pData->imgLayerWidth); + const int layerH = static_cast(pData->imgLayerHeight); + int layerNum = 0; if (pData->rotationAngle == 0) - layerNum = uint(normValue * float(pData->imgLayerCount-1)); + layerNum = static_cast(normValue * static_cast(pData->imgLayerCount - 1) + 0.5); - const uint layerX = pData->isImgVertical ? 0 : layerNum * layerW; - const uint layerY = !pData->isImgVertical ? 0 : layerNum * layerH; + const int layerX = pData->isImgVertical ? 0 : layerNum * layerW; + const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH; cairo_surface_t* newsurface; @@ -672,8 +677,8 @@ void ImageBaseKnob::onDisplay() newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH); cairo_t* const cr = cairo_create(newsurface); cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); - cairo_rotate(cr, normValue * pData->rotationAngle * (float)(M_PI / 180)); - cairo_set_source_surface(cr, pData->image.getSurface(), -0.5f * layerW, -0.5f * layerH); + cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180)); + cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH); cairo_paint(cr); cairo_destroy(cr); } diff --git a/dgl/src/Common.hpp b/dgl/src/Common.hpp index b55a62ac..b0f86629 100644 --- a/dgl/src/Common.hpp +++ b/dgl/src/Common.hpp @@ -75,7 +75,7 @@ struct ButtonImpl { // button was pressed, wait for release if (ev.press && self->contains(ev.pos)) { - button = ev.button; + button = static_cast(ev.button); state = kStateDown; self->repaint(); return true; @@ -135,8 +135,8 @@ struct ImageBaseKnob::PrivateData { int rotationAngle; bool dragging; - int lastX; - int lastY; + double lastX; + double lastY; Callback* callback; @@ -164,18 +164,18 @@ struct ImageBaseKnob::PrivateData { void init(); void cleanup(); - inline float logscale(float value) const + inline float logscale(const float v) const { const float b = std::log(maximum/minimum)/(maximum-minimum); const float a = maximum/std::exp(maximum*b); - return a * std::exp(b*value); + return a * std::exp(b*v); } - inline float invlogscale(float value) const + inline float invlogscale(const float v) const { const float b = std::log(maximum/minimum)/(maximum-minimum); const float a = maximum/std::exp(maximum*b); - return std::log(value/a)/b; + return std::log(v/a)/b; } DISTRHO_DECLARE_NON_COPYABLE(PrivateData) diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index 4ad25db4..f2c44c44 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -249,7 +249,22 @@ bool Size::isInvalid() const noexcept template Size Size::toInt() const noexcept { - return Size(fWidth, fHeight); + return Size(static_cast(fWidth), + static_cast(fHeight)); +} + +template<> +Size Size::toInt() const noexcept +{ + return Size(static_cast(fWidth + 0.5), + static_cast(fHeight + 0.5)); +} + +template<> +Size Size::toInt() const noexcept +{ + return Size(static_cast(fWidth + 0.5f), + static_cast(fHeight + 0.5f)); } template @@ -645,10 +660,10 @@ Triangle::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const pos3(x3, y3) {} template -Triangle::Triangle(const Point& pos1, const Point& pos2, const Point& pos3) noexcept - : pos1(pos1), - pos2(pos2), - pos3(pos3) {} +Triangle::Triangle(const Point& p1, const Point& p2, const Point& p3) noexcept + : pos1(p1), + pos2(p2), + pos3(p3) {} template Triangle::Triangle(const Triangle& tri) noexcept @@ -710,24 +725,24 @@ Rectangle::Rectangle() noexcept size(0, 0) {} template -Rectangle::Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept +Rectangle::Rectangle(const T& x, const T& y, const T& w, const T& h) noexcept : pos(x, y), - size(width, height) {} + size(w, h) {} template -Rectangle::Rectangle(const T& x, const T& y, const Size& size) noexcept +Rectangle::Rectangle(const T& x, const T& y, const Size& s) noexcept : pos(x, y), - size(size) {} + size(s) {} template -Rectangle::Rectangle(const Point& pos, const T& width, const T& height) noexcept - : pos(pos), - size(width, height) {} +Rectangle::Rectangle(const Point& p, const T& w, const T& h) noexcept + : pos(p), + size(w, h) {} template -Rectangle::Rectangle(const Point& pos, const Size& size) noexcept - : pos(pos), - size(size) {} +Rectangle::Rectangle(const Point& p, const Size& s) noexcept + : pos(p), + size(s) {} template Rectangle::Rectangle(const Rectangle& rect) noexcept @@ -865,9 +880,9 @@ bool Rectangle::contains(const T& x, const T& y) const noexcept } template -bool Rectangle::contains(const Point& pos) const noexcept +bool Rectangle::contains(const Point& p) const noexcept { - return contains(pos.x, pos.y); + return contains(p.x, p.y); } template diff --git a/dgl/src/ImageBase.cpp b/dgl/src/ImageBase.cpp index ea3477b6..ae04b147 100644 --- a/dgl/src/ImageBase.cpp +++ b/dgl/src/ImageBase.cpp @@ -81,12 +81,12 @@ ImageFormat ImageBase::getFormat() const noexcept return format; } -void ImageBase::loadFromMemory(const char* const rawData, - const uint width, - const uint height, - const ImageFormat format) noexcept +void ImageBase::loadFromMemory(const char* const rdata, + const uint width, + const uint height, + const ImageFormat fmt) noexcept { - loadFromMemory(rawData, Size(width, height), format); + loadFromMemory(rdata, Size(width, height), fmt); } void ImageBase::loadFromMemory(const char* const rdata, const Size& s, const ImageFormat fmt) noexcept diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index fcf04b1c..2d056446 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -201,8 +201,8 @@ ImageBaseKnob::PrivateData::PrivateData(const ImageType& img, const O orientation(o), rotationAngle(0), dragging(false), - lastX(0), - lastY(0), + lastX(0.0), + lastY(0.0), callback(nullptr), alwaysRepaint(false), isImgVertical(img.getHeight() > img.getWidth()), @@ -228,8 +228,8 @@ ImageBaseKnob::PrivateData::PrivateData(PrivateData* const other) orientation(other->orientation), rotationAngle(other->rotationAngle), dragging(false), - lastX(0), - lastY(0), + lastX(0.0), + lastY(0.0), callback(other->callback), alwaysRepaint(other->alwaysRepaint), isImgVertical(other->isImgVertical), @@ -245,21 +245,21 @@ template void ImageBaseKnob::PrivateData::assignFrom(PrivateData* const other) { cleanup(); - image = other->image; - minimum = other->minimum; - maximum = other->maximum; - step = other->step; - value = other->value; - valueDef = other->valueDef; - valueTmp = value; - usingDefault = other->usingDefault; - usingLog = other->usingLog; - orientation = other->orientation; - rotationAngle = other->rotationAngle; - dragging = false; - lastX = 0; - lastY = 0; - callback = other->callback; + image = other->image; + minimum = other->minimum; + maximum = other->maximum; + step = other->step; + value = other->value; + valueDef = other->valueDef; + valueTmp = value; + usingDefault = other->usingDefault; + usingLog = other->usingLog; + orientation = other->orientation; + rotationAngle = other->rotationAngle; + dragging = false; + lastX = 0.0; + lastY = 0.0; + callback = other->callback; alwaysRepaint = other->alwaysRepaint; isImgVertical = other->isImgVertical; imgLayerWidth = other->imgLayerWidth; @@ -476,7 +476,7 @@ bool ImageBaseKnob::onMotion(const MotionEvent& ev) if (pData->orientation == ImageBaseKnob::Horizontal) { - if (const int movX = ev.pos.getX() - pData->lastX) + if (const double movX = ev.pos.getX() - pData->lastX) { d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movX)); @@ -485,7 +485,7 @@ bool ImageBaseKnob::onMotion(const MotionEvent& ev) } else if (pData->orientation == ImageBaseKnob::Vertical) { - if (const int movY = pData->lastY - ev.pos.getY()) + if (const double movY = pData->lastY - ev.pos.getY()) { d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movY)); @@ -529,7 +529,8 @@ bool ImageBaseKnob::onScroll(const ScrollEvent& ev) return false; const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - float value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * 10.f * ev.delta.getY()); + float value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + + ((pData->maximum - pData->minimum) / d * 10.f * static_cast(ev.delta.getY())); if (pData->usingLog) value = pData->logscale(value); @@ -569,8 +570,8 @@ struct ImageBaseSlider::PrivateData { bool dragging; bool inverted; bool valueIsSet; - int startedX; - int startedY; + double startedX; + double startedY; Callback* callback; @@ -590,8 +591,8 @@ struct ImageBaseSlider::PrivateData { dragging(false), inverted(false), valueIsSet(false), - startedX(0), - startedY(0), + startedX(0.0), + startedY(0.0), callback(nullptr), startPos(), endPos(), @@ -814,8 +815,8 @@ bool ImageBaseSlider::onMouse(const MouseEvent& ev) } float vper; - const int x = ev.pos.getX(); - const int y = ev.pos.getY(); + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); if (pData->startPos.getY() == pData->endPos.getY()) { @@ -880,8 +881,8 @@ bool ImageBaseSlider::onMotion(const MotionEvent& ev) return false; const bool horizontal = pData->startPos.getY() == pData->endPos.getY(); - const int x = ev.pos.getX(); - const int y = ev.pos.getY(); + const double x = ev.pos.getX(); + const double y = ev.pos.getY(); if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal)) { diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index ad179532..5af0588a 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -60,7 +60,7 @@ void Line::draw(const GraphicsContext&, const T width) { DISTRHO_SAFE_ASSERT_RETURN(width != 0,); - glLineWidth(width); + glLineWidth(static_cast(width)); drawLine(posStart, posEnd); } @@ -120,7 +120,7 @@ void Circle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - glLineWidth(lineWidth); + glLineWidth(static_cast(lineWidth)); drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); } @@ -177,7 +177,7 @@ void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - glLineWidth(lineWidth); + glLineWidth(static_cast(lineWidth)); drawTriangle(pos1, pos2, pos3, true); } @@ -244,7 +244,7 @@ void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - glLineWidth(lineWidth); + glLineWidth(static_cast(lineWidth)); drawRectangle(*this, true); } @@ -348,8 +348,8 @@ OpenGLImage::OpenGLImage() DISTRHO_SAFE_ASSERT(textureId != 0); } -OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) - : ImageBase(rawData, width, height, format), +OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) + : ImageBase(rdata, w, h, fmt), textureId(0), setupCalled(false) { @@ -357,8 +357,8 @@ OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint DISTRHO_SAFE_ASSERT(textureId != 0); } -OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const ImageFormat format) - : ImageBase(rawData, size, format), +OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const ImageFormat fmt) + : ImageBase(rdata, s, fmt), textureId(0), setupCalled(false) { @@ -402,8 +402,8 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept } // deprecated calls -OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint height, const GLenum format) - : ImageBase(rawData, width, height, asDISTRHOImageFormat(format)), +OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) + : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), textureId(0), setupCalled(false) { @@ -411,8 +411,8 @@ OpenGLImage::OpenGLImage(const char* const rawData, const uint width, const uint DISTRHO_SAFE_ASSERT(textureId != 0); } -OpenGLImage::OpenGLImage(const char* const rawData, const Size& size, const GLenum format) - : ImageBase(rawData, size, asDISTRHOImageFormat(format)), +OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const GLenum fmt) + : ImageBase(rdata, s, asDISTRHOImageFormat(fmt)), textureId(0), setupCalled(false) { @@ -568,31 +568,33 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const { // full viewport size glViewport(0, - -(height * autoScaleFactor - height), - width * autoScaleFactor, - height * autoScaleFactor); + -static_cast(height * autoScaleFactor - height + 0.5), + static_cast(width * autoScaleFactor + 0.5), + static_cast(height * autoScaleFactor + 0.5)); } else if (needsViewportScaling) { // limit viewport to widget bounds glViewport(absolutePos.getX(), - height - self->getHeight() - absolutePos.getY(), - self->getWidth(), - self->getHeight()); + static_cast(height - self->getHeight()) - absolutePos.getY(), + static_cast(self->getWidth()), + static_cast(self->getHeight())); } else { // set viewport pos - glViewport(absolutePos.getX() * autoScaleFactor, - -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), - std::round(width * autoScaleFactor), - std::round(height * autoScaleFactor)); + glViewport(static_cast(absolutePos.getX() * autoScaleFactor + 0.5), + -static_cast(std::round((height * autoScaleFactor - height) + + (absolutePos.getY() * autoScaleFactor))), + static_cast(std::round(width * autoScaleFactor)), + static_cast(std::round(height * autoScaleFactor))); // then cut the outer bounds - glScissor(absolutePos.getX() * autoScaleFactor, - height - std::round((self->getHeight() + absolutePos.getY()) * autoScaleFactor), - std::round(self->getWidth() * autoScaleFactor), - std::round(self->getHeight() * autoScaleFactor)); + glScissor(static_cast(absolutePos.getX() * autoScaleFactor + 0.5), + static_cast(height - std::round((static_cast(self->getHeight()) + absolutePos.getY()) + * autoScaleFactor)), + static_cast(std::round(self->getWidth() * autoScaleFactor)), + static_cast(std::round(self->getHeight() * autoScaleFactor))); glEnable(GL_SCISSOR_TEST); needsDisableScissor = true; @@ -622,9 +624,16 @@ void TopLevelWidget::PrivateData::display() // full viewport size if (window.pData->autoScaling) - glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor); + { + glViewport(0, + -static_cast(height * autoScaleFactor - height), + static_cast(width * autoScaleFactor), + static_cast(height * autoScaleFactor)); + } else - glViewport(0, 0, width, height); + { + glViewport(0, 0, static_cast(width), static_cast(height)); + } // main widget drawing self->onDisplay(); diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index b22360ca..3e0ef59d 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -64,8 +64,8 @@ Rectangle SubWidget::getAbsoluteArea() const noexcept Rectangle SubWidget::getConstrainedAbsoluteArea() const noexcept { - return Rectangle(std::max(0, getAbsoluteX()), - std::max(0, getAbsoluteY()), + return Rectangle(static_cast(std::max(0, getAbsoluteX())), + static_cast(std::max(0, getAbsoluteY())), getSize()); } diff --git a/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp index 5f15d4b2..d18b3c32 100644 --- a/dgl/src/Vulkan.cpp +++ b/dgl/src/Vulkan.cpp @@ -170,11 +170,11 @@ template class Rectangle; VulkanImage::VulkanImage() : ImageBase() {} -VulkanImage::VulkanImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) - : ImageBase(rawData, width, height, format) {} +VulkanImage::VulkanImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) + : ImageBase(rdata, w, h, fmt) {} -VulkanImage::VulkanImage(const char* const rawData, const Size& size, const ImageFormat format) - : ImageBase(rawData, size, format) {} +VulkanImage::VulkanImage(const char* const rdata, const Size& s, const ImageFormat fmt) + : ImageBase(rdata, s, fmt) {} VulkanImage::VulkanImage(const VulkanImage& image) : ImageBase(image.rawData, image.size, image.format) {} diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 66a02ef4..9866d606 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -93,18 +93,25 @@ void Window::setResizable(const bool resizable) uint Window::getWidth() const noexcept { - return puglGetFrame(pData->view).width; + const double width = puglGetFrame(pData->view).width; + DISTRHO_SAFE_ASSERT_RETURN(width >= 0.0, 0); + return static_cast(width + 0.5); } uint Window::getHeight() const noexcept { - return puglGetFrame(pData->view).height; + const double height = puglGetFrame(pData->view).height; + DISTRHO_SAFE_ASSERT_RETURN(height >= 0.0, 0); + return static_cast(height + 0.5); } Size Window::getSize() const noexcept { const PuglRect rect = puglGetFrame(pData->view); - return Size(rect.width, rect.height); + DISTRHO_SAFE_ASSERT_RETURN(rect.width >= 0.0, Size()); + DISTRHO_SAFE_ASSERT_RETURN(rect.height >= 0.0, Size()); + return Size(static_cast(rect.width + 0.5), + static_cast(rect.height + 0.5)); } void Window::setWidth(const uint width) @@ -123,7 +130,7 @@ void Window::setSize(const uint width, const uint height) // FIXME add default and min props for this if (pData->minWidth == 0 && pData->minHeight == 0) - puglSetDefaultSize(pData->view, width, height); + puglSetDefaultSize(pData->view, static_cast(width), static_cast(height)); puglSetWindowSize(pData->view, width, height); } @@ -237,16 +244,16 @@ void Window::setGeometryConstraints(const uint minimumWidth, const double scaleFactor = pData->scaleFactor; puglSetGeometryConstraints(pData->view, - minimumWidth * scaleFactor, - minimumHeight * scaleFactor, + static_cast(minimumWidth * scaleFactor + 0.5), + static_cast(minimumHeight * scaleFactor + 0.5), keepAspectRatio); if (scaleFactor != 1.0) { const Size size(getSize()); - setSize(size.getWidth() * scaleFactor, - size.getHeight() * scaleFactor); + setSize(static_cast(size.getWidth() * scaleFactor + 0.5), + static_cast(size.getHeight() * scaleFactor + 0.5)); } } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 302d3ac4..e4dd6e4c 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -146,7 +146,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, { if (isEmbed) { - puglSetDefaultSize(view, width, height); + puglSetDefaultSize(view, static_cast(width), static_cast(height)); puglSetParentWindow(view, parentWindowHandle); } @@ -246,8 +246,8 @@ void Window::PrivateData::show() // FIXME PuglRect rect = puglGetFrame(view); - puglSetDefaultSize(view, rect.width, rect.height); - puglSetWindowSize(view, rect.width, rect.height); + puglSetDefaultSize(view, static_cast(rect.width), static_cast(rect.height)); + puglSetWindowSize(view, static_cast(rect.width), static_cast(rect.height)); #ifdef DISTRHO_OS_WINDOWS puglWin32ShowWindowCentered(view); @@ -395,7 +395,7 @@ void Window::PrivateData::startModal() // FIXME? PuglRect rect = puglGetFrame(view); - puglSetDefaultSize(view, rect.width, rect.height); + puglSetDefaultSize(view, static_cast(rect.width), static_cast(rect.height)); // make sure both parent and ourselves are visible modal.parent->show(); @@ -464,7 +464,7 @@ void Window::PrivateData::runAsModal(const bool blockWait) // ----------------------------------------------------------------------- // pugl events -void Window::PrivateData::onPuglConfigure(const int width, const int height) +void Window::PrivateData::onPuglConfigure(const double width, const double height) { DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); @@ -472,16 +472,18 @@ void Window::PrivateData::onPuglConfigure(const int width, const int height) if (autoScaling) { - const double scaleHorizontal = static_cast(width) / static_cast(minWidth); - const double scaleVertical = static_cast(height) / static_cast(minHeight); + const double scaleHorizontal = width / static_cast(minWidth); + const double scaleVertical = height / static_cast(minHeight); autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical; } - self->onReshape(width, height); + const uint uwidth = static_cast(width + 0.5); + const uint uheight = static_cast(height + 0.5); + self->onReshape(uwidth, uheight); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) - topLevelWidget->setSize(width, height); + topLevelWidget->setSize(uwidth, uheight); #endif // always repaint after a resize diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 47dbf9ab..934a6222 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -91,6 +91,9 @@ struct Window::PrivateData : IdleCallback { { DISTRHO_SAFE_ASSERT(! enabled); } + + DISTRHO_DECLARE_NON_COPYABLE(Modal) + DISTRHO_PREVENT_HEAP_ALLOCATION } modal; /** Constructor for a regular, standalone window. */ @@ -144,7 +147,7 @@ struct Window::PrivateData : IdleCallback { void runAsModal(bool blockWait); // pugl events - void onPuglConfigure(int width, int height); + void onPuglConfigure(double width, double height); void onPuglExpose(); void onPuglClose(); void onPuglFocus(bool focus, CrossingMode mode); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index d7dac469..7372b830 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -194,14 +194,14 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect) { - view->minWidth = width; - view->minHeight = height; + view->minWidth = (int)width; + view->minHeight = (int)height; if (aspect) { - view->minAspectX = width; - view->minAspectY = height; - view->maxAspectX = width; - view->maxAspectY = height; + view->minAspectX = (int)width; + view->minAspectY = (int)height; + view->maxAspectX = (int)width; + view->maxAspectY = (int)height; } #if defined(DISTRHO_OS_HAIKU) From d61d547e2124fc5d87702683850e10935c2ac70d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 19:32:33 +0100 Subject: [PATCH 139/159] Special handling for macOS compatiblity --- Makefile.base.mk | 11 ++++++++++- Makefile.plugins.mk | 32 ++++++++++++++++++++++++++------ dgl/Makefile | 4 ++++ dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 21 +++++++++++++++------ dgl/src/pugl.hpp | 17 +++++++++++++---- distrho/DistrhoUI_macOS.mm | 33 +++++++++++++++++++++++++++++++++ tests/Makefile | 4 ++++ 8 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 distrho/DistrhoUI_macOS.mm diff --git a/Makefile.base.mk b/Makefile.base.mk index 59433f83..740c24b2 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -63,6 +63,10 @@ ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) CPU_ARM=true CPU_ARM_OR_AARCH64=true endif +ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) +CPU_ARM64=true +CPU_ARM_OR_AARCH64=true +endif ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) CPU_AARCH64=true CPU_ARM_OR_AARCH64=true @@ -136,12 +140,17 @@ BASE_OPTS += -mtune=generic -msse -msse2 endif ifeq ($(CPU_ARM),true) +ifneq ($(CPU_ARM64),true) BASE_OPTS += -mfpu=neon-vfpv4 -mfloat-abi=hard endif +endif ifeq ($(MACOS),true) # MacOS linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs +ifneq ($(SKIP_STRIPPING),true) +LINK_OPTS += -Wl,-x +endif else # Common linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed @@ -247,7 +256,7 @@ DGL_SYSTEM_LIBS += -lbe endif ifeq ($(MACOS),true) -DGL_SYSTEM_LIBS += -framework Cocoa +DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo endif ifeq ($(WINDOWS),true) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 8e4e9fa3..e7ff2dc9 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -55,6 +55,10 @@ endif OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) +ifeq ($(MACOS),true) +OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o +endif + # --------------------------------------------------------------------------------------------------------------------- # Set plugin binary file targets @@ -67,6 +71,17 @@ lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) +# --------------------------------------------------------------------------------------------------------------------- +# Set plugin symbols to export + +ifeq ($(MACOS),true) +SYMBOLS_LADSPA = -Wl,-exported_symbol,_ladspa_descriptor +SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_dssi_descriptor +SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl +SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor +SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain +endif + # --------------------------------------------------------------------------------------------------------------------- # Handle UI stuff, disable UI support automatically @@ -189,6 +204,11 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp @echo "Compiling DistrhoUIMain.cpp ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ +$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm + -@mkdir -p $(BUILD_DIR) + @echo "Compiling DistrhoUI_macOS.mm ($*)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$* -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@ + $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp (JACK)" @@ -221,7 +241,7 @@ ladspa: $(ladspa_dsp) $(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating LADSPA plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@ # --------------------------------------------------------------------------------------------------------------------- # DSSI @@ -233,7 +253,7 @@ dssi_ui: $(dssi_ui) $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating DSSI plugin library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@ $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @@ -250,17 +270,17 @@ lv2_sep: $(lv2_dsp) $(lv2_ui) $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) $(SYMBOLS_LV2UI) -o $@ $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2) -o $@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin UI for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ # --------------------------------------------------------------------------------------------------------------------- # VST @@ -274,7 +294,7 @@ $(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/Makefile b/dgl/Makefile index 041e1d03..170a694c 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -16,6 +16,10 @@ LINK_FLAGS += $(DGL_LIBS) # TODO fix these after pugl-upstream is done BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -Wno-narrowing +ifeq ($(MACOS),true) +BUILD_CXX_FLAGS += -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations +endif + # ifneq ($(MACOS_OLD),true) # needed by sofd right now, fix later # BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 13cd61b4..40915321 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 13cd61b4915d3314cc8e43e12a3baccef6650c94 +Subproject commit 4091532180ee74ebedbb7690c225a363c0ebe142 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 7372b830..ac2013d2 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -89,18 +89,21 @@ # endif #endif +#ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL +#endif // -------------------------------------------------------------------------------------------------------------------- -#define PUGL_DISABLE_DEPRECATED - #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) -/* -# define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) -# define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) -*/ +# ifndef DISTRHO_MACOS_NAMESPACE_MACRO +# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE +# define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) +# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView) +# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView) +# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow) +# endif # import "pugl-upstream/src/mac.m" # import "pugl-upstream/src/mac_stub.m" # ifdef DGL_CAIRO @@ -207,6 +210,7 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co #if defined(DISTRHO_OS_HAIKU) // nothing? #elif defined(DISTRHO_OS_MAC) + /* if (view->impl->window) { [view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; @@ -214,6 +218,9 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co if (aspect) [view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)]; } + */ + puglSetMinSize(view, width, height); + puglSetAspectRatio(view, width, height, width, height); #elif defined(DISTRHO_OS_WINDOWS) // nothing #else @@ -352,4 +359,6 @@ void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) // -------------------------------------------------------------------------------------------------------------------- +#ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL +#endif diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 7fdde137..6bee1687 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -24,15 +24,22 @@ #include #include -START_NAMESPACE_DGL - -// -------------------------------------------------------------------------------------------------------------------- - #define PUGL_API #define PUGL_DISABLE_DEPRECATED #define PUGL_NO_INCLUDE_GLU_H + +// -------------------------------------------------------------------------------------------------------------------- + +#ifndef DISTRHO_OS_MAC +START_NAMESPACE_DGL +#else +USE_NAMESPACE_DGL +#endif + #include "pugl-upstream/include/pugl/pugl.h" +// -------------------------------------------------------------------------------------------------------------------- + PUGL_BEGIN_DECLS // expose backend enter @@ -85,6 +92,8 @@ PUGL_END_DECLS // -------------------------------------------------------------------------------------------------------------------- +#ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL +#endif #endif // DGL_PUGL_HPP_INCLUDED diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm new file mode 100644 index 00000000..2cdbe151 --- /dev/null +++ b/distrho/DistrhoUI_macOS.mm @@ -0,0 +1,33 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 PUGL_NAMESPACE +# error PUGL_NAMESPACE must be set when compiling this file +#endif + +#include "src/DistrhoPluginChecks.h" +#include "../dgl/Base.hpp" + +#define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE +#define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE) + +#define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView) +#define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView) +#define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window) +#define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate) +#define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView) + +#import "src/pugl.mm" diff --git a/tests/Makefile b/tests/Makefile index 66d5f84e..1cf83350 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -15,6 +15,10 @@ LINK_FLAGS += -lpthread # TODO fix within pugl BUILD_CXX_FLAGS += -Wno-extra -Wno-missing-field-initializers +ifeq ($(MACOS),true) +BUILD_CXX_FLAGS += -ObjC++ -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations +endif + # --------------------------------------------------------------------------------------------------------------------- MANUAL_TESTS = From 84bfd0d6a5809cd6664dbb304f5405de0561c09d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 19:37:53 +0100 Subject: [PATCH 140/159] Update pugl submodule --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 40915321..ecfa8171 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 4091532180ee74ebedbb7690c225a363c0ebe142 +Subproject commit ecfa817136831ca2fe38561a68344f9e9076d3e7 From 2daf92818bf057bb09d9a69201b8562a4e65d717 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 22 May 2021 19:46:45 +0100 Subject: [PATCH 141/159] More backwards compat fixes --- dgl/ImageWidgets.hpp | 2 +- dgl/src/Cairo.cpp | 10 +++------- dgl/src/OpenGL.cpp | 10 +++------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index aa85fe6f..bf42ad3e 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -17,7 +17,7 @@ #ifndef DGL_IMAGE_WIDGETS_HPP_INCLUDED #define DGL_IMAGE_WIDGETS_HPP_INCLUDED -#include "OpenGL.hpp" +#include "Image.hpp" START_NAMESPACE_DGL diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index c505099f..91107b57 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -25,6 +25,9 @@ #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" +// templated classes +#include "ImageBaseWidgets.cpp" + START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -799,10 +802,3 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept // ----------------------------------------------------------------------- END_NAMESPACE_DGL - -// ----------------------------------------------------------------------- -// templated classes - -#include "ImageBaseWidgets.cpp" - -// ----------------------------------------------------------------------- diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 5af0588a..dbd8428e 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -24,6 +24,9 @@ #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" +// templated classes +#include "ImageBaseWidgets.cpp" + START_NAMESPACE_DGL // ----------------------------------------------------------------------- @@ -652,10 +655,3 @@ const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept // ----------------------------------------------------------------------- END_NAMESPACE_DGL - -// ----------------------------------------------------------------------- -// templated classes - -#include "ImageBaseWidgets.cpp" - -// ----------------------------------------------------------------------- From 2d1ff33d8180bae75e7d931e8e5a9a74ca06bab0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 12:19:16 +0100 Subject: [PATCH 142/159] Less annoying backwards compatibility Signed-off-by: falkTX --- dgl/Image.hpp | 9 ++++++++- dgl/ImageWidgets.hpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/dgl/Image.hpp b/dgl/Image.hpp index f6489bdf..2e515361 100644 --- a/dgl/Image.hpp +++ b/dgl/Image.hpp @@ -17,12 +17,19 @@ #ifndef DGL_IMAGE_HPP_INCLUDED #define DGL_IMAGE_HPP_INCLUDED +#ifdef DGL_CAIRO +#include "Cairo.hpp" +#else #include "OpenGL.hpp" +#endif START_NAMESPACE_DGL -DISTRHO_DEPRECATED_BY("OpenGLImage") +#ifdef DGL_CAIRO +typedef CairoImage Image; +#else typedef OpenGLImage Image; +#endif END_NAMESPACE_DGL diff --git a/dgl/ImageWidgets.hpp b/dgl/ImageWidgets.hpp index bf42ad3e..992b6e37 100644 --- a/dgl/ImageWidgets.hpp +++ b/dgl/ImageWidgets.hpp @@ -18,23 +18,23 @@ #define DGL_IMAGE_WIDGETS_HPP_INCLUDED #include "Image.hpp" +#include "ImageBaseWidgets.hpp" START_NAMESPACE_DGL -DISTRHO_DEPRECATED_BY("OpenGLImageAboutWindow") +#ifdef DGL_CAIRO +typedef CairoImageAboutWindow ImageAboutWindow; +typedef CairoImageButton ImageButton; +typedef CairoImageKnob ImageKnob; +typedef CairoImageSlider ImageSlider; +typedef CairoImageSwitch ImageSwitch; +#else typedef OpenGLImageAboutWindow ImageAboutWindow; - -DISTRHO_DEPRECATED_BY("OpenGLImageButton") typedef OpenGLImageButton ImageButton; - -DISTRHO_DEPRECATED_BY("OpenGLImageKnob") typedef OpenGLImageKnob ImageKnob; - -DISTRHO_DEPRECATED_BY("OpenGLImageSlider") typedef OpenGLImageSlider ImageSlider; - -DISTRHO_DEPRECATED_BY("OpenGLImageSwitch") typedef OpenGLImageSwitch ImageSwitch; +#endif END_NAMESPACE_DGL From 97a8a19b856264a10aee7070d20f293d3b30a2c3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 12:28:12 +0100 Subject: [PATCH 143/159] Correct some deprecated comments Signed-off-by: falkTX --- dgl/OpenGL.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index deb677fc..9328e5c1 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -234,16 +234,16 @@ public: @note @a rawData must remain valid for the lifetime of this Image. DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. */ - DISTRHO_DEPRECATED_BY("OpenGLImage(const char*,uint,uint,ImageFormat") - explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum format); + DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, uint, uint, ImageFormat)") + explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum glFormat); /** Constructor using raw image data, specifying an OpenGL image format. @note @a rawData must remain valid for the lifetime of this Image. DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one. */ - DISTRHO_DEPRECATED_BY("OpenGLImage(const char*,const Size&,ImageFormat") - explicit OpenGLImage(const char* rawData, const Size& size, GLenum format); + DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, const Size&, ImageFormat)") + explicit OpenGLImage(const char* rawData, const Size& size, GLenum glFormat); /** Draw this image at (0, 0) point using the current OpenGL context. @@ -256,14 +256,14 @@ public: Draw this image at (x, y) point using the current OpenGL context. DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. */ - DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,int,int)") - void drawAt(const int x, const int y); + DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, int, int)") + void drawAt(int x, int y); /** Draw this image at position @a pos using the current OpenGL context. DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL. */ - DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&,const Point&)") + DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, const Point&)") void drawAt(const Point& pos); /** From 20f7322c71c1e036518ff27aba358bf4ed744414 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 12:30:09 +0100 Subject: [PATCH 144/159] Remove useless ImageBaseAboutWindow::onReshape Signed-off-by: falkTX --- dgl/ImageBaseWidgets.hpp | 3 --- dgl/src/ImageBaseWidgets.cpp | 8 -------- 2 files changed, 11 deletions(-) diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 20682aec..bbc34b6d 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -38,9 +38,6 @@ protected: bool onKeyboard(const KeyboardEvent&) override; bool onMouse(const MouseEvent&) override; - // FIXME needed? - void onReshape(uint width, uint height) override; - private: ImageType img; diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 2d056446..976887d3 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -86,14 +86,6 @@ bool ImageBaseAboutWindow::onMouse(const MouseEvent& ev) return false; } -template -void ImageBaseAboutWindow::onReshape(uint width, uint height) -{ - // FIXME needed? - TopLevelWidget::setSize(width, height); - StandaloneWindow::onReshape(width, height); -} - // -------------------------------------------------------------------------------------------------------------------- template From d36f61a78a99694cb8a8d58ae0c0cbbdf503c605 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 15:42:34 +0100 Subject: [PATCH 145/159] Start to bring back file dialog, add a few more comments Signed-off-by: falkTX --- Makefile.base.mk | 2 +- dgl/Window.hpp | 149 ++++++++++++++++++----------- dgl/src/Window.cpp | 50 +++------- dgl/src/WindowPrivateData.cpp | 153 ++++++++++++++++++++++++------ dgl/src/WindowPrivateData.hpp | 21 ++-- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 94 ++++++++++++++++++ dgl/src/pugl.hpp | 22 +++++ dgl/src/sofd/libsofd.c | 35 ++++--- dgl/src/sofd/libsofd.h | 23 ++++- distrho/DistrhoUI.hpp | 2 +- distrho/src/DistrhoUI.cpp | 3 + distrho/src/DistrhoUIInternal.hpp | 6 +- 13 files changed, 409 insertions(+), 153 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 740c24b2..62dea1a1 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -265,7 +265,7 @@ endif ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) ifeq ($(HAVE_X11),true) -DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) ifeq ($(HAVE_XCURSOR),true) # TODO -DHAVE_XCURSOR diff --git a/dgl/Window.hpp b/dgl/Window.hpp index d4d109bb..46d7c1be 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -50,6 +50,60 @@ class TopLevelWidget; class Window { public: +#ifndef DGL_FILE_BROWSER_DISABLED + /** + File browser options. + @see Window::openFileBrowser + */ + struct FileBrowserOptions { + /** + File browser button state. + This allows to customize the behaviour of the file browse dialog buttons. + */ + enum ButtonState { + kButtonInvisible, + kButtonVisibleUnchecked, + kButtonVisibleChecked, + }; + + /** Start directory, uses current working directory if null */ + const char* startDir; + /** File browser dialog window title, uses "FileBrowser" if null */ + const char* title; + /** File browser dialog window width */ + uint width; + /** File browser dialog window height */ + uint height; + // TODO file filter + + /** + File browser buttons. + */ + struct Buttons { + /** Whether to list all files vs only those with matching file extension */ + ButtonState listAllFiles; + /** Whether to show hidden files */ + ButtonState showHidden; + /** Whether to show list of places (bookmarks) */ + ButtonState showPlaces; + + /** Constuctor for default values */ + Buttons() + : listAllFiles(kButtonVisibleChecked), + showHidden(kButtonVisibleUnchecked), + showPlaces(kButtonVisibleUnchecked) {} + } buttons; + + /** Constuctor for default values */ + FileBrowserOptions() + : startDir(nullptr), + title(nullptr), + width(0), + height(0), + buttons() {} + }; +#endif // DGL_FILE_BROWSER_DISABLED + /** Constructor for a regular, standalone window. */ @@ -130,7 +184,18 @@ public: */ void close(); + /** + Check if this window is resizable. + @see setResizable + */ bool isResizable() const noexcept; + + /** + Set window as resizable (by the user or window manager). + It is always possible to resize a window programmatically, which is not the same as the user being allowed to it. + @note This function does nothing for plugins, where the resizable state is set via macro. + @see DISTRHO_UI_USER_RESIZABLE + */ void setResizable(bool resizable); /** @@ -247,6 +312,17 @@ public: */ void focus(); +#ifndef DGL_FILE_BROWSER_DISABLED + /** + Open a file browser dialog with this window as parent. + A few options can be specified to setup the dialog. + + This function does not block. + If a path is selected, onFileSelected() will be called with the user chosen path. + */ + bool openFileBrowser(const FileBrowserOptions& options); +#endif + /** Request repaint of this window, for the entire area. */ @@ -272,16 +348,15 @@ public: bool keepAspectRatio = false, bool automaticallyScale = false); - /* - void setTransientWinId(uintptr_t winId); - */ - + /** DEPRECATED Use isIgnoringKeyRepeat(). */ DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } + /** DEPRECATED Use getScaleFactor(). */ DISTRHO_DEPRECATED_BY("getScaleFactor()") inline double getScaling() const noexcept { return getScaleFactor(); } + /** DEPRECATED Use runAsModal(bool). */ DISTRHO_DEPRECATED_BY("runAsModal(bool)") inline void exec(bool blockWait = false) { runAsModal(blockWait); } @@ -302,9 +377,23 @@ protected: /** A function called when the window is resized. If there is a top-level widget associated with this window, its size will be set right after this function. + The default implementation sets up drawing context where necessary. */ virtual void onReshape(uint width, uint height); +#ifndef DGL_FILE_BROWSER_DISABLED + /** + A function called when a path is selected by the user, as triggered by openFileBrowser(). + This action happens after the user confirms the action, so the file browser dialog will be closed at this point. + The default implementation does nothing. + */ + virtual void onFileSelected(const char* filename); + + /** DEPRECATED Use onFileSelected(). */ + DISTRHO_DEPRECATED_BY("onFileSelected(const char*)") + inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); } +#endif + private: struct PrivateData; PrivateData* const pData; @@ -319,62 +408,10 @@ private: END_NAMESPACE_DGL /* TODO - * add focusEvent with CrossingMode arg * add eventcrossing/enter-leave event */ - #if 0 -#ifndef DGL_FILE_BROWSER_DISABLED - /** - File browser options. - */ - struct FileBrowserOptions { - const char* startDir; - const char* title; - uint width; - uint height; - - /** - File browser buttons. - - 0 means hidden. - 1 means visible and unchecked. - 2 means visible and checked. - */ - struct Buttons { - uint listAllFiles; - uint showHidden; - uint showPlaces; - - /** Constuctor for default values */ - Buttons() - : listAllFiles(2), - showHidden(1), - showPlaces(1) {} - } buttons; - - /** Constuctor for default values */ - FileBrowserOptions() - : startDir(nullptr), - title(nullptr), - width(0), - height(0), - buttons() {} - }; -#endif // DGL_FILE_BROWSER_DISABLED - - void addIdleCallback(IdleCallback* const callback); - void removeIdleCallback(IdleCallback* const callback); - -#ifndef DGL_FILE_BROWSER_DISABLED - bool openFileBrowser(const FileBrowserOptions& options); -#endif - protected: -#ifndef DGL_FILE_BROWSER_DISABLED - virtual void fileBrowserSelected(const char* filename); -#endif - bool handlePluginKeyboard(const bool press, const uint key); bool handlePluginSpecial(const bool press, const Key key); #endif diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 9866d606..27774c2c 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -201,6 +201,13 @@ void Window::focus() pData->focus(); } +#ifndef DGL_FILE_BROWSER_DISABLED +bool Window::openFileBrowser(const FileBrowserOptions& options) +{ + return pData->openFileBrowser(options); +} +#endif + void Window::repaint() noexcept { puglPostRedisplay(pData->view); @@ -271,51 +278,20 @@ void Window::onReshape(uint, uint) puglFallbackOnResize(pData->view); } -#if 0 -void Window::setTransientWinId(const uintptr_t winId) -{ - puglSetTransientFor(pData->fView, winId); -} - -void Window::_addWidget(Widget* const widget) -{ - pData->addWidget(widget); -} - -void Window::_removeWidget(Widget* const widget) -{ - pData->removeWidget(widget); -} - -void Window::_idle() -{ - pData->windowSpecificIdle(); -} - -// ----------------------------------------------------------------------- - -void Window::addIdleCallback(IdleCallback* const callback) +#ifndef DGL_FILE_BROWSER_DISABLED +void Window::onFileSelected(const char*) { - DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) - - pData->fAppData->idleCallbacks.push_back(callback); } +#endif -void Window::removeIdleCallback(IdleCallback* const callback) +#if 0 +void Window::setTransientWinId(const uintptr_t winId) { - DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) - - pData->fAppData->idleCallbacks.remove(callback); + puglSetTransientFor(pData->view, winId); } // ----------------------------------------------------------------------- -#ifndef DGL_FILE_BROWSER_DISABLED -void Window::fileBrowserSelected(const char*) -{ -} -#endif - bool Window::handlePluginKeyboard(const bool press, const uint key) { // TODO diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index e4dd6e4c..0377915d 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -19,6 +19,14 @@ #include "pugl.hpp" +#include "../../distrho/extra/String.hpp" + +#ifdef DISTRHO_OS_WINDOWS +# include +#else +# include +#endif + #define DGL_DEBUG_EVENTS #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) @@ -66,6 +74,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaleFactor(1.0), minWidth(0), minHeight(0), +#ifdef DISTRHO_OS_WINDOWS + win32SelectedFile(nullptr), +#endif modal() { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -85,6 +96,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c autoScaleFactor(1.0), minWidth(0), minHeight(0), +#ifdef DISTRHO_OS_WINDOWS + win32SelectedFile(nullptr), +#endif modal(ppData) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); @@ -108,6 +122,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), +#ifdef DISTRHO_OS_WINDOWS + win32SelectedFile(nullptr), +#endif modal() { if (isEmbed) @@ -142,6 +159,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), +#ifdef DISTRHO_OS_WINDOWS + win32SelectedFile(nullptr), +#endif modal() { if (isEmbed) @@ -163,6 +183,9 @@ Window::PrivateData::~PrivateData() { if (isEmbed) { +#ifdef HAVE_X11 + sofdFileDialogClose(view); +#endif puglHide(view); appData->oneWindowClosed(); isClosed = true; @@ -199,9 +222,6 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r puglSetViewHint(view, PUGL_STENCIL_BITS, 8); // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); -// #ifndef DGL_FILE_BROWSER_DISABLED -// puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); -// #endif PuglRect rect = puglGetFrame(view); rect.width = width; @@ -215,6 +235,21 @@ void Window::PrivateData::init(const uint width, const uint height, const bool r // ----------------------------------------------------------------------- +void Window::PrivateData::close() +{ + DGL_DBG("Window close\n"); + // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData); + + if (isEmbed || isClosed) + return; + + isClosed = true; + hide(); + appData->oneWindowClosed(); +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::show() { if (isVisible) @@ -293,6 +328,9 @@ void Window::PrivateData::hide() if (modal.enabled) stopModal(); +#ifdef HAVE_X11 + sofdFileDialogClose(view); +#endif puglHide(view); isVisible = false; @@ -300,21 +338,6 @@ void Window::PrivateData::hide() // ----------------------------------------------------------------------- -void Window::PrivateData::close() -{ - DGL_DBG("Window close\n"); - // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData); - - if (isEmbed || isClosed) - return; - - isClosed = true; - hide(); - appData->oneWindowClosed(); -} - -// ----------------------------------------------------------------------- - void Window::PrivateData::focus() { if (! isEmbed) @@ -341,20 +364,29 @@ void Window::PrivateData::setResizable(const bool resizable) void Window::PrivateData::idleCallback() { -// #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 (modal.enabled && modal.parent != nullptr) -// modal.parent->idleCallback(); +#ifndef DGL_FILE_BROWSER_DISABLED +# ifdef DISTRHO_OS_WINDOWS + if (char* const path = win32SelectedFile) + { + win32SelectedFile = nullptr; + self->onFileSelected(path); + std::free(path); + } +# endif +# ifdef HAVE_X11 + char* path; + if (sofdFileDialogGetPath(&path)) + { + // TODO ignore null path?? + self->onFileSelected(path); + sofdFileDialogFree(path); + } +# endif +#endif } // ----------------------------------------------------------------------- +// idle callback stuff bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { @@ -379,6 +411,69 @@ bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback) return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS; } +// ----------------------------------------------------------------------- +// file handling + +bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& options) +{ + using DISTRHO_NAMESPACE::String; + + // -------------------------------------------------------------------------- + // configure start dir + + // TODO: get abspath if needed + // TODO: cross-platform + + String startDir(options.startDir); + + if (startDir.isEmpty()) + { + // TESTING verify this whole thing... +#ifdef DISTRHO_OS_WINDOWS + if (char* const cwd = _getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#else + if (char* const cwd = getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#endif + } + + DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); + + if (! startDir.endsWith(DISTRHO_OS_SEP)) + startDir += DISTRHO_OS_SEP_STR; + + // -------------------------------------------------------------------------- + // configure title + + String title(options.title); + + if (title.isEmpty()) + { + title = puglGetWindowTitle(view); + + if (title.isEmpty()) + title = "FileBrowser"; + } + + // -------------------------------------------------------------------------- + // show + +#ifdef HAVE_X11 + uint flags = 0x0; + // TODO flags + return sofdFileDialogShow(view, startDir, title, flags, options.width, options.height); +#endif + + return false; +} + // ----------------------------------------------------------------------- // modal handling diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 934a6222..83b9d86a 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -30,7 +30,7 @@ class TopLevelWidget; // ----------------------------------------------------------------------- struct Window::PrivateData : IdleCallback { - /* Reference to the DGL Application class this (private data) window associates with. */ + /** Reference to the DGL Application class this (private data) window associates with. */ Application& app; /** Direct access to the DGL Application private data where we registers ourselves in. */ @@ -68,6 +68,11 @@ struct Window::PrivateData : IdleCallback { /** Pugl minWidth, minHeight access. */ uint minWidth, minHeight; +#ifdef DISTRHO_OS_WINDOWS + /** Selected file for openFileBrowser on windows, stored for fake async operation. */ + const char* win32SelectedFile; +#endif + /** Modal window setup. */ struct Modal { PrivateData* parent; // parent of this window (so we can become modal) @@ -102,9 +107,6 @@ struct Window::PrivateData : IdleCallback { /** Constructor for a modal window. */ explicit PrivateData(Application& app, Window* self, PrivateData* ppData); - /** Constructor for a regular, standalone window with a transient parent. */ -// explicit PrivateData(Application& app, Window* self, Window& transientWindow); - /** Constructor for an embed Window, with a few extra hints from the host side. */ explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); @@ -118,9 +120,6 @@ struct Window::PrivateData : IdleCallback { /** Helper initialization function called at the end of all this class constructors. */ void init(uint width, uint height, bool resizable); - void show(); - void hide(); - /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). * The application event-loop will stop when all windows have been closed. @@ -130,17 +129,23 @@ struct Window::PrivateData : IdleCallback { */ void close(); + void show(); + void hide(); + void focus(); void setResizable(bool resizable); const GraphicsContext& getGraphicsContext() const noexcept; + // idle callback stuff void idleCallback() override; - bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs); bool removeIdleCallback(IdleCallback* callback); + // file handling + bool openFileBrowser(const Window::FileBrowserOptions& options); + // modal handling void startModal(); void stopModal(); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index ecfa8171..83ccad73 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit ecfa817136831ca2fe38561a68344f9e9076d3e7 +Subproject commit 83ccad73546576a5b439c0fe2415e16509f8f28a diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index ac2013d2..3902a199 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -89,6 +89,12 @@ # endif #endif +#ifdef HAVE_X11 +# define 400 +# include "sofd/libsofd.h" +# include "sofd/libsofd.c" +#endif + #ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL #endif @@ -355,6 +361,94 @@ void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) : GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX; SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); } + +// -------------------------------------------------------------------------------------------------------------------- +#endif + +#ifdef HAVE_X11 +// -------------------------------------------------------------------------------------------------------------------- +// X11 specific, setup event loop filter for sofd file dialog + +static bool sofd_has_action; +static char* sofd_filename; + +static bool sofd_event_filter(Display* const display, XEvent* const xevent) +{ + if (x_fib_handle_events(display, xevent) == 0) + return false; + + if (sofd_filename != nullptr) + std::free(sofd_filename); + + if (x_fib_status() > 0) + sofd_filename = x_fib_filename(); + else + sofd_filename = nullptr; + + x_fib_close(display); + sofd_has_action = true; + return true; +} + +void sofdFileDialogSetup(PuglWorld* const world) +{ + puglX11SetEventFilter(world, sofd_event_filter); +} + +// -------------------------------------------------------------------------------------------------------------------- +// X11 specific, show file dialog via sofd + +bool sofdFileDialogShow(PuglView* const view, + const char* const startDir, const char* const title, + const uint flags, const uint width, const uint height) +{ + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); + + /* + 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); + */ + + PuglInternals* const impl = view->impl; + return (x_fib_show(impl->display, impl->win, width, height) == 0); +} + +// -------------------------------------------------------------------------------------------------------------------- +// X11 specific, close sofd file dialog + +void sofdFileDialogClose(PuglView* const view) +{ + PuglInternals* const impl = view->impl; + x_fib_close(impl->display); +} + +// -------------------------------------------------------------------------------------------------------------------- +// X11 specific, get path chosen via sofd file dialog + +bool sofdFileDialogGetPath(char** path) +{ + if (! sofd_has_action) + return false; + + sofd_has_action = false; + *path = sofd_filename; + return true; +} + +// -------------------------------------------------------------------------------------------------------------------- +// X11 specific, free path of sofd file dialog, no longer needed + +void sofdFileDialogFree(char* const path) +{ + DISTRHO_SAFE_ASSERT_RETURN(path == nullptr || path == sofd_filename,); + + std::free(sofd_filename); + sofd_filename = nullptr; +} + +// -------------------------------------------------------------------------------------------------------------------- #endif // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 6bee1687..f89e2847 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -88,6 +88,28 @@ PUGL_API void puglWin32SetWindowResizable(PuglView* view, bool resizable); #endif +#ifdef HAVE_X11 +// X11 specific, setup event loop filter for sofd file dialog +PUGL_API void +sofdFileDialogSetup(PuglWorld* world); + +// X11 specific, show file dialog via sofd +PUGL_API bool +sofdFileDialogShow(PuglView* view, const char* startDir, const char* title, uint flags, uint width, uint height); + +// X11 specific, close sofd file dialog +PUGL_API void +sofdFileDialogClose(PuglView* view); + +// X11 specific, get path chosen via sofd file dialog +PUGL_API bool +sofdFileDialogGetPath(char** path); + +// X11 specific, free path of sofd file dialog, no longer needed +PUGL_API void +sofdFileDialogFree(char* path); +#endif + PUGL_END_DECLS // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/sofd/libsofd.c b/dgl/src/sofd/libsofd.c index 06273d4c..288e37b4 100644 --- a/dgl/src/sofd/libsofd.c +++ b/dgl/src/sofd/libsofd.c @@ -32,7 +32,7 @@ */ #ifdef SOFD_TEST -#define SOFD_HAVE_X11 +#define HAVE_X11 #include "libsofd.h" #endif @@ -337,7 +337,7 @@ const char *x_fib_recent_file(const char *appname) { return NULL; } -#ifdef SOFD_HAVE_X11 +#ifdef HAVE_X11 #include #include @@ -475,7 +475,10 @@ static int (*_fib_filter_function)(const char *filename); #define FAREATEXTL (FAREAMRGL + TEXTSEP) //px; filename text-left FAREAMRGL + TEXTSEP #define SORTBTNOFF -10 //px; -#define DBLCLKTME 400 //msec; double click time +#ifndef DBLCLKTME +#define DBLCLKTME 200 //msec; double click time +#endif + #define DRAW_OUTLINE #define DOUBLE_BUFFER @@ -1208,7 +1211,7 @@ static int fib_dirlistadd (Display *dpy, const int i, const char* path, const ch static int fib_openrecent (Display *dpy, const char *sel) { int i; - unsigned int j; + unsigned int j; assert (_recentcnt > 0); fib_pre_opendir (dpy); query_font_geometry (dpy, _fib_gc, "Last Used", &_fib_font_time_width, NULL, NULL, NULL); @@ -1332,8 +1335,8 @@ static int fib_open (Display *dpy, int item) { static void cb_cancel (Display *dpy) { _status = -1; - // unused - return; (void)dpy; + // unused + return; (void)dpy; } static void cb_open (Display *dpy) { @@ -1478,8 +1481,8 @@ static int fib_widget_at_pos (Display *dpy, int x, int y, int *it) { return 0; - // unused - (void)dpy; + // unused + (void)dpy; } static void fib_update_hover (Display *dpy, int need_expose, const int type, const int item) { @@ -1592,9 +1595,11 @@ static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long ti fib_select (dpy, it); _dblclk = time; } - /*if (_fsel >= 0) { + /* + if (_fsel >= 0) { if (!(_dirlist[_fsel].flags & 4)); - }*/ + } + */ } break; case 1: // paths @@ -1656,8 +1661,8 @@ static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long ti static void fib_mouseup (Display *dpy, int x, int y, int btn, unsigned long time) { _scrl_my = -1; - // unused - return; (void)dpy; (void)x; (void)y; (void)btn; (void)time; + // unused + return; (void)dpy; (void)x; (void)y; (void)btn; (void)time; } static void add_place_raw (Display *dpy, const char *name, const char *path) { @@ -1885,8 +1890,8 @@ static int x_error_handler (Display *d, XErrorEvent *e) { font_err = 1; return 0; - // unused - (void)d; (void)e; + // unused + (void)d; (void)e; } int x_fib_show (Display *dpy, Window parent, int x, int y) { @@ -2340,7 +2345,7 @@ char *x_fib_filename () { else return NULL; } -#endif // SOFD_HAVE_X11 +#endif // HAVE_X11 #if defined(__clang__) # pragma clang diagnostic pop diff --git a/dgl/src/sofd/libsofd.h b/dgl/src/sofd/libsofd.h index ba8a254f..2abf7881 100644 --- a/dgl/src/sofd/libsofd.h +++ b/dgl/src/sofd/libsofd.h @@ -22,10 +22,15 @@ */ #ifndef LIBSOFD_H -#define LIBSOFD_H +#define LIBSOFD_H 1 +#ifdef HAVE_X11 #include +#ifdef __cplusplus +extern "C" { +#endif + /////////////////////////////////////////////////////////////////////////////// /* public API */ @@ -110,6 +115,16 @@ int x_fib_cfg_buttons (int k, int v); */ int x_fib_cfg_filter_callback (int (*cb)(const char*)); +#ifdef __cplusplus +} +#endif + +#endif /* END X11 specific functions */ + +#ifdef __cplusplus +extern "C" { +#endif + /* 'recently used' API. x-platform * NOTE: all functions use a static cache and are not reentrant. * It is expected that none of these functions are called in @@ -172,4 +187,8 @@ unsigned int x_fib_recent_count (); */ const char *x_fib_recent_at (unsigned int i); -#endif // LIBSOFD_H +#ifdef __cplusplus +} +#endif + +#endif // header guard diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index df042753..cded5826 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -246,7 +246,7 @@ protected: # ifndef DGL_FILE_BROWSER_DISABLED /** File browser selected function. - @see Window::fileBrowserSelected(const char*) + @see Window::onFileSelected(const char*) */ virtual void uiFileBrowserSelected(const char* filename); # endif diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 126a5e38..652826ff 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -185,6 +185,7 @@ void UI::uiFileBrowserSelected(const char*) void UI::uiReshape(uint, uint) { + // NOTE this must be the same as Window::onReshape pData->fallbackOnResize(); } @@ -201,7 +202,9 @@ void UI::onResize(const ResizeEvent& ev) const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); + /* pData->window.setSize(width, height); + */ uiData->setSizeCallback(width, height); } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 3a04a41b..d628c619 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -95,25 +95,25 @@ protected: UI::PrivateData* const uiData = fUI->uiData; DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,); + /* uiData->resizeInProgress = true; fUI->setSize(width, height); uiData->resizeInProgress = false; + */ fUI->uiReshape(width, height); fIsReady = true; } -#if 0 /* TODO */ # ifndef DGL_FILE_BROWSER_DISABLED // custom file-browser selected - void fileBrowserSelected(const char* filename) override + void onFileSelected(const char* const filename) override { DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); fUI->uiFileBrowserSelected(filename); } # endif -#endif private: UI* const fUI; From 4b07bd7c51d5996d6bbfdfbd0554ba34470cd3f0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 16:34:30 +0100 Subject: [PATCH 146/159] Add a file browser dialog test tool Signed-off-by: falkTX --- dgl/Geometry.hpp | 6 ++ dgl/src/Geometry.cpp | 19 ++++ dgl/src/pugl.cpp | 2 +- examples/FileHandling/NanoButton.cpp | 4 +- tests/FileBrowserDialog.cpp | 151 +++++++++++++++++++++++++++ tests/Makefile | 5 + 6 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 tests/FileBrowserDialog.cpp diff --git a/dgl/Geometry.hpp b/dgl/Geometry.hpp index 1bd02282..76d9c158 100644 --- a/dgl/Geometry.hpp +++ b/dgl/Geometry.hpp @@ -751,6 +751,12 @@ public: */ bool contains(const Point& pos) const noexcept; + /** + Check if this rectangle contains the point @a pos of another type. + */ + template + bool contains(const Point& pos) const noexcept; + /** Check if this rectangle contains X. */ diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index f2c44c44..8b1ba7d1 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -885,6 +885,25 @@ bool Rectangle::contains(const Point& p) const noexcept return contains(p.x, p.y); } +template +template +bool Rectangle::contains(const Point& p) const noexcept +{ + return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); +} + +template<> template<> +bool Rectangle::contains(const Point& p) const noexcept +{ + return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); +} + +template<> template<> +bool Rectangle::contains(const Point& p) const noexcept +{ + return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight); +} + template bool Rectangle::containsX(const T& x) const noexcept { diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 3902a199..5a013bda 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -90,7 +90,7 @@ #endif #ifdef HAVE_X11 -# define 400 +# define DBLCLKTME 400 # include "sofd/libsofd.h" # include "sofd/libsofd.c" #endif diff --git a/examples/FileHandling/NanoButton.cpp b/examples/FileHandling/NanoButton.cpp index a3850403..6b77dccd 100644 --- a/examples/FileHandling/NanoButton.cpp +++ b/examples/FileHandling/NanoButton.cpp @@ -84,10 +84,10 @@ void Button::setBackgroundColor(const Color color) bool Button::onMouse(const MouseEvent &ev) { - if (ev.press & contains(ev.pos)) + if (ev.press && contains(ev.pos)) { buttonActive = true; - setLabelColor(labelColor); + setLabelColor(labelColor); fCallback->buttonClicked(this, true); return true; } diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp new file mode 100644 index 00000000..e3a15847 --- /dev/null +++ b/tests/FileBrowserDialog.cpp @@ -0,0 +1,151 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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 "tests.hpp" + +#include "dgl/NanoVG.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- + +class NanoFilePicker : public NanoStandaloneWindow +{ + Rectangle buttonBounds; + bool buttonClick = false; + bool buttonHover = false; + +public: + NanoFilePicker(Application& app) + : NanoStandaloneWindow(app) + { +#ifndef DGL_NO_SHARED_RESOURCES + loadSharedResources(); +#endif + + } + +protected: + void onNanoDisplay() override + { + Color labelColor(255, 255, 255); + Color backgroundColor(32, + buttonClick ? 128 : 32, + buttonHover ? 128 : 32); + Color borderColor; + + // Button background + beginPath(); + fillColor(backgroundColor); + strokeColor(borderColor); + rect(buttonBounds.getX(), buttonBounds.getY(), buttonBounds.getWidth(), buttonBounds.getHeight()); + fill(); + stroke(); + closePath(); + + // Label + beginPath(); + fontSize(14); + fillColor(labelColor); + Rectangle buttonTextBounds; + textBounds(0, 0, "Press me", NULL, buttonTextBounds); + textAlign(ALIGN_CENTER | ALIGN_MIDDLE); + + fillColor(255, 255, 255, 255); + text(buttonBounds.getX() + buttonBounds.getWidth()/2, + buttonBounds.getY() + buttonBounds.getHeight()/2, + "Press me", NULL); + closePath(); + } + + bool onMotion(const MotionEvent& ev) override + { + const bool newButtonHover = buttonBounds.contains(ev.pos); + + if (newButtonHover != buttonHover) + { + buttonHover = newButtonHover; + repaint(); + return true; + } + + return newButtonHover; + } + + bool onMouse(const MouseEvent& ev) override + { + if (ev.button != 1) + return false; + + if (! buttonBounds.contains(ev.pos)) + { + if (buttonClick) + { + buttonClick = false; + repaint(); + return true; + } + return false; + } + + const bool newButtonClick = ev.press; + + if (newButtonClick != buttonClick) + { + buttonClick = newButtonClick; + repaint(); + + if (newButtonClick) + { + FileBrowserOptions opts; + opts.title = "Look at me"; + openFileBrowser(opts); + } + + return true; + } + + return newButtonClick; + } + + void onResize(const ResizeEvent& ev) override + { + const uint width = ev.size.getWidth(); + const uint height = ev.size.getHeight(); + + buttonBounds = Rectangle(width - 120, height/2 - 20, 100, 40); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +int main() +{ + USE_NAMESPACE_DGL; + + Application app(true); + NanoFilePicker win(app); + win.setSize(500, 200); + win.setTitle("FileBrowserDialog"); + win.show(); + app.exec(); + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/Makefile b/tests/Makefile index 1cf83350..a0a9c31e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -30,6 +30,7 @@ UNIT_TESTS += Window.cairo endif ifeq ($(HAVE_OPENGL),true) MANUAL_TESTS += Demo.opengl +MANUAL_TESTS += FileBrowserDialog MANUAL_TESTS += NanoSubWidgets UNIT_TESTS += Window.opengl endif @@ -138,6 +139,10 @@ clean: @echo "Linking Demo (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ +../build/tests/FileBrowserDialog$(APP_EXT): ../build/tests/FileBrowserDialog.cpp.o ../build/libdgl-opengl.a + @echo "Linking Demo (OpenGL)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + ../build/tests/NanoSubWidgets$(APP_EXT): ../build/tests/NanoSubWidgets.cpp.o ../build/libdgl-opengl.a @echo "Linking Demo (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ From 1425c28b2ebe3d81fe1fa43a09882b4ddd2c48fe Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 19:03:57 +0100 Subject: [PATCH 147/159] Get file browser to work, including test Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 3 +++ tests/FileBrowserDialog.cpp | 40 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 0a75af82..20a678f5 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -39,6 +39,9 @@ Application::PrivateData::PrivateData(const bool standalone) puglSetWorldHandle(world, this); puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); +#ifdef HAVE_X11 + sofdFileDialogSetup(world); +#endif } Application::PrivateData::~PrivateData() diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp index e3a15847..6191956e 100644 --- a/tests/FileBrowserDialog.cpp +++ b/tests/FileBrowserDialog.cpp @@ -27,43 +27,44 @@ class NanoFilePicker : public NanoStandaloneWindow Rectangle buttonBounds; bool buttonClick = false; bool buttonHover = false; + String selectedFile; public: NanoFilePicker(Application& app) - : NanoStandaloneWindow(app) + : NanoStandaloneWindow(app), + selectedFile("No file selected yet") { #ifndef DGL_NO_SHARED_RESOURCES loadSharedResources(); #endif - } protected: void onNanoDisplay() override { - Color labelColor(255, 255, 255); - Color backgroundColor(32, - buttonClick ? 128 : 32, - buttonHover ? 128 : 32); - Color borderColor; + // Selected file + beginPath(); + fontSize(14); + textAlign(ALIGN_LEFT | ALIGN_MIDDLE); + fillColor(255, 255, 255, 255); + text(20, getHeight()/2, selectedFile, NULL); + closePath(); // Button background beginPath(); - fillColor(backgroundColor); - strokeColor(borderColor); + fillColor(Color(32, buttonClick ? 128 : 32, buttonHover ? 128 : 32)); + strokeColor(Color()); rect(buttonBounds.getX(), buttonBounds.getY(), buttonBounds.getWidth(), buttonBounds.getHeight()); fill(); stroke(); closePath(); - // Label + // Button label beginPath(); fontSize(14); - fillColor(labelColor); Rectangle buttonTextBounds; textBounds(0, 0, "Press me", NULL, buttonTextBounds); textAlign(ALIGN_CENTER | ALIGN_MIDDLE); - fillColor(255, 255, 255, 255); text(buttonBounds.getX() + buttonBounds.getWidth()/2, buttonBounds.getY() + buttonBounds.getHeight()/2, @@ -110,6 +111,9 @@ protected: if (newButtonClick) { + selectedFile = "(in progress)"; + repaint(); + FileBrowserOptions opts; opts.title = "Look at me"; openFileBrowser(opts); @@ -128,6 +132,18 @@ protected: buttonBounds = Rectangle(width - 120, height/2 - 20, 100, 40); } + + void onFileSelected(const char* filename) override + { + if (filename == nullptr) + filename = "Cancelled"; + + if (selectedFile == filename) + return; + + selectedFile = filename; + repaint(); + } }; // -------------------------------------------------------------------------------------------------------------------- From ac93a69edcb04973ddeaede0318055076f397368 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 19:20:09 +0100 Subject: [PATCH 148/159] VST2 UI is not user resizable Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index a2fd36ca..31b64e2b 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -23,6 +23,8 @@ #endif #if DISTRHO_PLUGIN_HAS_UI +# undef DISTRHO_UI_USER_RESIZABLE +# define DISTRHO_UI_USER_RESIZABLE 0 # define DISTRHO_UI_IS_STANDALONE 0 # include "DistrhoUIInternal.hpp" #endif From 99d4bff4a5ec575d22c96720e97fb0a41405c48e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 19:23:27 +0100 Subject: [PATCH 149/159] Fix windows build Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 2 +- dgl/src/WindowPrivateData.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 0377915d..29a54900 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -563,7 +563,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh { DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); - DGL_DBGp("PUGL: onReshape : %i %i\n", width, height); + DGL_DBGp("PUGL: onReshape : %f %f\n", width, height); if (autoScaling) { diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 83b9d86a..11fac31a 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -70,7 +70,7 @@ struct Window::PrivateData : IdleCallback { #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ - const char* win32SelectedFile; + char* win32SelectedFile; #endif /** Modal window setup. */ From 9fe07168865ca9ff9a360b0aa4da0ae277125c71 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 19:47:09 +0100 Subject: [PATCH 150/159] Update pugl Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 83ccad73..5a4d2469 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 83ccad73546576a5b439c0fe2415e16509f8f28a +Subproject commit 5a4d2469a72ad072a0e2397477a2cd701c2ca309 From 3a8bd346b71685f93d9f15bfb93e31b4c3cc8eab Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 19:59:56 +0100 Subject: [PATCH 151/159] Allow to build tests from root dir Signed-off-by: falkTX --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1bc07b82..117dabc9 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,9 @@ else gen: endif +tests: + $(MAKE) -C tests + # -------------------------------------------------------------- clean: @@ -71,4 +74,4 @@ endif # -------------------------------------------------------------- -.PHONY: dgl examples +.PHONY: dgl examples tests From 21cb97c6883c12c32ff5874aee8c924044304462 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 20:31:45 +0100 Subject: [PATCH 152/159] Fix clang windows build Signed-off-by: falkTX --- Makefile | 2 +- Makefile.base.mk | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 117dabc9..8e66f355 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ else gen: endif -tests: +tests: dgl $(MAKE) -C tests # -------------------------------------------------------------- diff --git a/Makefile.base.mk b/Makefile.base.mk index 62dea1a1..34286c37 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -38,6 +38,9 @@ endif ifneq (,$(findstring mingw,$(TARGET_MACHINE))) WINDOWS=true endif +ifneq (,$(findstring windows,$(TARGET_MACHINE))) +WINDOWS=true +endif endif endif @@ -165,10 +168,7 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif ifeq ($(WINDOWS),true) -# mingw has issues with this specific optimization -# See https://github.com/falkTX/Carla/issues/696 -BASE_OPTS += -fno-rerun-cse-after-loop -# See https://github.com/falkTX/Carla/issues/855 +# Needed for windows, see https://github.com/falkTX/Carla/issues/855 BASE_OPTS += -mstackrealign else # Not needed for Windows From 94cd0e0821570e370863ebe7cd45d4edf4fc4da5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 21:21:19 +0100 Subject: [PATCH 153/159] Temporary debug measures Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 14 +++++++++++++- dgl/src/WindowPrivateData.cpp | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 20a678f5..dad54a0a 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -19,6 +19,8 @@ #include "pugl.hpp" +#include + START_NAMESPACE_DGL typedef std::list::reverse_iterator WindowListReverseIterator; @@ -38,7 +40,17 @@ Application::PrivateData::PrivateData(const bool standalone) DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); puglSetWorldHandle(world, this); - puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); + + // FIXME + static int wc_count = 0; + char classNameBuf[256]; + std::srand((std::time(NULL))); + std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d-%p", + DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE), std::rand(), ++wc_count, this); + classNameBuf[sizeof(classNameBuf)-1] = '\0'; + d_stderr("--------------------------------------------------------------- className is %s", classNameBuf); + + puglSetClassName(world, classNameBuf); #ifdef HAVE_X11 sofdFileDialogSetup(world); #endif diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 29a54900..08985995 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -627,7 +627,7 @@ void Window::PrivateData::onPuglClose() void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) { - DGL_DBGp("onPuglFocus : %i %i\n", focus, mode); + DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed); if (isClosed) return; From 633e1a4c1ad40351a68816c913029c41a64bfb44 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 21:24:18 +0100 Subject: [PATCH 154/159] testing Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index dad54a0a..a23a8062 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -46,7 +46,8 @@ Application::PrivateData::PrivateData(const bool standalone) char classNameBuf[256]; std::srand((std::time(NULL))); std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d-%p", - DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE), std::rand(), ++wc_count, this); + "TESTING", std::rand(), ++wc_count, this); + // DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE) classNameBuf[sizeof(classNameBuf)-1] = '\0'; d_stderr("--------------------------------------------------------------- className is %s", classNameBuf); From 396f5edb07ecfa5d87ab29733bb03713696912c5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 22:46:19 +0100 Subject: [PATCH 155/159] Make Application::quit() thread-safe Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 13 +++++++++++++ dgl/src/ApplicationPrivateData.hpp | 3 +++ 2 files changed, 16 insertions(+) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index a23a8062..07cb21fb 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -32,6 +32,7 @@ Application::PrivateData::PrivateData(const bool standalone) standalone ? PUGL_WORLD_THREADS : 0x0)), isStandalone(standalone), isQuitting(false), + isQuittingInNextCycle(false), isStarting(true), visibleWindows(0), windows(), @@ -92,6 +93,12 @@ void Application::PrivateData::oneWindowClosed() noexcept void Application::PrivateData::idle(const uint timeoutInMs) { + if (isQuittingInNextCycle) + { + quit(); + isQuittingInNextCycle = false; + } + if (world != nullptr) { const double timeoutInSeconds = timeoutInMs != 0 @@ -112,6 +119,12 @@ void Application::PrivateData::quit() { DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); + if (! isQuittingInNextCycle) + { + isQuittingInNextCycle = true; + return; + } + isQuitting = true; #ifndef DPF_TEST_APPLICATION_CPP diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index a636902c..4841c969 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -39,6 +39,9 @@ struct Application::PrivateData { /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ bool isQuitting; + /** Helper for safely close everything from main thread. */ + bool isQuittingInNextCycle; + /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ bool isStarting; From fe06ccecf5014e9509124d2a4b83cec4239531a8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 23:20:40 +0100 Subject: [PATCH 156/159] Add String::remove(char) method Signed-off-by: falkTX --- distrho/extra/String.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index f74c430e..335cd9b8 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -527,6 +527,29 @@ public: return *this; } + /* + * Remove all occurrences of character 'c', shifting and truncating the string as necessary. + */ + String& remove(const char c) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this); + + if (fBufferLen == 0) + return *this; + + for (std::size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] == c) + { + --fBufferLen; + std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i); + } + } + + fBuffer[fBufferLen] = '\0'; + return *this; + } + /* * Truncate the string to size 'n'. */ From cbe07278771727abb9dc2108786f08ff687bc48e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 23:25:39 +0100 Subject: [PATCH 157/159] Fixup mess surrounding application class name once and for all Signed-off-by: falkTX --- dgl/Application.hpp | 11 ++++++++++ dgl/src/Application.cpp | 5 +++++ dgl/src/ApplicationPrivateData.cpp | 20 ++++++++---------- dgl/src/ApplicationPrivateData.hpp | 3 +++ distrho/src/DistrhoUIInternal.hpp | 33 ++++++++++++++++++++++++------ 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 2c166b8e..3bac2f8a 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -84,6 +84,17 @@ public: */ void removeIdleCallback(IdleCallback* callback); + /** + Set the class name of the application. + + This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. + It is not displayed to the user, but can be used in scripts and by window managers, + so it should be the same for every instance of the application, but different from other applications. + + Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. + */ + void setClassName(const char* name); + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index cd6e8a34..80d5957c 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -67,6 +67,11 @@ void Application::removeIdleCallback(IdleCallback* const callback) pData->idleCallbacks.remove(callback); } +void Application::setClassName(const char* const name) +{ + pData->setClassName(name); +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 07cb21fb..409085b2 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -41,18 +41,7 @@ Application::PrivateData::PrivateData(const bool standalone) DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); puglSetWorldHandle(world, this); - - // FIXME - static int wc_count = 0; - char classNameBuf[256]; - std::srand((std::time(NULL))); - std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d-%p", - "TESTING", std::rand(), ++wc_count, this); - // DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE) - classNameBuf[sizeof(classNameBuf)-1] = '\0'; - d_stderr("--------------------------------------------------------------- className is %s", classNameBuf); - - puglSetClassName(world, classNameBuf); + puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); #ifdef HAVE_X11 sofdFileDialogSetup(world); #endif @@ -136,6 +125,13 @@ void Application::PrivateData::quit() #endif } +void Application::PrivateData::setClassName(const char* const name) +{ + DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); + + puglSetClassName(world, name); +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 4841c969..627f2a1f 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -76,6 +76,9 @@ struct Application::PrivateData { For standalone mode only. */ void quit(); + /** Set pugl world class name. */ + void setClassName(const char* name); + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) }; diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index d628c619..21a0625e 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -53,14 +53,35 @@ UI* createUiWrapper(void* dspPtr, uintptr_t winId, double scaleFactor, const cha UI* createUiWrapper(void* dspPtr, Window* window); #endif +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +// ----------------------------------------------------------------------- +// Plugin Application, will set class name based on plugin details + +class PluginApplication : public Application +{ +public: + PluginApplication() + : Application(DISTRHO_UI_IS_STANDALONE) + { + const char* const className = ( +#ifdef DISTRHO_PLUGIN_BRAND + DISTRHO_PLUGIN_BRAND +#else + DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE) +#endif + "-" DISTRHO_PLUGIN_NAME + ); + setClassName(className); + } +}; + // ----------------------------------------------------------------------- // Plugin Window, needed to take care of resize properly -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI class UIExporterWindow : public Window { public: - UIExporterWindow(Application& app, const intptr_t winId, const double scaleFactor, void* const dspPtr) + UIExporterWindow(PluginApplication& app, const intptr_t winId, const double scaleFactor, void* const dspPtr) : Window(app, winId, scaleFactor, DISTRHO_UI_USER_RESIZABLE), fUI(createUiWrapper(dspPtr, this)), fIsReady(false) @@ -143,7 +164,7 @@ public: #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)), #else - : glApp(DISTRHO_UI_IS_STANDALONE), + : glApp(), glWindow(glApp, winId, scaleFactor, dspPtr), fChangingSize(false), fUI(glWindow.getUI()), @@ -461,8 +482,8 @@ private: // ------------------------------------------------------------------- // DGL Application and Window for this widget - Application glApp; - UIExporterWindow glWindow; + PluginApplication glApp; + UIExporterWindow glWindow; // prevent recursion bool fChangingSize; From 50849162456c244deb4878fee21efbfae50ce3f5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 23:27:15 +0100 Subject: [PATCH 158/159] Fix crashes under win32 Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 4 +++- dgl/src/pugl-upstream | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 08985995..5c8b8fa8 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -574,7 +574,9 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh const uint uwidth = static_cast(width + 0.5); const uint uheight = static_cast(height + 0.5); - self->onReshape(uwidth, uheight); + + if (self != nullptr) + self->onReshape(uwidth, uheight); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 5a4d2469..c5a5eb4c 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 5a4d2469a72ad072a0e2397477a2cd701c2ca309 +Subproject commit c5a5eb4cdcb12dc8d74be4f5fbb335e8b3eac711 From a44de6c0b542258aa3aa512a19330edc8fb0c094 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 23 May 2021 23:52:08 +0100 Subject: [PATCH 159/159] Get win32 file dialog working again Signed-off-by: falkTX --- dgl/Window.hpp | 4 ++- dgl/src/WindowPrivateData.cpp | 67 +++++++++++++++++++++++++++++++++-- dgl/src/WindowPrivateData.hpp | 2 +- tests/FileBrowserDialog.cpp | 16 ++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 46d7c1be..f1f07c85 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -317,8 +317,10 @@ public: Open a file browser dialog with this window as parent. A few options can be specified to setup the dialog. - This function does not block. If a path is selected, onFileSelected() will be called with the user chosen path. + If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename. + + This function does not block the event loop. */ bool openFileBrowser(const FileBrowserOptions& options); #endif diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 5c8b8fa8..4a1da9d4 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -23,6 +23,9 @@ #ifdef DISTRHO_OS_WINDOWS # include +# include +# include +# include #else # include #endif @@ -50,6 +53,11 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +// static pointer used for direct comparisons +static const char* const kWin32SelectedFileCancelled = "__dpf_cancelled__"; +#endif + static double getDesktopScaleFactor() { if (const char* const scale = getenv("DPF_SCALE_FACTOR")) @@ -195,6 +203,11 @@ Window::PrivateData::~PrivateData() appData->idleCallbacks.remove(this); appData->windows.remove(self); +#ifdef DISTRHO_OS_WINDOWS + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) + std::free(const_cast(win32SelectedFile)); +#endif + if (view != nullptr) puglFreeView(view); } @@ -366,11 +379,13 @@ void Window::PrivateData::idleCallback() { #ifndef DGL_FILE_BROWSER_DISABLED # ifdef DISTRHO_OS_WINDOWS - if (char* const path = win32SelectedFile) + if (const char* path = win32SelectedFile) { win32SelectedFile = nullptr; + if (path == kWin32SelectedFileCancelled) + path = nullptr; self->onFileSelected(path); - std::free(path); + std::free(const_cast(path)); } # endif # ifdef HAVE_X11 @@ -465,6 +480,54 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti // -------------------------------------------------------------------------- // show +#ifdef DISTRHO_OS_WINDOWS + // the old and compatible dialog API + OPENFILENAMEW ofn; + memset(&ofn, 0, sizeof(ofn)); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) + std::free(const_cast(win32SelectedFile)); + win32SelectedFile = nullptr; + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = (HWND)puglGetNativeWindow(view); + + // set start directory in UTF-16 encoding + std::vector startDirW; + startDirW.resize(startDir.length() + 1); + if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), startDirW.size())) + ofn.lpstrInitialDir = startDirW.data(); + + // set title in UTF-16 encoding + std::vector titleW; + titleW.resize(title.length() + 1); + if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), titleW.size())) + ofn.lpstrTitle = titleW.data(); + + // prepare a buffer to receive the result + std::vector 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 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) + win32SelectedFile = strdup(fileNameA.data()); + } + } + + if (win32SelectedFile == nullptr) + win32SelectedFile = kWin32SelectedFileCancelled; + + return true; +#endif #ifdef HAVE_X11 uint flags = 0x0; // TODO flags diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 11fac31a..83b9d86a 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -70,7 +70,7 @@ struct Window::PrivateData : IdleCallback { #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ - char* win32SelectedFile; + const char* win32SelectedFile; #endif /** Modal window setup. */ diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp index 6191956e..aa70ac51 100644 --- a/tests/FileBrowserDialog.cpp +++ b/tests/FileBrowserDialog.cpp @@ -116,7 +116,11 @@ protected: FileBrowserOptions opts; opts.title = "Look at me"; - openFileBrowser(opts); + if (! openFileBrowser(opts)) + { + selectedFile = "(Failed to start file browser)"; + repaint(); + } } return true; @@ -133,6 +137,16 @@ protected: buttonBounds = Rectangle(width - 120, height/2 - 20, 100, 40); } + void onFocus(const bool focus, CrossingMode) override + { + if (focus) + return; + + buttonClick = false; + buttonHover = false; + repaint(); + } + void onFileSelected(const char* filename) override { if (filename == nullptr)