@@ -2,39 +2,61 @@ | |||
set -e | |||
cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/dgl/*.hpp /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/ | |||
cp -r -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/dgl/src /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/ | |||
cp -r -v /home/falktx/FOSS/GIT-mine/DISTRHO/DPF/distrho/* /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/ || true | |||
cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.h /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
rm -r /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/resources | |||
rm -r /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/sofd | |||
rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/dgl/src/Resources.* | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/include /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/src /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/AUTHORS /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/COPYING /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/src/DistrhoPlugin{Jack,LADSPA+DSSI,LV2,LV2export,VST}.cpp | |||
rm /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/distrho/src/DistrhoUI{DSSI,LV2}.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Cairo.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Vulkan.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Cairo.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Vulkan.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Resources.{cpp,hpp} | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/3BandEQ/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/3bandeq/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/3BandSplitter/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/3bandsplitter/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_mini-series/plugins/PingPongPan/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/pingpongpan/ | |||
# rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.mm | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/jackbridge | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/travesty | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/vst | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/DistrhoInfo.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPlugin{JACK,LADSPA+DSSI,LV2,LV2export,VST2,VST3}.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPluginVST3.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoUI{DSSI,LV2,VST3}.cpp | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/sofd | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/FileBrowserDialog.{cpp,hpp} | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/LibraryUtils.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/RingBuffer.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/ScopedSafeLocale.hpp | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandEQ/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandeq/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandSplitter/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandsplitter/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/PingPongPan/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/pingpongpan/ | |||
# | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/nekobi/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/nekobee-src/{*.c,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/nekobi/nekobee-src/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/nekobee-src/{*.c,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/nekobee-src/ | |||
# | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/DISTRHO_prom/plugins/ProM/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/prom/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_prom/plugins/ProM/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/prom/ | |||
# | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/GrooveJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/groovejuice/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/PowerJuice/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/powerjuice/ | |||
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/PowerJuiceX2/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/powerjuicex2/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/SegmentJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/segmentjuice/ | |||
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/StutterJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/stutterjuice/ | |||
# # cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/TriggerJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/triggerjuice/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/VectorJuice/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/vectorjuice/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/DISTRHO/JuicePlugins/plugins/WobbleJuice/{*.cpp,*.hpp,*.hxx,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/wobblejuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/GrooveJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/groovejuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuiceX2/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuicex2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/SegmentJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/segmentjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/StutterJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/stutterjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/TriggerJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/triggerjuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/VectorJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/vectorjuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/WobbleJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/wobblejuice/ | |||
# | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamComp/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamcomp/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamCompX2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamcompx2/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamEQ2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zameq2/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamSynth/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamsynth/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZamTube/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamtube/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZaMultiComp/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamulticomp/ | |||
# cp -v /home/falktx/FOSS/GIT-mine/zam-plugins-DPF/plugins/ZaMultiCompX2/{*.cpp,*.hpp,*.h} /home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/native-plugins/zamulticompx2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcomp/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcompx2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamEQ2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zameq2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamSynth/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamsynth/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamTube/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamtube/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticomp/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticompx2/ |
@@ -178,7 +178,11 @@ endif | |||
ifeq ($(HAVE_DGL),true) | |||
BASE_FLAGS += -DHAVE_DGL | |||
BASE_FLAGS += -DDGL_NAMESPACE=CarlaDGL -DDGL_FILE_BROWSER_DISABLED -DDGL_NO_SHARED_RESOURCES | |||
BASE_FLAGS += -DDGL_NAMESPACE=CarlaDGL | |||
BASE_FLAGS += -DDGL_OPENGL | |||
BASE_FLAGS += -DDGL_FILE_BROWSER_DISABLED | |||
BASE_FLAGS += -DDGL_NO_SHARED_RESOURCES | |||
BASE_FLAGS += -DDONT_SET_USING_DGL_NAMESPACE | |||
endif | |||
ifeq ($(HAVE_FLUIDSYNTH),true) | |||
@@ -218,6 +222,10 @@ endif | |||
ifeq ($(USING_JUCE),true) | |||
BASE_FLAGS += -DUSING_JUCE | |||
BASE_FLAGS += -DJUCE_APP_CONFIG_HEADER='"AppConfig.h"' | |||
ifeq ($(WIN32),true) | |||
BASE_FLAGS += -D_WIN32_WINNT=0x0600 | |||
endif | |||
endif | |||
ifeq ($(USING_JUCE_AUDIO_DEVICES),true) | |||
@@ -232,16 +240,6 @@ ifeq ($(STATIC_PLUGIN_TARGET),true) | |||
BASE_FLAGS += -DSTATIC_PLUGIN_TARGET | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Custom build flags for JUCE | |||
ifeq ($(USING_JUCE),true) | |||
BUILD_CXX_FLAGS += -DJUCE_APP_CONFIG_HEADER='"AppConfig.h"' | |||
ifeq ($(WIN32),true) | |||
BASE_FLAGS += -D_WIN32_WINNT=0x0600 | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Allow custom namespace | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -21,30 +21,26 @@ | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// Forward class names | |||
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. | |||
Unless stated otherwise, functions within this class are not thread-safe. | |||
*/ | |||
class Application | |||
class DISTRHO_API Application | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
*/ | |||
Application(); | |||
// NOTE: the default value is not yet passed, so we catch where we use this | |||
Application(bool isStandalone = true); | |||
/** | |||
Destructor. | |||
@@ -62,29 +58,71 @@ public: | |||
idle() is called at regular intervals. | |||
@note This function is meant for standalones only, *never* call this from plugins. | |||
*/ | |||
void exec(unsigned int idleTime = 10); | |||
void exec(uint idleTimeInMs = 30); | |||
/** | |||
Quit the application. | |||
This stops the event-loop and closes all Windows. | |||
This function is thread-safe. | |||
*/ | |||
void quit(); | |||
/** | |||
Check if the application is about to quit. | |||
Returning true means there's no event-loop running at the moment (or it's just about to stop). | |||
This function is thread-safe. | |||
*/ | |||
bool isQuitting() const noexcept; | |||
/** | |||
Check if the application is standalone, otherwise running as a module or plugin. | |||
This function is thread-safe. | |||
*/ | |||
bool isStandalone() const noexcept; | |||
/** | |||
Return the time in seconds. | |||
This is a monotonically increasing clock with high resolution.@n | |||
The returned time is only useful to compare against other times returned by this function, | |||
its absolute value has no meaning. | |||
*/ | |||
double getTime() const; | |||
/** | |||
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, use Window::addIdleCallback for time-relative callbacks. | |||
*/ | |||
void addIdleCallback(IdleCallback* callback); | |||
/** | |||
Remove an idle callback previously added via addIdleCallback(). | |||
*/ | |||
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. | |||
*/ | |||
bool isQuiting() const noexcept; | |||
void setClassName(const char* name); | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
friend class PluginApplication; | |||
friend class Window; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,7 +20,7 @@ | |||
#include "../distrho/extra/LeakDetector.hpp" | |||
#include "../distrho/extra/ScopedPointer.hpp" | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Define namespace | |||
#ifndef DGL_NAMESPACE | |||
@@ -31,124 +31,48 @@ | |||
#define END_NAMESPACE_DGL } | |||
#define USE_NAMESPACE_DGL using namespace DGL_NAMESPACE; | |||
#ifdef DISTRHO_OS_WINDOWS | |||
// ----------------------------------------------------------------------- | |||
// Fix OpenGL includes for Windows, based on glfw code | |||
#ifndef APIENTRY | |||
# define APIENTRY __stdcall | |||
# define DGL_APIENTRY_DEFINED | |||
#endif // APIENTRY | |||
/* We need WINGDIAPI defined */ | |||
#ifndef WINGDIAPI | |||
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) | |||
# define WINGDIAPI __declspec(dllimport) | |||
# elif defined(__LCC__) | |||
# define WINGDIAPI __stdcall | |||
# else | |||
# define WINGDIAPI extern | |||
# endif | |||
# define DGL_WINGDIAPI_DEFINED | |||
#endif // WINGDIAPI | |||
/* Some <GL/glu.h> files also need CALLBACK defined */ | |||
#ifndef CALLBACK | |||
# if defined(_MSC_VER) | |||
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) | |||
# define CALLBACK __stdcall | |||
# else | |||
# define CALLBACK | |||
# endif | |||
# else | |||
# define CALLBACK __stdcall | |||
# endif | |||
# define DGL_CALLBACK_DEFINED | |||
#endif // CALLBACK | |||
/* Most GL/glu.h variants on Windows need wchar_t */ | |||
#include <cstddef> | |||
#endif // DISTRHO_OS_WINDOWS | |||
// ----------------------------------------------------------------------- | |||
// OpenGL includes | |||
#ifdef DISTRHO_OS_MAC | |||
# include <OpenGL/gl.h> | |||
#else | |||
# ifndef DISTRHO_OS_WINDOWS | |||
# define GL_GLEXT_PROTOTYPES | |||
# endif | |||
# include <GL/gl.h> | |||
# include <GL/glext.h> | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Missing OpenGL defines | |||
#if defined(GL_BGR_EXT) && ! defined(GL_BGR) | |||
# define GL_BGR GL_BGR_EXT | |||
#endif | |||
#if defined(GL_BGRA_EXT) && ! defined(GL_BGRA) | |||
# define GL_BGRA GL_BGRA_EXT | |||
#endif | |||
#ifndef GL_CLAMP_TO_BORDER | |||
# define GL_CLAMP_TO_BORDER 0x812D | |||
#endif | |||
#ifdef DISTRHO_OS_WINDOWS | |||
// ----------------------------------------------------------------------- | |||
// Fix OpenGL includes for Windows, based on glfw code | |||
#ifdef DGL_APIENTRY_DEFINED | |||
# undef APIENTRY | |||
# undef DGL_APIENTRY_DEFINED | |||
#endif | |||
#ifdef DGL_WINGDIAPI_DEFINED | |||
# undef WINGDIAPI | |||
# undef DGL_WINGDIAPI_DEFINED | |||
#endif | |||
#ifdef DGL_CALLBACK_DEFINED | |||
# undef CALLBACK | |||
# undef DGL_CALLBACK_DEFINED | |||
#endif | |||
#endif // DISTRHO_OS_WINDOWS | |||
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 Widget::KeyboardEvent::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 DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace, | |||
kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape, | |||
kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete, | |||
// Unicode Private Use Area | |||
kKeyF1 = 0xE000, | |||
kKeyF2, | |||
kKeyF3, | |||
kKeyF4, | |||
@@ -170,25 +94,92 @@ 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 | |||
}; | |||
/** | |||
Common flags for all events. | |||
*/ | |||
enum EventFlag { | |||
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 | |||
}; | |||
// ----------------------------------------------------------------------- | |||
/** | |||
A mouse cursor type. | |||
This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. | |||
*/ | |||
enum MouseCursor { | |||
kMouseCursorArrow, ///< Default pointing arrow | |||
kMouseCursorCaret, ///< Caret (I-Beam) for text entry | |||
kMouseCursorCrosshair, ///< Cross-hair | |||
kMouseCursorHand, ///< Hand with a pointing finger | |||
kMouseCursorNotAllowed, ///< Operation not allowed | |||
kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize | |||
kMouseCursorUpDown, ///< Up/down arrow for vertical resize | |||
kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize | |||
kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize | |||
}; | |||
/** | |||
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 | |||
/** | |||
Graphics context, definition depends on build type. | |||
*/ | |||
struct GraphicsContext {}; | |||
/** | |||
Idle callback. | |||
*/ | |||
class IdleCallback | |||
struct IdleCallback | |||
{ | |||
public: | |||
virtual ~IdleCallback() {} | |||
virtual void idleCallback() = 0; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -198,6 +189,6 @@ END_NAMESPACE_DGL | |||
using namespace DGL_NAMESPACE; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#endif // DGL_BASE_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -23,7 +23,7 @@ struct NVGcolor; | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
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; | |||
@@ -65,6 +65,11 @@ struct Color { | |||
*/ | |||
Color(const Color& color1, const Color& color2, float u) noexcept; | |||
/** | |||
Create a new color based on this one but with a different alpha value. | |||
*/ | |||
Color withAlpha(float alpha) noexcept; | |||
/** | |||
Create a color specified by hue, saturation and lightness. | |||
Values must in [0..1] range. | |||
@@ -74,7 +79,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 +88,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; | |||
@@ -95,6 +100,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. | |||
@@ -103,7 +113,7 @@ struct Color { | |||
operator NVGcolor() const noexcept; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -0,0 +1,174 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
#define DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
#include "Widget.hpp" | |||
START_NAMESPACE_DGL | |||
/* NOTE none of these classes get assigned to a widget automatically | |||
Manual plugging into Widget events is needed, like so: | |||
``` | |||
bool onMouse(const MouseEvent& ev) override | |||
{ | |||
return ButtonEventHandler::mouseEvent(ev); | |||
} | |||
``` | |||
*/ | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
class ButtonEventHandler | |||
{ | |||
public: | |||
enum State { | |||
kButtonStateDefault = 0x0, | |||
kButtonStateHover = 0x1, | |||
kButtonStateActive = 0x2, | |||
kButtonStateActiveHover = kButtonStateActive|kButtonStateHover | |||
}; | |||
class Callback | |||
{ | |||
public: | |||
virtual ~Callback() {} | |||
virtual void buttonClicked(SubWidget* widget, int button) = 0; | |||
}; | |||
explicit ButtonEventHandler(SubWidget* self); | |||
virtual ~ButtonEventHandler(); | |||
bool isActive() noexcept; | |||
void setActive(bool active, bool sendCallback) noexcept; | |||
bool isChecked() const noexcept; | |||
void setChecked(bool checked, bool sendCallback) noexcept; | |||
bool isCheckable() const noexcept; | |||
void setCheckable(bool checkable) noexcept; | |||
Point<double> getLastClickPosition() const noexcept; | |||
Point<double> getLastMotionPosition() const noexcept; | |||
void setCallback(Callback* callback) noexcept; | |||
bool mouseEvent(const Widget::MouseEvent& ev); | |||
bool motionEvent(const Widget::MotionEvent& ev); | |||
protected: | |||
State getState() const noexcept; | |||
void clearState() noexcept; | |||
virtual void stateChanged(State state, State oldState); | |||
void setInternalCallback(Callback* callback) noexcept; | |||
void triggerUserCallback(SubWidget* widget, int button); | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ButtonEventHandler) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
class KnobEventHandler | |||
{ | |||
public: | |||
enum Orientation { | |||
Horizontal, | |||
Vertical | |||
}; | |||
// NOTE hover not implemented yet | |||
enum State { | |||
kKnobStateDefault = 0x0, | |||
kKnobStateHover = 0x1, | |||
kKnobStateDragging = 0x2, | |||
kKnobStateDraggingHover = kKnobStateDragging|kKnobStateHover | |||
}; | |||
class Callback | |||
{ | |||
public: | |||
virtual ~Callback() {} | |||
virtual void knobDragStarted(SubWidget* widget) = 0; | |||
virtual void knobDragFinished(SubWidget* widget) = 0; | |||
virtual void knobValueChanged(SubWidget* widget, float value) = 0; | |||
}; | |||
explicit KnobEventHandler(SubWidget* self); | |||
explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other); | |||
KnobEventHandler& operator=(const KnobEventHandler& other); | |||
virtual ~KnobEventHandler(); | |||
// returns raw value, is assumed to be scaled if using log | |||
float getValue() const noexcept; | |||
// NOTE: value is assumed to be scaled if using log | |||
virtual bool setValue(float value, bool sendCallback = false) noexcept; | |||
// returns 0-1 ranged value, already with log scale as needed | |||
float getNormalizedValue() const noexcept; | |||
// NOTE: value is assumed to be scaled if using log | |||
void setDefault(float def) noexcept; | |||
// NOTE: value is assumed to be scaled if using log | |||
void setRange(float min, float max) noexcept; | |||
void setStep(float step) noexcept; | |||
void setUsingLogScale(bool yesNo) noexcept; | |||
Orientation getOrientation() const noexcept; | |||
void setOrientation(const Orientation orientation) noexcept; | |||
void setCallback(Callback* callback) noexcept; | |||
bool mouseEvent(const Widget::MouseEvent& ev); | |||
bool motionEvent(const Widget::MotionEvent& ev); | |||
bool scrollEvent(const Widget::ScrollEvent& ev); | |||
protected: | |||
State getState() const noexcept; | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
/* not for use */ | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
KnobEventHandler(KnobEventHandler& other) = delete; | |||
KnobEventHandler(const KnobEventHandler& other) = delete; | |||
#else | |||
KnobEventHandler(KnobEventHandler& other); | |||
KnobEventHandler(const KnobEventHandler& other); | |||
#endif | |||
DISTRHO_LEAK_DETECTOR(KnobEventHandler) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -21,7 +21,7 @@ | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Forward class names | |||
template<typename> class Line; | |||
@@ -29,7 +29,7 @@ template<typename> class Circle; | |||
template<typename> class Triangle; | |||
template<typename> class Rectangle; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
DGL Point class. | |||
@@ -114,14 +114,14 @@ public: | |||
bool operator!=(const Point<T>& pos) const noexcept; | |||
private: | |||
T fX, fY; | |||
T x, y; | |||
template<typename> friend class Line; | |||
template<typename> friend class Circle; | |||
template<typename> friend class Triangle; | |||
template<typename> friend class Rectangle; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
DGL Size class. | |||
@@ -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; | |||
@@ -210,6 +210,8 @@ public: | |||
*/ | |||
bool isInvalid() const noexcept; | |||
Size<int> toInt() const noexcept; | |||
Size<T> operator+(const Size<T>& size) noexcept; | |||
Size<T> operator-(const Size<T>& size) noexcept; | |||
Size<T>& operator=(const Size<T>& size) noexcept; | |||
@@ -217,6 +219,8 @@ public: | |||
Size<T>& operator-=(const Size<T>& size) noexcept; | |||
Size<T>& operator*=(double m) noexcept; | |||
Size<T>& operator/=(double d) noexcept; | |||
Size<T> operator*(double m) const noexcept; | |||
Size<T> operator/(double m) const noexcept; | |||
bool operator==(const Size<T>& size) const noexcept; | |||
bool operator!=(const Size<T>& size) const noexcept; | |||
@@ -346,11 +350,6 @@ public: | |||
*/ | |||
void moveBy(const Point<T>& pos) noexcept; | |||
/** | |||
Draw this line using the current OpenGL state. | |||
*/ | |||
void draw(); | |||
/** | |||
Return true if line is null (start and end pos are equal). | |||
*/ | |||
@@ -361,12 +360,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<T>& operator=(const Line<T>& line) noexcept; | |||
bool operator==(const Line<T>& line) const noexcept; | |||
bool operator!=(const Line<T>& line) const noexcept; | |||
#ifndef DPF_TEST_POINT_CPP | |||
/** | |||
Draw this line using the current OpenGL state.@n | |||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
*/ | |||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||
void draw(); | |||
#endif | |||
private: | |||
Point<T> fPosStart, fPosEnd; | |||
Point<T> posStart, posEnd; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -461,19 +476,35 @@ public: | |||
void setNumSegments(const uint num); | |||
/** | |||
Draw this circle using the current OpenGL state. | |||
Draw this circle using the provided graphics context. | |||
*/ | |||
void draw(); | |||
void draw(const GraphicsContext& context); | |||
/** | |||
Draw lines (outline of this circle) using the current OpenGL state. | |||
Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width. | |||
*/ | |||
void drawOutline(); | |||
void drawOutline(const GraphicsContext& context, T lineWidth = 1); | |||
Circle<T>& operator=(const Circle<T>& cir) noexcept; | |||
bool operator==(const Circle<T>& cir) const noexcept; | |||
bool operator!=(const Circle<T>& cir) const noexcept; | |||
#ifndef DPF_TEST_POINT_CPP | |||
/** | |||
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.@n | |||
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead. | |||
*/ | |||
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
void drawOutline(); | |||
#endif | |||
private: | |||
Point<T> fPos; | |||
float fSize; | |||
@@ -481,8 +512,6 @@ private: | |||
// cached values | |||
float fTheta, fCos, fSin; | |||
void _draw(const bool outline); | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -516,16 +545,6 @@ public: | |||
*/ | |||
Triangle(const Triangle<T>& tri) noexcept; | |||
/** | |||
Draw this triangle using the current OpenGL state. | |||
*/ | |||
void draw(); | |||
/** | |||
Draw lines (outline of this triangle) using the current OpenGL state. | |||
*/ | |||
void drawOutline(); | |||
/** | |||
Return true if triangle is null (all its points are equal). | |||
An null triangle is also invalid. | |||
@@ -549,14 +568,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<T>& operator=(const Triangle<T>& tri) noexcept; | |||
bool operator==(const Triangle<T>& tri) const noexcept; | |||
bool operator!=(const Triangle<T>& tri) const noexcept; | |||
private: | |||
Point<T> fPos1, fPos2, fPos3; | |||
#ifndef DPF_TEST_POINT_CPP | |||
/** | |||
Draw this triangle using the current OpenGL state.@n | |||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
*/ | |||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||
void draw(); | |||
void _draw(const bool outline); | |||
/** | |||
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(); | |||
#endif | |||
private: | |||
Point<T> pos1, pos2, pos3; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -710,6 +753,17 @@ public: | |||
*/ | |||
bool contains(const Point<T>& pos) const noexcept; | |||
/** | |||
Check if this rectangle contains the point @a pos affected by a custom scale. | |||
*/ | |||
bool containsAfterScaling(const Point<T>& pos, double scaling) const noexcept; | |||
/** | |||
Check if this rectangle contains the point @a pos of another type. | |||
*/ | |||
template<typename T2> | |||
bool contains(const Point<T2>& pos) const noexcept; | |||
/** | |||
Check if this rectangle contains X. | |||
*/ | |||
@@ -721,14 +775,37 @@ public: | |||
bool containsY(const T& y) const noexcept; | |||
/** | |||
Draw this rectangle using the current OpenGL state. | |||
Return true if size is null (0x0). | |||
An null size is also invalid. | |||
*/ | |||
bool isNull() const noexcept; | |||
/** | |||
Return true if size is not null (0x0). | |||
A non-null size is still invalid if its width or height are negative. | |||
*/ | |||
void draw(); | |||
bool isNotNull() const noexcept; | |||
/** | |||
Draw lines (outline of this rectangle) using the current OpenGL state. | |||
Return true if size is valid (width and height are higher than zero). | |||
*/ | |||
void drawOutline(); | |||
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, optionally specifying line width. | |||
*/ | |||
void drawOutline(const GraphicsContext& context, T lineWidth = 1); | |||
Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept; | |||
Rectangle<T>& operator*=(double m) noexcept; | |||
@@ -736,11 +813,23 @@ public: | |||
bool operator==(const Rectangle<T>& size) const noexcept; | |||
bool operator!=(const Rectangle<T>& size) const noexcept; | |||
private: | |||
Point<T> fPos; | |||
Size<T> fSize; | |||
/** | |||
Draw this rectangle using the current OpenGL state.@n | |||
DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
*/ | |||
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||
void draw(); | |||
void _draw(const bool outline); | |||
/** | |||
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(); | |||
private: | |||
Point<T> pos; | |||
Size<T> size; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -17,131 +17,20 @@ | |||
#ifndef DGL_IMAGE_HPP_INCLUDED | |||
#define DGL_IMAGE_HPP_INCLUDED | |||
#include "Geometry.hpp" | |||
#ifdef DGL_CAIRO | |||
#include "Cairo.hpp" | |||
#else | |||
#include "OpenGL.hpp" | |||
#endif | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
/** | |||
Base DGL 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: | |||
/** | |||
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<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE); | |||
/** | |||
Constructor using another image data. | |||
*/ | |||
Image(const Image& image); | |||
/** | |||
Destructor. | |||
*/ | |||
~Image(); | |||
/** | |||
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<uint>& size, const GLenum format = GL_BGRA, const GLenum type = GL_UNSIGNED_BYTE) noexcept; | |||
/** | |||
Check if this image is valid. | |||
*/ | |||
bool isValid() const noexcept; | |||
/** | |||
Get width. | |||
*/ | |||
uint getWidth() const noexcept; | |||
/** | |||
Get height. | |||
*/ | |||
uint getHeight() const noexcept; | |||
/** | |||
Get size. | |||
*/ | |||
const Size<uint>& getSize() const noexcept; | |||
/** | |||
Get the raw image data. | |||
*/ | |||
const char* getRawData() const noexcept; | |||
/** | |||
Get the image format. | |||
*/ | |||
GLenum getFormat() const noexcept; | |||
/** | |||
Get the image type. | |||
*/ | |||
GLenum getType() const noexcept; | |||
/** | |||
Draw this image at (0, 0) point. | |||
*/ | |||
void draw(); | |||
/** | |||
Draw this image at (x, y) point. | |||
*/ | |||
void drawAt(const int x, const int y); | |||
/** | |||
Draw this image at position @a pos. | |||
*/ | |||
void drawAt(const Point<int>& pos); | |||
Image& operator=(const Image& image) noexcept; | |||
bool operator==(const Image& image) const noexcept; | |||
bool operator!=(const Image& image) const noexcept; | |||
private: | |||
const char* fRawData; | |||
Size<uint> fSize; | |||
GLenum fFormat; | |||
GLenum fType; | |||
GLuint fTextureId; | |||
bool fIsReady; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
#ifdef DGL_CAIRO | |||
typedef CairoImage Image; | |||
#else | |||
typedef OpenGLImage Image; | |||
#endif | |||
END_NAMESPACE_DGL | |||
#endif // DGL_IMAGE_HPP_INCLUDED | |||
#endif |
@@ -0,0 +1,156 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_IMAGE_BASE_HPP_INCLUDED | |||
#define DGL_IMAGE_BASE_HPP_INCLUDED | |||
#include "Geometry.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
enum ImageFormat { | |||
kImageFormatNull, | |||
kImageFormatGrayscale, | |||
kImageFormatBGR, | |||
kImageFormatBGRA, | |||
kImageFormatRGB, | |||
kImageFormatRGBA, | |||
}; | |||
/** | |||
Base DGL Image class. | |||
This is an Image class that handles raw image data in pixels. | |||
It is an abstract class that provides the common methods to build on top. | |||
Cairo and OpenGL Image classes are based upon this one. | |||
@see CairoImage, OpenGLImage | |||
*/ | |||
class ImageBase | |||
{ | |||
protected: | |||
/** | |||
Constructor for a null Image. | |||
*/ | |||
ImageBase(); | |||
/** | |||
Constructor using raw image data. | |||
@note @a rawData must remain valid for the lifetime of this Image. | |||
*/ | |||
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* rawData, const Size<uint>& size, ImageFormat format); | |||
/** | |||
Constructor using another image data. | |||
*/ | |||
ImageBase(const ImageBase& image); | |||
public: | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~ImageBase(); | |||
/** | |||
Check if this image is valid. | |||
*/ | |||
bool isValid() const noexcept; | |||
/** | |||
Check if this image is not valid. | |||
*/ | |||
bool isInvalid() const noexcept; | |||
/** | |||
Get width. | |||
*/ | |||
uint getWidth() const noexcept; | |||
/** | |||
Get height. | |||
*/ | |||
uint getHeight() const noexcept; | |||
/** | |||
Get size. | |||
*/ | |||
const Size<uint>& getSize() const noexcept; | |||
/** | |||
Get the raw image data. | |||
*/ | |||
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<uint>& size, | |||
ImageFormat format = kImageFormatBGRA) noexcept; | |||
/** | |||
Draw this image at (0, 0) point using the current OpenGL context. | |||
*/ | |||
void draw(const GraphicsContext& context); | |||
/** | |||
Draw this image at (x, y) point using the current OpenGL context. | |||
*/ | |||
void drawAt(const GraphicsContext& context, int x, int y); | |||
/** | |||
Draw this image at position @a pos using the current OpenGL context. | |||
*/ | |||
virtual void drawAt(const GraphicsContext& context, const Point<int>& pos) = 0; | |||
/** | |||
TODO document this. | |||
*/ | |||
ImageBase& operator=(const ImageBase& image) noexcept; | |||
bool operator==(const ImageBase& image) const noexcept; | |||
bool operator!=(const ImageBase& image) const noexcept; | |||
protected: | |||
const char* rawData; | |||
Size<uint> size; | |||
ImageFormat format; | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_IMAGE_HPP_INCLUDED |
@@ -0,0 +1,257 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||
#include "EventHandlers.hpp" | |||
#include "StandaloneWindow.hpp" | |||
#include "SubWidget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
DGL Image About Window class. | |||
This is a Window attached (transient) to another Window that simply shows an Image as its content. | |||
It is typically used for "about this project" style pop-up Windows. | |||
Pressing 'Esc' or clicking anywhere on the window will automatically close it. | |||
@see CairoImageAboutWindow, OpenGLImageAboutWindow, Window::runAsModal(bool) | |||
*/ | |||
template <class ImageType> | |||
class ImageBaseAboutWindow : public StandaloneWindow | |||
{ | |||
public: | |||
/** | |||
Constructor taking an existing Window as the parent transient window and an optional image. | |||
If @a image is valid, the about window size will match the image size. | |||
*/ | |||
explicit ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image = ImageType()); | |||
/** | |||
Constructor taking a top-level-widget's Window as the parent transient window and an optional image. | |||
If @a image is valid, the about window size will match the image size. | |||
*/ | |||
explicit ImageBaseAboutWindow(TopLevelWidget* topLevelWidget, const ImageType& image = ImageType()); | |||
/** | |||
Set a new image to use as background for this window. | |||
Window size will adjust to match the image size. | |||
*/ | |||
void setImage(const ImageType& image); | |||
protected: | |||
void onDisplay() override; | |||
bool onKeyboard(const KeyboardEvent&) override; | |||
bool onMouse(const MouseEvent&) override; | |||
private: | |||
ImageType img; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
DGL Image Button class. | |||
This is a typical button, where the drawing comes from a pregenerated set of images. | |||
The button can be under "normal", "hover" and "down" states, with one separate image possible for each. | |||
The event logic for this button comes from the ButtonEventHandler class. | |||
@see CairoImageButton, OpenGLImageButton | |||
*/ | |||
template <class ImageType> | |||
class ImageBaseButton : public SubWidget, | |||
public ButtonEventHandler | |||
{ | |||
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) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
DGL Image Knob class. | |||
This is a typical knob/dial, where the drawing comes from a pregenerated image "filmstrip". | |||
The knob's "filmstrip" image can be either horizontal or vertical, | |||
with the number of steps automatically based on the largest value (ie, horizontal if width>height, vertical if height>width). | |||
There are no different images for "hover" or "down" states. | |||
The event logic for this knob comes from the KnobEventHandler class. | |||
@see CairoImageKnob, OpenGLImageKnob | |||
*/ | |||
template <class ImageType> | |||
class ImageBaseKnob : public SubWidget, | |||
public KnobEventHandler | |||
{ | |||
public: | |||
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; | |||
void setCallback(Callback* callback) noexcept; | |||
void setImageLayerCount(uint count) noexcept; | |||
void setRotationAngle(int angle); | |||
bool setValue(float value, bool sendCallback = false) noexcept override; | |||
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; | |||
DISTRHO_LEAK_DETECTOR(ImageBaseKnob) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// note set range and step before setting the value | |||
template <class ImageType> | |||
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; | |||
~ImageBaseSlider() override; | |||
float getValue() const noexcept; | |||
void setValue(float value, bool sendCallback = false) noexcept; | |||
void setDefault(float def) noexcept; | |||
void setStartPos(const Point<int>& startPos) noexcept; | |||
void setStartPos(int x, int y) noexcept; | |||
void setEndPos(const Point<int>& 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; | |||
// 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<int>&) const noexcept {} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
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; | |||
~ImageBaseSwitch() override; | |||
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; | |||
DISTRHO_LEAK_DETECTOR(ImageBaseSwitch) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,253 +18,23 @@ | |||
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED | |||
#include "Image.hpp" | |||
#include "Widget.hpp" | |||
#include "Window.hpp" | |||
#include "ImageBaseWidgets.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
class ImageAboutWindow : public Window, | |||
public Widget | |||
{ | |||
public: | |||
explicit ImageAboutWindow(Window& parent, const Image& image = Image()); | |||
explicit ImageAboutWindow(Widget* widget, const Image& image = Image()); | |||
void setImage(const Image& image); | |||
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 Widget | |||
{ | |||
public: | |||
class Callback | |||
{ | |||
public: | |||
virtual ~Callback() {} | |||
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); | |||
~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 Widget | |||
{ | |||
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(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept; | |||
explicit ImageKnob(Widget* widget, 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 Widget | |||
{ | |||
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(Window& parent, const Image& image) noexcept; | |||
explicit ImageSlider(Widget* widget, const Image& image) noexcept; | |||
float getValue() const noexcept; | |||
void setValue(float value, bool sendCallback = false) noexcept; | |||
void setStartPos(const Point<int>& startPos) noexcept; | |||
void setStartPos(int x, int y) noexcept; | |||
void setEndPos(const Point<int>& 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: | |||
Image fImage; | |||
float fMinimum; | |||
float fMaximum; | |||
float fStep; | |||
float fValue; | |||
float fValueTmp; | |||
bool fDragging; | |||
bool fInverted; | |||
bool fValueIsSet; | |||
int fStartedX; | |||
int fStartedY; | |||
Callback* fCallback; | |||
Point<int> fStartPos; | |||
Point<int> fEndPos; | |||
Rectangle<int> 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<int>&) const noexcept {} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
class ImageSwitch : public Widget | |||
{ | |||
public: | |||
class Callback | |||
{ | |||
public: | |||
virtual ~Callback() {} | |||
virtual void imageSwitchClicked(ImageSwitch* imageButton, 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(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) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
#ifdef DGL_CAIRO | |||
typedef CairoImageAboutWindow ImageAboutWindow; | |||
typedef CairoImageButton ImageButton; | |||
typedef CairoImageKnob ImageKnob; | |||
typedef CairoImageSlider ImageSlider; | |||
typedef CairoImageSwitch ImageSwitch; | |||
#else | |||
typedef OpenGLImageAboutWindow ImageAboutWindow; | |||
typedef OpenGLImageButton ImageButton; | |||
typedef OpenGLImageKnob ImageKnob; | |||
typedef OpenGLImageSlider ImageSlider; | |||
typedef OpenGLImageSwitch ImageSwitch; | |||
#endif | |||
END_NAMESPACE_DGL | |||
@@ -11,6 +11,7 @@ include ../Makefile.mk | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
BUILD_CXX_FLAGS += $(DGL_FLAGS) -Isrc | |||
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include | |||
ifneq ($(MACOS),true) | |||
WINE_FLAGS = -I/usr/include/wine/wine/windows | |||
@@ -22,27 +23,47 @@ endif | |||
OBJS = \ | |||
$(OBJDIR)/Application.cpp.o \ | |||
$(OBJDIR)/ApplicationPrivateData.cpp.o \ | |||
$(OBJDIR)/Color.cpp.o \ | |||
$(OBJDIR)/EventHandlers.cpp.o \ | |||
$(OBJDIR)/Geometry.cpp.o \ | |||
$(OBJDIR)/Image.cpp.o \ | |||
$(OBJDIR)/ImageWidgets.cpp.o \ | |||
$(OBJDIR)/ImageBase.cpp.o \ | |||
$(OBJDIR)/ImageBaseWidgets.cpp.o \ | |||
$(OBJDIR)/NanoVG.cpp.o \ | |||
$(OBJDIR)/Widget.cpp.o | |||
$(OBJDIR)/OpenGL.cpp.o \ | |||
$(OBJDIR)/SubWidget.cpp.o \ | |||
$(OBJDIR)/SubWidgetPrivateData.cpp.o \ | |||
$(OBJDIR)/TopLevelWidget.cpp.o \ | |||
$(OBJDIR)/TopLevelWidgetPrivateData.cpp.o \ | |||
$(OBJDIR)/Widget.cpp.o \ | |||
$(OBJDIR)/WidgetPrivateData.cpp.o \ | |||
$(OBJDIR)/Window.cpp.o \ | |||
$(OBJDIR)/WindowPrivateData.cpp.o | |||
ifeq ($(MACOS),true) | |||
OBJS += $(OBJDIR)/Window.mm.o | |||
OBJS += $(OBJDIR)/pugl.mm.o | |||
else | |||
OBJS += $(OBJDIR)/Window.cpp.o | |||
OBJS += $(OBJDIR)/pugl.cpp.o | |||
endif | |||
OBJS_wine = \ | |||
$(OBJDIR)/pugl.cpp-wine.o \ | |||
$(OBJDIR)/Application.cpp-wine.o \ | |||
$(OBJDIR)/ApplicationPrivateData.cpp-wine.o \ | |||
$(OBJDIR)/Color.cpp-wine.o \ | |||
$(OBJDIR)/EventHandlers.cpp-wine.o \ | |||
$(OBJDIR)/Geometry.cpp-wine.o \ | |||
$(OBJDIR)/Image.cpp-wine.o \ | |||
$(OBJDIR)/ImageWidgets.cpp-wine.o \ | |||
$(OBJDIR)/ImageBase.cpp-wine.o \ | |||
$(OBJDIR)/ImageBaseWidgets.cpp-wine.o \ | |||
$(OBJDIR)/OpenGL.cpp-wine.o \ | |||
$(OBJDIR)/SubWidget.cpp-wine.o \ | |||
$(OBJDIR)/SubWidgetPrivateData.cpp-wine.o \ | |||
$(OBJDIR)/TopLevelWidget.cpp-wine.o \ | |||
$(OBJDIR)/TopLevelWidgetPrivateData.cpp-wine.o \ | |||
$(OBJDIR)/Widget.cpp-wine.o \ | |||
$(OBJDIR)/Window.cpp-wine.o | |||
$(OBJDIR)/WidgetPrivateData.cpp-wine.o \ | |||
$(OBJDIR)/Window.cpp-wine.o \ | |||
$(OBJDIR)/WindowPrivateData.cpp-wine.o | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,7 +18,15 @@ | |||
#define DGL_NANO_WIDGET_HPP_INCLUDED | |||
#include "Color.hpp" | |||
#include "Widget.hpp" | |||
#include "OpenGL.hpp" | |||
#include "SubWidget.hpp" | |||
#include "TopLevelWidget.hpp" | |||
#include "StandaloneWindow.hpp" | |||
#ifdef _MSC_VER | |||
# pragma warning(push) | |||
# pragma warning(disable:4661) /* instantiated template classes whose methods are defined elsewhere */ | |||
#endif | |||
#ifndef DGL_NO_SHARED_RESOURCES | |||
# define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" | |||
@@ -32,9 +40,17 @@ START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// Forward class names | |||
class BlendishWidget; | |||
class NanoVG; | |||
// ----------------------------------------------------------------------- | |||
// Helper methods | |||
/** | |||
Create a NanoVG context using the DPF-provided NanoVG library. | |||
On Windows this will load a few extra OpenGL functions required for NanoVG to work. | |||
*/ | |||
NVGcontext* nvgCreateGL(int flags); | |||
// ----------------------------------------------------------------------- | |||
// NanoImage | |||
@@ -304,7 +320,9 @@ public: | |||
/** | |||
Constructor reusing a NanoVG context, used for subwidgets. | |||
*/ | |||
/* | |||
NanoVG(NanoWidget* groupWidget); | |||
*/ | |||
/** | |||
Destructor. | |||
@@ -435,6 +453,11 @@ public: | |||
*/ | |||
void globalAlpha(float alpha); | |||
/** | |||
Sets the color tint applied to all rendered shapes. | |||
*/ | |||
void globalTint(Color tint); | |||
/* -------------------------------------------------------------------- | |||
* Transforms */ | |||
@@ -578,12 +601,26 @@ public: | |||
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags); | |||
/** | |||
Creates image from specified image data. | |||
Creates image from specified raw format image data. | |||
*/ | |||
NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data, | |||
ImageFlags imageFlags, ImageFormat format); | |||
/** | |||
Creates image from specified raw format image data. | |||
Overloaded function for convenience. | |||
@see ImageFlags | |||
*/ | |||
NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data, | |||
int imageFlags, ImageFormat format); | |||
/** | |||
Creates image from specified RGBA image data. | |||
*/ | |||
NanoImage::Handle createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags); | |||
/** | |||
Creates image from specified image data. | |||
Creates image from specified RGBA image data. | |||
Overloaded function for convenience. | |||
@see ImageFlags | |||
*/ | |||
@@ -850,14 +887,13 @@ public: | |||
/** | |||
Load DPF's internal shared resources for this NanoVG class. | |||
*/ | |||
virtual void loadSharedResources(); | |||
virtual bool loadSharedResources(); | |||
#endif | |||
private: | |||
NVGcontext* const fContext; | |||
bool fInFrame; | |||
bool fIsSubWidget; | |||
friend class BlendishWidget; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoVG) | |||
}; | |||
@@ -872,30 +908,39 @@ private: | |||
The drawing function onDisplay() is implemented internally but a | |||
new onNanoDisplay() needs to be overridden instead. | |||
*/ | |||
class NanoWidget : public Widget, | |||
public NanoVG | |||
template <class BaseWidget> | |||
class NanoBaseWidget : public BaseWidget, | |||
public NanoVG | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
Constructor for a NanoSubWidget. | |||
@see CreateFlags | |||
*/ | |||
explicit NanoBaseWidget(Widget* parentGroupWidget, int flags = CREATE_ANTIALIAS); | |||
/** | |||
Constructor for a NanoTopLevelWidget. | |||
@see CreateFlags | |||
*/ | |||
explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS); | |||
explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); | |||
/** | |||
Constructor for a subwidget. | |||
Constructor for a NanoStandaloneWindow without transient parent window. | |||
@see CreateFlags | |||
*/ | |||
explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS); | |||
explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS); | |||
/** | |||
Constructor for a subwidget, reusing a NanoVG context. | |||
Constructor for a NanoStandaloneWindow with transient parent window. | |||
@see CreateFlags | |||
*/ | |||
explicit NanoWidget(NanoWidget* groupWidget); | |||
explicit NanoBaseWidget(Application& app, Window& transientParentWindow, int flags = CREATE_ANTIALIAS); | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~NanoWidget(); | |||
virtual ~NanoBaseWidget() {} | |||
protected: | |||
/** | |||
@@ -905,14 +950,17 @@ 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 | |||
{ | |||
// NOTE maybe should use BaseWidget::getWindow().getScaleFactor() as 3rd arg ? | |||
NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); | |||
onNanoDisplay(); | |||
NanoVG::endFrame(); | |||
} | |||
// these should not be used | |||
void beginFrame(uint,uint) {} | |||
@@ -921,11 +969,22 @@ private: | |||
void cancelFrame() {} | |||
void endFrame() {} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget) | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget) | |||
}; | |||
typedef NanoBaseWidget<SubWidget> NanoSubWidget; | |||
typedef NanoBaseWidget<TopLevelWidget> NanoTopLevelWidget; | |||
typedef NanoBaseWidget<StandaloneWindow> NanoStandaloneWindow; | |||
DISTRHO_DEPRECATED_BY("NanoSubWidget") | |||
typedef NanoSubWidget NanoWidget; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#ifdef _MSC_VER | |||
# pragma warning(pop) | |||
#endif | |||
#endif // DGL_NANO_WIDGET_HPP_INCLUDED |
@@ -0,0 +1,308 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_OPENGL_HPP_INCLUDED | |||
#define DGL_OPENGL_HPP_INCLUDED | |||
#include "ImageBase.hpp" | |||
#include "ImageBaseWidgets.hpp" | |||
// ----------------------------------------------------------------------- | |||
// Fix OpenGL includes for Windows, based on glfw code (part 1) | |||
#undef DGL_CALLBACK_DEFINED | |||
#undef DGL_WINGDIAPI_DEFINED | |||
#ifdef DISTRHO_OS_WINDOWS | |||
#ifndef APIENTRY | |||
# define APIENTRY __stdcall | |||
#endif // APIENTRY | |||
/* We need WINGDIAPI defined */ | |||
#ifndef WINGDIAPI | |||
# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) | |||
# define WINGDIAPI __declspec(dllimport) | |||
# elif defined(__LCC__) | |||
# define WINGDIAPI __stdcall | |||
# else | |||
# define WINGDIAPI extern | |||
# endif | |||
# define DGL_WINGDIAPI_DEFINED | |||
#endif // WINGDIAPI | |||
/* Some <GL/glu.h> files also need CALLBACK defined */ | |||
#ifndef CALLBACK | |||
# if defined(_MSC_VER) | |||
# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) | |||
# define CALLBACK __stdcall | |||
# else | |||
# define CALLBACK | |||
# endif | |||
# else | |||
# define CALLBACK __stdcall | |||
# endif | |||
# define DGL_CALLBACK_DEFINED | |||
#endif // CALLBACK | |||
/* Most GL/glu.h variants on Windows need wchar_t */ | |||
#include <cstddef> | |||
#endif // DISTRHO_OS_WINDOWS | |||
// ----------------------------------------------------------------------- | |||
// OpenGL includes | |||
#ifdef DISTRHO_OS_MAC | |||
# ifdef DGL_USE_OPENGL3 | |||
# include <OpenGL/gl3.h> | |||
# include <OpenGL/gl3ext.h> | |||
# else | |||
# include <OpenGL/gl.h> | |||
# endif | |||
#else | |||
# ifndef DISTRHO_OS_WINDOWS | |||
# define GL_GLEXT_PROTOTYPES | |||
# endif | |||
# ifndef __GLEW_H__ | |||
# include <GL/gl.h> | |||
# include <GL/glext.h> | |||
# endif | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Missing OpenGL defines | |||
#if defined(GL_BGR_EXT) && !defined(GL_BGR) | |||
# define GL_BGR GL_BGR_EXT | |||
#endif | |||
#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) | |||
# define GL_BGRA GL_BGRA_EXT | |||
#endif | |||
#ifndef GL_CLAMP_TO_BORDER | |||
# define GL_CLAMP_TO_BORDER 0x812D | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Fix OpenGL includes for Windows, based on glfw code (part 2) | |||
#ifdef DGL_CALLBACK_DEFINED | |||
# undef CALLBACK | |||
# undef DGL_CALLBACK_DEFINED | |||
#endif | |||
#ifdef DGL_WINGDIAPI_DEFINED | |||
# undef WINGDIAPI | |||
# undef DGL_WINGDIAPI_DEFINED | |||
#endif | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
/** | |||
OpenGL Graphics context. | |||
*/ | |||
struct OpenGLGraphicsContext : GraphicsContext | |||
{ | |||
}; | |||
// ----------------------------------------------------------------------- | |||
static inline | |||
ImageFormat asDISTRHOImageFormat(const GLenum format) | |||
{ | |||
switch (format) | |||
{ | |||
#ifdef DGL_USE_OPENGL3 | |||
case GL_RED: | |||
#else | |||
case GL_LUMINANCE: | |||
#endif | |||
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) | |||
{ | |||
switch (format) | |||
{ | |||
case kImageFormatNull: | |||
break; | |||
case kImageFormatGrayscale: | |||
#ifdef DGL_USE_OPENGL3 | |||
return GL_RED; | |||
#else | |||
return GL_LUMINANCE; | |||
#endif | |||
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. | |||
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* 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* rawData, const Size<uint>& size, ImageFormat format = kImageFormatBGRA); | |||
/** | |||
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* rawData, | |||
const Size<uint>& 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<int>& pos) override; | |||
/** | |||
TODO document this. | |||
*/ | |||
OpenGLImage& operator=(const OpenGLImage& image) noexcept; | |||
// FIXME this should not be needed | |||
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) | |||
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); }; | |||
inline void draw(const GraphicsContext& context) | |||
{ drawAt(context, Point<int>(0, 0)); }; | |||
inline void drawAt(const GraphicsContext& context, int x, int y) | |||
{ drawAt(context, Point<int>(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 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<uint>&, ImageFormat)") | |||
explicit OpenGLImage(const char* rawData, const Size<uint>& size, GLenum glFormat); | |||
/** | |||
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(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<int>&)") | |||
void drawAt(const Point<int>& pos); | |||
/** | |||
Get the image type. | |||
DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE. | |||
*/ | |||
DISTRHO_DEPRECATED | |||
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | |||
private: | |||
GLuint textureId; | |||
bool setupCalled; | |||
}; | |||
// ----------------------------------------------------------------------- | |||
typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | |||
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | |||
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | |||
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -17,39 +17,69 @@ | |||
#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED | |||
#define DGL_STANDALONE_WINDOW_HPP_INCLUDED | |||
#include "Application.hpp" | |||
#include "Widget.hpp" | |||
#include "TopLevelWidget.hpp" | |||
#include "Window.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
class StandaloneWindow : public Application, | |||
public Window | |||
class StandaloneWindow : public Window, | |||
public TopLevelWidget | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
Constructor without parent. | |||
*/ | |||
StandaloneWindow(); | |||
StandaloneWindow(Application& app) | |||
: Window(app), | |||
TopLevelWidget((Window&)*this), | |||
sgc((Window&)*this) {} | |||
/** | |||
Show window and execute application. | |||
Constructor with a transient parent window, typically used to run as modal. | |||
*/ | |||
void exec(); | |||
StandaloneWindow(Application& app, Window& transientParentWindow) | |||
: Window(app, transientParentWindow), | |||
TopLevelWidget((Window&)*this), | |||
sgc((Window&)*this, transientParentWindow) {} | |||
private: | |||
Widget* fWidget; | |||
/** @internal */ | |||
void onReshape(uint width, uint height) override; | |||
/** | |||
Clear current graphics context. | |||
Must be called at the end of your StandaloneWindow constructor. | |||
*/ | |||
void done() | |||
{ | |||
sgc.done(); | |||
} | |||
/** @internal */ | |||
void _addWidget(Widget* widget) override; | |||
/** | |||
Overloaded functions to ensure they apply to the Window class. | |||
*/ | |||
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<uint> getSize() const noexcept { return Window::getSize(); } | |||
void repaint() noexcept { Window::repaint(); } | |||
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<uint>& size) { Window::setSize(size); } | |||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) | |||
{ return Window::addIdleCallback(callback, timerFrequencyInMs); } | |||
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | |||
Application& getApp() const noexcept { return Window::getApp(); } | |||
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | |||
double getScaleFactor() const noexcept { return Window::getScaleFactor(); } | |||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | |||
bool keepAspectRatio = false, bool automaticallyScale = false) | |||
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); } | |||
/** @internal */ | |||
void _removeWidget(Widget* widget) override; | |||
private: | |||
ScopedGraphicsContext sgc; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | |||
}; | |||
@@ -0,0 +1,179 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_SUBWIDGET_HPP_INCLUDED | |||
#define DGL_SUBWIDGET_HPP_INCLUDED | |||
#include "Widget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
Sub-Widget class. | |||
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. | |||
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 | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
*/ | |||
explicit SubWidget(Widget* parentWidget); | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~SubWidget(); | |||
/** | |||
Check if this widget contains the point defined by @a x and @a y. | |||
*/ | |||
// TODO rename as containsRelativePos | |||
template<typename T> | |||
bool contains(T x, T y) const noexcept; | |||
/** | |||
Check if this widget contains the point @a pos. | |||
*/ | |||
// TODO rename as containsRelativePos | |||
template<typename T> | |||
bool contains(const Point<T>& pos) const noexcept; | |||
/** | |||
Get absolute X. | |||
*/ | |||
int getAbsoluteX() const noexcept; | |||
/** | |||
Get absolute Y. | |||
*/ | |||
int getAbsoluteY() const noexcept; | |||
/** | |||
Get absolute position. | |||
*/ | |||
Point<int> getAbsolutePos() const noexcept; | |||
/** | |||
Get absolute area of this subwidget. | |||
This is the same as `Rectangle<int>(getAbsolutePos(), getSize());` | |||
@see getConstrainedAbsoluteArea() | |||
*/ | |||
Rectangle<int> getAbsoluteArea() const noexcept; | |||
/** | |||
Get absolute area of this subwidget, with special consideration for not allowing negative values. | |||
@see getAbsoluteArea() | |||
*/ | |||
Rectangle<uint> getConstrainedAbsoluteArea() 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<int>& pos) noexcept; | |||
/** | |||
Get the margin currently in use for widget coordinates. | |||
By default this value is (0,0). | |||
*/ | |||
Point<int> getMargin() const noexcept; | |||
/** | |||
Set a margin to be used for widget coordinates using @a x and @a y values. | |||
*/ | |||
void setMargin(int x, int y) noexcept; | |||
/** | |||
Set a margin to be used for widget coordinates. | |||
*/ | |||
void setMargin(const Point<int>& offset) 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. | |||
*/ | |||
void repaint() noexcept override; | |||
/** | |||
Bring this widget to the "front" of the parent widget. | |||
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front". | |||
*/ | |||
virtual void toFront(); | |||
/** | |||
Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing. | |||
*/ | |||
void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true); | |||
/** | |||
Indicate that this subwidget will always draw at its own internal size and needs scaling to fit target size. | |||
*/ | |||
void setNeedsViewportScaling(bool needsViewportScaling = true, double autoScaleFactor = 0.0); | |||
/** | |||
Indicate that this subwidget should not be drawn on screen, typically because it is managed by something else. | |||
*/ | |||
void setSkipDrawing(bool skipDrawing = true); | |||
protected: | |||
/** | |||
A function called when the subwidget's absolute position is changed. | |||
*/ | |||
virtual void onPositionChanged(const PositionChangedEvent&); | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
friend class Widget; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_SUBWIDGET_HPP_INCLUDED | |||
@@ -0,0 +1,146 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED | |||
#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED | |||
#include "Widget.hpp" | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
START_NAMESPACE_DISTRHO | |||
class UI; | |||
END_NAMESPACE_DISTRHO | |||
#endif | |||
START_NAMESPACE_DGL | |||
class Window; | |||
// ----------------------------------------------------------------------- | |||
/** | |||
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. | |||
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 | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
*/ | |||
explicit TopLevelWidget(Window& windowToMapTo); | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~TopLevelWidget(); | |||
/** | |||
Get the application associated with this top-level widget's window. | |||
*/ | |||
Application& getApp() const noexcept; | |||
/** | |||
Get the window associated with this top-level widget. | |||
*/ | |||
Window& getWindow() const noexcept; | |||
/** | |||
Set width of this widget's window. | |||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||
*/ | |||
void setWidth(uint width); | |||
/** | |||
Set height of this widget's window. | |||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||
*/ | |||
void setHeight(uint height); | |||
/** | |||
Set size of this widget's window, using @a width and @a height values. | |||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||
*/ | |||
void setSize(uint width, uint height); | |||
/** | |||
Set size of this widget's window. | |||
@note This will not change the widget's size right away, but be pending on the OS resizing the window | |||
*/ | |||
void setSize(const Size<uint>& size); | |||
/** | |||
TODO document this. | |||
*/ | |||
void repaint() noexcept override; | |||
/** | |||
TODO document this. | |||
*/ | |||
void repaint(const Rectangle<uint>& rect) noexcept; | |||
// TODO group stuff after here, convenience functions present in Window class | |||
bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | |||
const void* getClipboard(const char*& mimeType, size_t& dataSize); | |||
bool setCursor(MouseCursor cursor); | |||
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); | |||
bool removeIdleCallback(IdleCallback* callback); | |||
double getScaleFactor() const noexcept; | |||
void setGeometryConstraints(uint minimumWidth, | |||
uint minimumHeight, | |||
bool keepAspectRatio = false, | |||
bool automaticallyScale = false, | |||
bool resizeNowIfAutoScaling = true); | |||
DISTRHO_DEPRECATED_BY("getApp()") | |||
Application& getParentApp() const noexcept { return getApp(); } | |||
DISTRHO_DEPRECATED_BY("getWindow()") | |||
Window& getParentWindow() const noexcept { return getWindow(); } | |||
protected: | |||
bool onKeyboard(const KeyboardEvent&) override; | |||
bool onCharacterInput(const CharacterInputEvent&) override; | |||
bool onMouse(const MouseEvent&) override; | |||
bool onMotion(const MotionEvent&) override; | |||
bool onScroll(const ScrollEvent&) override; | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
friend class Window; | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
friend class DISTRHO_NAMESPACE::UI; | |||
#endif | |||
/** @internal */ | |||
virtual void requestSizeChange(uint width, uint height); | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,91 +19,102 @@ | |||
#include "Geometry.hpp" | |||
#include <vector> | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Forward class names | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
START_NAMESPACE_DISTRHO | |||
class UI; | |||
END_NAMESPACE_DISTRHO | |||
#endif | |||
START_NAMESPACE_DGL | |||
class Application; | |||
class ImageSlider; | |||
class NanoWidget; | |||
class SubWidget; | |||
class TopLevelWidget; | |||
class Window; | |||
class StandaloneWindow; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/** | |||
Base DGL Widget class. | |||
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 | |||
{ | |||
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 EventFlag. | |||
@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) {} | |||
/** Constructor */ | |||
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 */ | |||
/** Constructor */ | |||
KeyboardEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
key(0) {} | |||
key(0), | |||
keycode(0) {} | |||
}; | |||
/** | |||
Special keyboard event. | |||
@a press True if the key was pressed, false if released. | |||
@a key The key pressed. | |||
@see onSpecial | |||
DEPRECATED This used to be part of DPF due to pugl, but now deprecated and simply non-functional. | |||
All events go through KeyboardEvent or CharacterInputEvent, use those instead. | |||
*/ | |||
struct SpecialEvent : BaseEvent { | |||
struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { | |||
bool press; | |||
Key key; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
SpecialEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
@@ -111,54 +122,107 @@ public: | |||
}; | |||
/** | |||
Mouse event. | |||
@a button The button number (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. | |||
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]; | |||
/** Constructor */ | |||
CharacterInputEvent() noexcept | |||
: BaseEvent(), | |||
keycode(0), | |||
character(0), | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
string{'\0','\0','\0','\0','\0','\0','\0','\0'} {} | |||
#else | |||
string() { std::memset(string, 0, sizeof(string)); } | |||
#endif | |||
}; | |||
/** | |||
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. | |||
@a absolutePos The absolute coordinates of the pointer. | |||
@see onMouse | |||
*/ | |||
struct MouseEvent : BaseEvent { | |||
int button; | |||
uint button; | |||
bool press; | |||
Point<int> pos; | |||
Point<double> pos; | |||
Point<double> absolutePos; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
MouseEvent() noexcept | |||
: BaseEvent(), | |||
button(0), | |||
press(false), | |||
pos(0, 0) {} | |||
pos(0.0, 0.0), | |||
absolutePos(0.0, 0.0) {} | |||
}; | |||
/** | |||
Mouse motion event. | |||
@a pos The widget-relative coordinates of the pointer. | |||
@a pos The widget-relative coordinates of the pointer. | |||
@a absolutePos The absolute coordinates of the pointer. | |||
@see onMotion | |||
*/ | |||
struct MotionEvent : BaseEvent { | |||
Point<int> pos; | |||
Point<double> pos; | |||
Point<double> absolutePos; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
MotionEvent() noexcept | |||
: BaseEvent(), | |||
pos(0, 0) {} | |||
pos(0.0, 0.0), | |||
absolutePos(0.0, 0.0) {} | |||
}; | |||
/** | |||
Mouse scroll event. | |||
@a pos The widget-relative coordinates of the pointer. | |||
@a delta The scroll distance. | |||
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 absolutePos The absolute coordinates of the pointer. | |||
@a delta The scroll distance. | |||
@a direction The direction of the scroll or "smooth". | |||
@see onScroll | |||
*/ | |||
struct ScrollEvent : BaseEvent { | |||
Point<int> pos; | |||
Point<float> delta; | |||
Point<double> pos; | |||
Point<double> absolutePos; | |||
Point<double> delta; | |||
ScrollDirection direction; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
ScrollEvent() noexcept | |||
: BaseEvent(), | |||
pos(0, 0), | |||
delta(0.0f, 0.0f) {} | |||
pos(0.0, 0.0), | |||
absolutePos(0.0, 0.0), | |||
delta(0.0, 0.0), | |||
direction(kScrollSmooth) {} | |||
}; | |||
/** | |||
@@ -171,22 +235,40 @@ public: | |||
Size<uint> size; | |||
Size<uint> oldSize; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
ResizeEvent() noexcept | |||
: size(0, 0), | |||
oldSize(0, 0) {} | |||
}; | |||
/** | |||
Constructor. | |||
Widget position changed event. | |||
@a pos The new absolute position of the widget. | |||
@a oldPos The previous absolute position of the widget. | |||
@see onPositionChanged | |||
*/ | |||
explicit Widget(Window& parent); | |||
struct PositionChangedEvent { | |||
Point<int> pos; | |||
Point<int> oldPos; | |||
/** Constructor */ | |||
PositionChangedEvent() noexcept | |||
: pos(0, 0), | |||
oldPos(0, 0) {} | |||
}; | |||
private: | |||
/** | |||
Constructor for a subwidget. | |||
Private constructor, reserved for TopLevelWidget class. | |||
*/ | |||
explicit Widget(Widget* groupWidget); | |||
explicit Widget(TopLevelWidget* topLevelWidget); | |||
/** | |||
Private constructor, reserved for SubWidget class. | |||
*/ | |||
explicit Widget(Widget* widgetToGroupTo); | |||
public: | |||
/** | |||
Destructor. | |||
*/ | |||
@@ -199,9 +281,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. | |||
@@ -228,7 +310,7 @@ public: | |||
/** | |||
Get size. | |||
*/ | |||
const Size<uint>& getSize() const noexcept; | |||
const Size<uint> getSize() const noexcept; | |||
/** | |||
Set width. | |||
@@ -251,81 +333,58 @@ public: | |||
void setSize(const Size<uint>& size) noexcept; | |||
/** | |||
Get absolute X. | |||
*/ | |||
int getAbsoluteX() const noexcept; | |||
/** | |||
Get absolute Y. | |||
*/ | |||
int getAbsoluteY() const noexcept; | |||
/** | |||
Get absolute position. | |||
*/ | |||
const Point<int>& 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. | |||
Get the Id associated with this widget. | |||
@see setId | |||
*/ | |||
void setAbsolutePos(int x, int y) noexcept; | |||
uint getId() const noexcept; | |||
/** | |||
Set absolute position. | |||
Set an Id to be associated with this widget. | |||
@see getId | |||
*/ | |||
void setAbsolutePos(const Point<int>& pos) noexcept; | |||
void setId(uint id) noexcept; | |||
/** | |||
Get this widget's window application. | |||
Same as calling getParentWindow().getApp(). | |||
Get the application associated with this widget's window. | |||
This is the same as calling `getTopLevelWidget()->getApp()`. | |||
*/ | |||
Application& getParentApp() const noexcept; | |||
Application& getApp() const noexcept; | |||
/** | |||
Get parent window, as passed in the constructor. | |||
Get the window associated with this widget. | |||
This is the same as calling `getTopLevelWidget()->getWindow()`. | |||
*/ | |||
Window& getParentWindow() const noexcept; | |||
Window& getWindow() const noexcept; | |||
/** | |||
Check if this widget contains the point defined by @a x and @a y. | |||
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 | |||
*/ | |||
bool contains(int x, int y) const noexcept; | |||
const GraphicsContext& getGraphicsContext() const noexcept; | |||
/** | |||
Check if this widget contains the point @a pos. | |||
Get top-level widget, as passed directly in the constructor | |||
or going up the chain of group widgets until it finds the top-level one. | |||
*/ | |||
bool contains(const Point<int>& pos) const noexcept; | |||
TopLevelWidget* getTopLevelWidget() const noexcept; | |||
/** | |||
Tell this widget's window to repaint itself. | |||
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; | |||
/** | |||
Get the Id associated with this widget. | |||
@see setId | |||
*/ | |||
uint getId() const noexcept; | |||
DISTRHO_DEPRECATED_BY("getApp()") | |||
Application& getParentApp() const noexcept { return getApp(); } | |||
/** | |||
Set an Id to be associated with this widget. | |||
@see getId | |||
*/ | |||
void setId(uint id) noexcept; | |||
DISTRHO_DEPRECATED_BY("getWindow()") | |||
Window& getParentWindow() const noexcept { return getWindow(); } | |||
protected: | |||
/** | |||
A function called to draw the view contents with OpenGL. | |||
A function called to draw the widget contents. | |||
*/ | |||
virtual void onDisplay() = 0; | |||
@@ -336,10 +395,10 @@ protected: | |||
virtual bool onKeyboard(const KeyboardEvent&); | |||
/** | |||
A function called when a special key is pressed or released. | |||
A function called when an UTF-8 character is received. | |||
@return True to stop event propagation, false otherwise. | |||
*/ | |||
virtual bool onSpecial(const SpecialEvent&); | |||
virtual bool onCharacterInput(const CharacterInputEvent&); | |||
/** | |||
A function called when a mouse button is pressed or released. | |||
@@ -364,25 +423,34 @@ protected: | |||
*/ | |||
virtual void onResize(const ResizeEvent&); | |||
/** | |||
A function called when a special key is pressed or released. | |||
DEPRECATED use onKeyboard or onCharacterInput | |||
*/ | |||
#if defined(__clang__) | |||
# pragma clang diagnostic push | |||
# pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
#endif | |||
virtual bool onSpecial(const SpecialEvent&) { return false; } | |||
#if defined(__clang__) | |||
# pragma clang diagnostic pop | |||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 | |||
# pragma GCC diagnostic pop | |||
#endif | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
/** @internal */ | |||
explicit Widget(Widget* groupWidget, bool addToSubWidgets); | |||
friend class ImageSlider; | |||
friend class NanoWidget; | |||
friend class Window; | |||
friend class StandaloneWindow; | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
friend class DISTRHO_NAMESPACE::UI; | |||
#endif | |||
friend class SubWidget; | |||
friend class TopLevelWidget; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,128 +19,498 @@ | |||
#include "Geometry.hpp" | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
START_NAMESPACE_DISTRHO | |||
class UIExporter; | |||
END_NAMESPACE_DISTRHO | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# include "../distrho/extra/FileBrowserDialog.hpp" | |||
#endif | |||
START_NAMESPACE_DGL | |||
class Application; | |||
class PluginWindow; | |||
class TopLevelWidget; | |||
// ----------------------------------------------------------------------- | |||
class Application; | |||
class Widget; | |||
class StandaloneWindow; | |||
/** | |||
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 | |||
class Window | |||
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 DISTRHO_API Window | |||
{ | |||
struct PrivateData; | |||
public: | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; | |||
typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; | |||
#endif | |||
/** | |||
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() {} | |||
Window graphics context as a scoped struct. | |||
This class gives graphics context drawing time to a window's widgets. | |||
Typically used for allowing OpenGL drawing operations during a window + widget constructor. | |||
Unless you are subclassing the Window or StandaloneWindow classes, you do not need to care. | |||
In such cases you will need to use this struct as a way to get a valid OpenGL context. | |||
For example in a standalone application: | |||
``` | |||
int main() | |||
{ | |||
Application app; | |||
Window win(app); | |||
ScopedPointer<MyCustomTopLevelWidget> widget; | |||
{ | |||
const Window::ScopedGraphicsContext sgc(win); | |||
widget = new MyCustomTopLevelWidget(win); | |||
} | |||
app.exec(); | |||
return 0; | |||
} | |||
``` | |||
This struct is necessary because we cannot automatically make the window leave the OpenGL context in custom code. | |||
And we must always cleanly enter and leave the OpenGL context. | |||
So in order to avoid messing up the global host context, this class is used around widget creation. | |||
*/ | |||
struct ScopedGraphicsContext | |||
{ | |||
/** Constructor that will make the @a window graphics context the current one */ | |||
explicit ScopedGraphicsContext(Window& window); | |||
/** Overloaded constructor, gives back context to its transient parent when done */ | |||
explicit ScopedGraphicsContext(Window& window, Window& transientParentWindow); | |||
/** Desstructor for clearing current context, if not done yet */ | |||
~ScopedGraphicsContext(); | |||
/** Early context clearing, useful for standalone windows not created by you. */ | |||
void done(); | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopedGraphicsContext) | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
private: | |||
Window& window; | |||
Window::PrivateData* ppData; | |||
bool active; | |||
}; | |||
#endif // DGL_FILE_BROWSER_DISABLED | |||
/** | |||
Constructor for a regular, standalone window. | |||
*/ | |||
explicit Window(Application& app); | |||
explicit Window(Application& app, Window& parent); | |||
explicit Window(Application& app, intptr_t parentId); | |||
/** | |||
Constructor for a modal window, by having another window as its transient parent. | |||
The Application instance must be the same between the 2 windows. | |||
*/ | |||
explicit Window(Application& app, Window& transientParentWindow); | |||
/** | |||
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 scaleFactor, | |||
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, | |||
uint width, | |||
uint height, | |||
double scaleFactor, | |||
bool resizable); | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~Window(); | |||
/** | |||
Whether this Window is embed into another (usually not DGL-controlled) Window. | |||
*/ | |||
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 window visible (or not) according to @a visible. | |||
Only valid for standalones, embed windows are always visible. | |||
@see isVisible(), hide(), show() | |||
*/ | |||
void setVisible(bool visible); | |||
/** | |||
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. | |||
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(); | |||
void exec(bool lockWait = false); | |||
void focus(); | |||
void repaint() noexcept; | |||
/** | |||
Check if this window is resizable (by the user or window manager). | |||
@see setResizable | |||
*/ | |||
bool isResizable() const noexcept; | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
bool openFileBrowser(const FileBrowserOptions& options); | |||
#endif | |||
/** | |||
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); | |||
bool isVisible() const noexcept; | |||
void setVisible(bool yesNo); | |||
/** | |||
Get X offset, typically 0. | |||
*/ | |||
int getOffsetX() const noexcept; | |||
bool isResizable() const noexcept; | |||
void setResizable(bool yesNo); | |||
/** | |||
Get Y offset, typically 0. | |||
*/ | |||
int getOffsetY() const noexcept; | |||
/** | |||
Get offset. | |||
*/ | |||
Point<int> getOffset() const noexcept; | |||
/** | |||
Set X offset. | |||
*/ | |||
void setOffsetX(int x); | |||
/** | |||
Set Y offset. | |||
*/ | |||
void setOffsetY(int y); | |||
/** | |||
Set offset using @a x and @a y values. | |||
*/ | |||
void setOffset(int x, int y); | |||
/** | |||
Set offset. | |||
*/ | |||
void setOffset(const Point<int>& offset); | |||
/** | |||
Get width. | |||
*/ | |||
uint getWidth() const noexcept; | |||
/** | |||
Get height. | |||
*/ | |||
uint getHeight() const noexcept; | |||
/** | |||
Get size. | |||
*/ | |||
Size<uint> 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); | |||
void setSize(Size<uint> size); | |||
/** | |||
Set size. | |||
*/ | |||
void setSize(const Size<uint>& 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); | |||
void setTransientWinId(uintptr_t winId); | |||
/** | |||
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; | |||
/** | |||
Set the clipboard contents. | |||
This sets the system clipboard contents, | |||
which can be retrieved with getClipboard() or pasted into other applications. | |||
If using a string, the use of a null terminator is required (and must be part of dataSize).@n | |||
The MIME type of the data "text/plain" is assumed if null is used. | |||
*/ | |||
bool setClipboard(const char* mimeType, const void* data, size_t dataSize); | |||
/** | |||
Get the clipboard contents. | |||
This gets the system clipboard contents, | |||
which may have been set with setClipboard() or copied from another application. | |||
returns the clipboard contents, or null. | |||
*/ | |||
const void* getClipboard(const char*& mimeType, size_t& dataSize); | |||
/** | |||
Set the mouse cursor. | |||
This changes the system cursor that is displayed when the pointer is inside the window. | |||
May fail if setting the cursor is not supported on this system, | |||
for example if compiled on X11 without Xcursor support. | |||
*/ | |||
bool setCursor(MouseCursor cursor); | |||
/** | |||
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. | |||
*/ | |||
Application& getApp() const noexcept; | |||
intptr_t getWindowId() const noexcept; | |||
void addIdleCallback(IdleCallback* const callback); | |||
void removeIdleCallback(IdleCallback* const callback); | |||
/** | |||
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: | |||
- 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; | |||
/** | |||
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(); | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** | |||
Open a file browser dialog with this window as transient parent. | |||
A few options can be specified to setup the dialog. | |||
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 = FileBrowserOptions()); | |||
#endif | |||
/** | |||
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<uint>& rect) noexcept; | |||
/** | |||
Render this window's content into a picture file, specified by @a filename. | |||
Window must be visible and on screen. | |||
Written picture format is PPM. | |||
*/ | |||
void renderToPicture(const char* filename); | |||
/** | |||
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); | |||
/** | |||
Get the geometry constraints set for the Window. | |||
@see setGeometryConstraints | |||
*/ | |||
Size<uint> getGeometryConstraints(bool& keepAspectRatio); | |||
/** | |||
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, | |||
bool resizeNowIfAutoScaling = true); | |||
/** 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); } | |||
protected: | |||
virtual void onDisplayBefore(); | |||
virtual void onDisplayAfter(); | |||
/** | |||
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. | |||
This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI. | |||
For embed windows, closing is handled by the host/parent process and we have no control over it. | |||
As such, a close action on embed windows will always succeed and cannot be cancelled. | |||
NOTE: This currently does not work under macOS. | |||
*/ | |||
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, CrossingMode mode); | |||
/** | |||
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); | |||
virtual void onClose(); | |||
/** | |||
A function called when scale factor requested for this window changes. | |||
The default implementation does nothing. | |||
WARNING function needs a proper name | |||
*/ | |||
virtual void onScaleFactorChanged(double scaleFactor); | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
virtual void fileBrowserSelected(const char* filename); | |||
/** | |||
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; | |||
friend class Application; | |||
friend class Widget; | |||
friend class StandaloneWindow; | |||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||
friend class DISTRHO_NAMESPACE::UIExporter; | |||
#endif | |||
virtual void _addWidget(Widget* const widget); | |||
virtual void _removeWidget(Widget* const widget); | |||
void _idle(); | |||
friend class PluginWindow; | |||
friend class TopLevelWidget; | |||
bool handlePluginKeyboard(const bool press, const uint key); | |||
bool handlePluginSpecial(const bool press, const Key key); | |||
/** @internal */ | |||
explicit Window(Application& app, | |||
uintptr_t parentWindowHandle, | |||
uint width, | |||
uint height, | |||
double scaleFactor, | |||
bool resizable, | |||
bool isVST3, | |||
bool doPostInit); | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) | |||
}; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -14,19 +14,14 @@ | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifdef __WINE__ | |||
# include "winsock2.h" | |||
#endif | |||
#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() | |||
{ | |||
@@ -35,44 +30,56 @@ Application::~Application() | |||
void Application::idle() | |||
{ | |||
for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it) | |||
{ | |||
Window* const window(*it); | |||
window->_idle(); | |||
} | |||
for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it) | |||
{ | |||
IdleCallback* const idleCallback(*it); | |||
idleCallback->idleCallback(); | |||
} | |||
pData->idle(0); | |||
} | |||
void Application::exec(unsigned int idleTime) | |||
void Application::exec(const uint idleTimeInMs) | |||
{ | |||
for (; pData->doLoop;) | |||
{ | |||
idle(); | |||
d_msleep(idleTime); | |||
} | |||
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); | |||
while (! pData->isQuitting) | |||
pData->idle(idleTimeInMs); | |||
} | |||
void Application::quit() | |||
{ | |||
pData->doLoop = false; | |||
pData->quit(); | |||
} | |||
bool Application::isQuitting() const noexcept | |||
{ | |||
return pData->isQuitting || pData->isQuittingInNextCycle; | |||
} | |||
bool Application::isStandalone() const noexcept | |||
{ | |||
return pData->isStandalone; | |||
} | |||
double Application::getTime() const | |||
{ | |||
return pData->getTime(); | |||
} | |||
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,) | |||
for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit) | |||
{ | |||
Window* const window(*rit); | |||
window->close(); | |||
} | |||
pData->idleCallbacks.remove(callback); | |||
} | |||
bool Application::isQuiting() const noexcept | |||
void Application::setClassName(const char* const name) | |||
{ | |||
return !pData->doLoop; | |||
pData->setClassName(name); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,172 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "ApplicationPrivateData.hpp" | |||
#include "../Window.hpp" | |||
#include "pugl.hpp" | |||
#include <ctime> | |||
START_NAMESPACE_DGL | |||
typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator; | |||
static d_ThreadHandle getCurrentThreadHandle() noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
return GetCurrentThread(); | |||
#else | |||
return pthread_self(); | |||
#endif | |||
} | |||
static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ? | |||
#else | |||
return pthread_equal(getCurrentThreadHandle(), mainThreadHandle) != 0; | |||
#endif | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
Application::PrivateData::PrivateData(const bool standalone) | |||
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||
standalone ? PUGL_WORLD_THREADS : 0x0)), | |||
isStandalone(standalone), | |||
isQuitting(false), | |||
isQuittingInNextCycle(false), | |||
isStarting(true), | |||
visibleWindows(0), | |||
mainThreadHandle(getCurrentThreadHandle()), | |||
windows(), | |||
idleCallbacks() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||
puglSetWorldHandle(world, this); | |||
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | |||
#ifdef DISTRHO_OS_MAC | |||
if (standalone) | |||
puglMacOSActivateApp(); | |||
#endif | |||
} | |||
Application::PrivateData::~PrivateData() | |||
{ | |||
DISTRHO_SAFE_ASSERT(isStarting || isQuitting); | |||
DISTRHO_SAFE_ASSERT(visibleWindows == 0); | |||
windows.clear(); | |||
idleCallbacks.clear(); | |||
if (world != nullptr) | |||
puglFreeWorld(world); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
void Application::PrivateData::oneWindowShown() noexcept | |||
{ | |||
if (++visibleWindows == 1) | |||
{ | |||
isQuitting = false; | |||
isStarting = false; | |||
} | |||
} | |||
void Application::PrivateData::oneWindowClosed() noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,); | |||
if (--visibleWindows == 0) | |||
isQuitting = true; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
void Application::PrivateData::idle(const uint timeoutInMs) | |||
{ | |||
if (isQuittingInNextCycle) | |||
{ | |||
quit(); | |||
isQuittingInNextCycle = false; | |||
} | |||
if (world != nullptr) | |||
{ | |||
const double timeoutInSeconds = timeoutInMs != 0 | |||
? static_cast<double>(timeoutInMs) / 1000.0 | |||
: 0.0; | |||
puglUpdate(world, timeoutInSeconds); | |||
} | |||
triggerIdleCallbacks(); | |||
} | |||
void Application::PrivateData::triggerIdleCallbacks() | |||
{ | |||
for (std::list<IdleCallback*>::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) | |||
{ | |||
IdleCallback* const idleCallback(*it); | |||
idleCallback->idleCallback(); | |||
} | |||
} | |||
void Application::PrivateData::quit() | |||
{ | |||
if (! isThisTheMainThread(mainThreadHandle)) | |||
{ | |||
if (! isQuittingInNextCycle) | |||
{ | |||
isQuittingInNextCycle = true; | |||
return; | |||
} | |||
} | |||
isQuitting = true; | |||
#ifndef DPF_TEST_APPLICATION_CPP | |||
for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) | |||
{ | |||
DGL_NAMESPACE::Window* const window(*rit); | |||
window->close(); | |||
} | |||
#endif | |||
} | |||
double Application::PrivateData::getTime() const | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, 0.0); | |||
return puglGetTime(world); | |||
} | |||
void Application::PrivateData::setClassName(const char* const name) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |||
puglSetClassName(world, name); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,53 +18,98 @@ | |||
#define DGL_APP_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../Application.hpp" | |||
#include "../../distrho/extra/Sleep.hpp" | |||
#include <list> | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
typedef HANDLE d_ThreadHandle; | |||
#else | |||
# include <pthread.h> | |||
typedef pthread_t d_ThreadHandle; | |||
#endif | |||
#ifdef DISTRHO_OS_MAC | |||
typedef struct PuglWorldImpl PuglWorld; | |||
#endif | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
class Window; | |||
#ifndef DISTRHO_OS_MAC | |||
typedef struct PuglWorldImpl PuglWorld; | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct Application::PrivateData { | |||
bool doLoop; | |||
/** 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 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; | |||
/** Counter of visible windows, only used in standalone mode. | |||
If 0->1, application is starting. If 1->0, application is quitting/stopping. */ | |||
uint visibleWindows; | |||
std::list<Window*> windows; | |||
std::list<IdleCallback*> idleCallbacks; | |||
PrivateData() | |||
: doLoop(true), | |||
visibleWindows(0), | |||
windows(), | |||
idleCallbacks() {} | |||
~PrivateData() | |||
{ | |||
DISTRHO_SAFE_ASSERT(! doLoop); | |||
DISTRHO_SAFE_ASSERT(visibleWindows == 0); | |||
windows.clear(); | |||
idleCallbacks.clear(); | |||
} | |||
void oneShown() noexcept | |||
{ | |||
if (++visibleWindows == 1) | |||
doLoop = true; | |||
} | |||
void oneHidden() noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,); | |||
if (--visibleWindows == 0) | |||
doLoop = false; | |||
} | |||
DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||
/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */ | |||
d_ThreadHandle mainThreadHandle; | |||
/** List of windows for this application. Only used during `close`. */ | |||
std::list<DGL_NAMESPACE::Window*> windows; | |||
/** List of idle callbacks for this application. */ | |||
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | |||
/** Constructor and destructor */ | |||
explicit PrivateData(bool standalone); | |||
~PrivateData(); | |||
/** 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; | |||
/** 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(uint timeoutInMs); | |||
/** Run each idle callback without updating pugl world. */ | |||
void triggerIdleCallbacks(); | |||
/** Set flag indicating application is quitting, and close all windows in reverse order of registration. | |||
For standalone mode only. */ | |||
void quit(); | |||
/** Get time via pugl */ | |||
double getTime() const; | |||
/** Set pugl world class name. */ | |||
void setClassName(const char* name); | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -16,12 +16,23 @@ | |||
#include "../Color.hpp" | |||
#include "nanovg/nanovg.h" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
static float computeHue(float h, float m1, float m2) | |||
{ | |||
if (h < 0) h += 1; | |||
if (h > 1) h -= 1; | |||
if (h < 1.0f/6.0f) | |||
return m1 + (m2 - m1) * h * 6.0f; | |||
if (h < 3.0f/6.0f) | |||
return m2; | |||
if (h < 4.0f/6.0f) | |||
return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f; | |||
return m1; | |||
} | |||
static void fixRange(float& value) | |||
{ | |||
/**/ if (value < 0.0f) | |||
@@ -46,27 +57,27 @@ static uchar getFixedRange2(const float& value) | |||
return 0; | |||
if (value2 >= 255.0f) | |||
return 255; | |||
return static_cast<uchar>(value2); | |||
return static_cast<uchar>(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<float>(r)/255.0f), | |||
green(static_cast<float>(g)/255.0f), | |||
blue(static_cast<float>(b)/255.0f), | |||
alpha(static_cast<float>(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), | |||
@@ -94,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), | |||
@@ -103,70 +114,91 @@ Color::Color(const Color& color1, const Color& color2, float u) noexcept | |||
interpolate(color2, u); | |||
} | |||
Color Color::withAlpha(const float alpha2) noexcept | |||
{ | |||
Color color(*this); | |||
color.alpha = alpha2; | |||
return color; | |||
} | |||
Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | |||
{ | |||
return nvgHSLA(hue, saturation, lightness, static_cast<uchar>(getFixedRange(alpha)*255.0f)); | |||
float m1, m2; | |||
Color col; | |||
hue = fmodf(hue, 1.0f); | |||
if (hue < 0.0f) hue += 1.0f; | |||
fixRange(saturation); | |||
fixRange(lightness); | |||
m2 = lightness <= 0.5f ? (lightness * (1 + saturation)) : (lightness + saturation - lightness * saturation); | |||
m1 = 2 * lightness - m2; | |||
col.red = computeHue(hue + 1.0f/3.0f, m1, m2); | |||
col.green = computeHue(hue, m1, m2); | |||
col.blue = computeHue(hue - 1.0f/3.0f, m1, m2); | |||
col.alpha = alpha; | |||
col.fixBounds(); | |||
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<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
rgbtmp[2] = rgb[0]; | |||
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17; | |||
rgbtmp[0] = rgb[1]; | |||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
rgbtmp[2] = rgb[1]; | |||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17; | |||
rgbtmp[0] = rgb[2]; | |||
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
rgbtmp[2] = rgb[2]; | |||
b = static_cast<int>(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<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
rgbtmp[0] = rgb[2]; | |||
rgbtmp[1] = rgb[3]; | |||
rgbtmp[2] = rgb[2]; | |||
rgbtmp[3] = rgb[3]; | |||
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
rgbtmp[0] = rgb[4]; | |||
rgbtmp[1] = rgb[5]; | |||
rgbtmp[2] = rgb[4]; | |||
rgbtmp[3] = rgb[5]; | |||
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)); | |||
} | |||
return Color(r, g, b, static_cast<int>(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]); | |||
@@ -184,7 +216,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]); | |||
@@ -224,22 +256,4 @@ void Color::fixBounds() noexcept | |||
// ----------------------------------------------------------------------- | |||
Color::Color(const NVGcolor& c) noexcept | |||
: red(c.r), green(c.g), blue(c.b), alpha(c.a) | |||
{ | |||
fixBounds(); | |||
} | |||
Color::operator NVGcolor() const noexcept | |||
{ | |||
NVGcolor nc; | |||
nc.r = red; | |||
nc.g = green; | |||
nc.b = blue; | |||
nc.a = alpha; | |||
return nc; | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,124 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_COMMON_HPP_INCLUDED | |||
#define DGL_COMMON_HPP_INCLUDED | |||
#include "../ImageWidgets.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
struct ButtonImpl { | |||
enum State { | |||
kStateNormal = 0, | |||
kStateHover, | |||
kStateDown | |||
}; | |||
int button; | |||
int state; | |||
Widget* self; | |||
ImageButton::Callback* callback_img; | |||
ButtonImpl(Widget* const s) noexcept | |||
: button(-1), | |||
state(kStateNormal), | |||
self(s), | |||
callback_img(nullptr) {} | |||
bool onMouse(const Widget::MouseEvent& ev) | |||
{ | |||
// button was released, handle it now | |||
if (button != -1 && ! ev.press) | |||
{ | |||
DISTRHO_SAFE_ASSERT(state == kStateDown); | |||
// release button | |||
const int button2 = button; | |||
button = -1; | |||
// cursor was moved outside the button bounds, ignore click | |||
if (! self->contains(ev.pos)) | |||
{ | |||
state = kStateNormal; | |||
self->repaint(); | |||
return true; | |||
} | |||
// still on bounds, register click | |||
state = kStateHover; | |||
self->repaint(); | |||
if (callback_img != nullptr) | |||
callback_img->imageButtonClicked((ImageButton*)self, button2); | |||
return true; | |||
} | |||
// button was pressed, wait for release | |||
if (ev.press && self->contains(ev.pos)) | |||
{ | |||
button = ev.button; | |||
state = kStateDown; | |||
self->repaint(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool onMotion(const Widget::MotionEvent& ev) | |||
{ | |||
// keep pressed | |||
if (button != -1) | |||
return true; | |||
if (self->contains(ev.pos)) | |||
{ | |||
// check if entering hover | |||
if (state == kStateNormal) | |||
{ | |||
state = kStateHover; | |||
self->repaint(); | |||
return true; | |||
} | |||
} | |||
else | |||
{ | |||
// check if exiting hover | |||
if (state == kStateHover) | |||
{ | |||
state = kStateNormal; | |||
self->repaint(); | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED |
@@ -0,0 +1,632 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "../EventHandlers.hpp" | |||
#include "../SubWidget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct ButtonEventHandler::PrivateData { | |||
ButtonEventHandler* const self; | |||
SubWidget* const widget; | |||
ButtonEventHandler::Callback* internalCallback; | |||
ButtonEventHandler::Callback* userCallback; | |||
int button; | |||
int state; | |||
bool checkable; | |||
bool checked; | |||
Point<double> lastClickPos; | |||
Point<double> lastMotionPos; | |||
PrivateData(ButtonEventHandler* const s, SubWidget* const w) | |||
: self(s), | |||
widget(w), | |||
internalCallback(nullptr), | |||
userCallback(nullptr), | |||
button(-1), | |||
state(kButtonStateDefault), | |||
checkable(false), | |||
checked(false), | |||
lastClickPos(0, 0), | |||
lastMotionPos(0, 0) {} | |||
bool mouseEvent(const Widget::MouseEvent& ev) | |||
{ | |||
lastClickPos = ev.pos; | |||
// button was released, handle it now | |||
if (button != -1 && ! ev.press) | |||
{ | |||
DISTRHO_SAFE_ASSERT(state & kButtonStateActive); | |||
// release button | |||
const int button2 = button; | |||
button = -1; | |||
const int state2 = state; | |||
state &= ~kButtonStateActive; | |||
self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); | |||
widget->repaint(); | |||
// cursor was moved outside the button bounds, ignore click | |||
if (! widget->contains(ev.pos)) | |||
return true; | |||
// still on bounds, register click | |||
if (checkable) | |||
checked = !checked; | |||
if (internalCallback != nullptr) | |||
internalCallback->buttonClicked(widget, button2); | |||
else if (userCallback != nullptr) | |||
userCallback->buttonClicked(widget, button2); | |||
return true; | |||
} | |||
// button was pressed, wait for release | |||
if (ev.press && widget->contains(ev.pos)) | |||
{ | |||
const int state2 = state; | |||
button = static_cast<int>(ev.button); | |||
state |= kButtonStateActive; | |||
self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); | |||
widget->repaint(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool motionEvent(const Widget::MotionEvent& ev) | |||
{ | |||
// keep pressed | |||
if (button != -1) | |||
{ | |||
lastMotionPos = ev.pos; | |||
return true; | |||
} | |||
bool ret = false; | |||
if (widget->contains(ev.pos)) | |||
{ | |||
// check if entering hover | |||
if ((state & kButtonStateHover) == 0x0) | |||
{ | |||
const int state2 = state; | |||
state |= kButtonStateHover; | |||
ret = widget->contains(lastMotionPos); | |||
self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); | |||
widget->repaint(); | |||
} | |||
} | |||
else | |||
{ | |||
// check if exiting hover | |||
if (state & kButtonStateHover) | |||
{ | |||
const int state2 = state; | |||
state &= ~kButtonStateHover; | |||
ret = widget->contains(lastMotionPos); | |||
self->stateChanged(static_cast<State>(state), static_cast<State>(state2)); | |||
widget->repaint(); | |||
} | |||
} | |||
lastMotionPos = ev.pos; | |||
return ret; | |||
} | |||
void setActive(const bool active2, const bool sendCallback) noexcept | |||
{ | |||
const bool active = state & kButtonStateActive; | |||
if (active == active2) | |||
return; | |||
state |= kButtonStateActive; | |||
widget->repaint(); | |||
if (sendCallback) | |||
{ | |||
if (internalCallback != nullptr) | |||
internalCallback->buttonClicked(widget, -1); | |||
else if (userCallback != nullptr) | |||
userCallback->buttonClicked(widget, -1); | |||
} | |||
} | |||
void setChecked(const bool checked2, const bool sendCallback) noexcept | |||
{ | |||
if (checked == checked2) | |||
return; | |||
checked = checked2; | |||
widget->repaint(); | |||
if (sendCallback) | |||
{ | |||
if (internalCallback != nullptr) | |||
internalCallback->buttonClicked(widget, -1); | |||
else if (userCallback != nullptr) | |||
userCallback->buttonClicked(widget, -1); | |||
} | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
ButtonEventHandler::ButtonEventHandler(SubWidget* const self) | |||
: pData(new PrivateData(this, self)) {} | |||
ButtonEventHandler::~ButtonEventHandler() | |||
{ | |||
delete pData; | |||
} | |||
bool ButtonEventHandler::isActive() noexcept | |||
{ | |||
return pData->state & kButtonStateActive; | |||
} | |||
void ButtonEventHandler::setActive(const bool active, const bool sendCallback) noexcept | |||
{ | |||
pData->setActive(active, sendCallback); | |||
} | |||
bool ButtonEventHandler::isChecked() const noexcept | |||
{ | |||
return pData->checked; | |||
} | |||
void ButtonEventHandler::setChecked(const bool checked, const bool sendCallback) noexcept | |||
{ | |||
pData->setChecked(checked, sendCallback); | |||
} | |||
bool ButtonEventHandler::isCheckable() const noexcept | |||
{ | |||
return pData->checkable; | |||
} | |||
void ButtonEventHandler::setCheckable(const bool checkable) noexcept | |||
{ | |||
if (pData->checkable == checkable) | |||
return; | |||
pData->checkable = checkable; | |||
} | |||
Point<double> ButtonEventHandler::getLastClickPosition() const noexcept | |||
{ | |||
return pData->lastClickPos; | |||
} | |||
Point<double> ButtonEventHandler::getLastMotionPosition() const noexcept | |||
{ | |||
return pData->lastMotionPos; | |||
} | |||
void ButtonEventHandler::setCallback(Callback* const callback) noexcept | |||
{ | |||
pData->userCallback = callback; | |||
} | |||
bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev) | |||
{ | |||
return pData->mouseEvent(ev); | |||
} | |||
bool ButtonEventHandler::motionEvent(const Widget::MotionEvent& ev) | |||
{ | |||
return pData->motionEvent(ev); | |||
} | |||
ButtonEventHandler::State ButtonEventHandler::getState() const noexcept | |||
{ | |||
return static_cast<State>(pData->state); | |||
} | |||
void ButtonEventHandler::clearState() noexcept | |||
{ | |||
pData->state = kButtonStateDefault; | |||
} | |||
void ButtonEventHandler::stateChanged(State, State) | |||
{ | |||
} | |||
void ButtonEventHandler::setInternalCallback(Callback* const callback) noexcept | |||
{ | |||
pData->internalCallback = callback; | |||
} | |||
void ButtonEventHandler::triggerUserCallback(SubWidget* const widget, const int button) | |||
{ | |||
if (pData->userCallback != nullptr) | |||
pData->userCallback->buttonClicked(widget, button); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct KnobEventHandler::PrivateData { | |||
KnobEventHandler* const self; | |||
SubWidget* const widget; | |||
KnobEventHandler::Callback* callback; | |||
float minimum; | |||
float maximum; | |||
float step; | |||
float value; | |||
float valueDef; | |||
float valueTmp; | |||
bool usingDefault; | |||
bool usingLog; | |||
Orientation orientation; | |||
int state; | |||
double lastX; | |||
double lastY; | |||
PrivateData(KnobEventHandler* const s, SubWidget* const w) | |||
: self(s), | |||
widget(w), | |||
callback(nullptr), | |||
minimum(0.0f), | |||
maximum(1.0f), | |||
step(0.0f), | |||
value(0.5f), | |||
valueDef(value), | |||
valueTmp(value), | |||
usingDefault(false), | |||
usingLog(false), | |||
orientation(Vertical), | |||
state(kKnobStateDefault), | |||
lastX(0.0), | |||
lastY(0.0) {} | |||
PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) | |||
: self(s), | |||
widget(w), | |||
callback(other->callback), | |||
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), | |||
state(kKnobStateDefault), | |||
lastX(0.0), | |||
lastY(0.0) {} | |||
void assignFrom(PrivateData* const other) | |||
{ | |||
callback = other->callback; | |||
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; | |||
state = kKnobStateDefault; | |||
lastX = 0.0; | |||
lastY = 0.0; | |||
} | |||
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*v); | |||
} | |||
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(v/a)/b; | |||
} | |||
bool mouseEvent(const Widget::MouseEvent& ev) | |||
{ | |||
if (ev.button != 1) | |||
return false; | |||
if (ev.press) | |||
{ | |||
if (! widget->contains(ev.pos)) | |||
return false; | |||
if ((ev.mod & kModifierShift) != 0 && usingDefault) | |||
{ | |||
setValue(valueDef, true); | |||
valueTmp = value; | |||
return true; | |||
} | |||
state |= kKnobStateDragging; | |||
lastX = ev.pos.getX(); | |||
lastY = ev.pos.getY(); | |||
widget->repaint(); | |||
if (callback != nullptr) | |||
callback->knobDragStarted(widget); | |||
return true; | |||
} | |||
else if (state & kKnobStateDragging) | |||
{ | |||
state &= ~kKnobStateDragging; | |||
widget->repaint(); | |||
if (callback != nullptr) | |||
callback->knobDragFinished(widget); | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool motionEvent(const Widget::MotionEvent& ev) | |||
{ | |||
if ((state & kKnobStateDragging) == 0x0) | |||
return false; | |||
bool doVal = false; | |||
float d, value2 = 0.0f; | |||
if (orientation == Horizontal) | |||
{ | |||
if (const double movX = ev.pos.getX() - lastX) | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX)); | |||
doVal = true; | |||
} | |||
} | |||
else if (orientation == Vertical) | |||
{ | |||
if (const double movY = lastY - ev.pos.getY()) | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY)); | |||
doVal = true; | |||
} | |||
} | |||
if (! doVal) | |||
return false; | |||
if (usingLog) | |||
value2 = logscale(value2); | |||
if (value2 < minimum) | |||
{ | |||
valueTmp = value2 = minimum; | |||
} | |||
else if (value2 > maximum) | |||
{ | |||
valueTmp = value2 = maximum; | |||
} | |||
else | |||
{ | |||
valueTmp = value2; | |||
if (d_isNotZero(step)) | |||
{ | |||
const float rest = std::fmod(value2, step); | |||
value2 -= rest + (rest > step/2.0f ? step : 0.0f); | |||
} | |||
} | |||
setValue(value2, true); | |||
lastX = ev.pos.getX(); | |||
lastY = ev.pos.getY(); | |||
return true; | |||
} | |||
bool scrollEvent(const Widget::ScrollEvent& ev) | |||
{ | |||
if (! widget->contains(ev.pos)) | |||
return false; | |||
const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; | |||
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) | |||
+ ((maximum - minimum) / d * 10.f * dir); | |||
if (usingLog) | |||
value2 = logscale(value2); | |||
if (value2 < minimum) | |||
{ | |||
valueTmp = value2 = minimum; | |||
} | |||
else if (value2 > maximum) | |||
{ | |||
valueTmp = value2 = maximum; | |||
} | |||
else | |||
{ | |||
valueTmp = value2; | |||
if (d_isNotZero(step)) | |||
{ | |||
const float rest = std::fmod(value2, step); | |||
value2 = value2 - rest + (rest > step/2.0f ? step : 0.0f); | |||
} | |||
} | |||
setValue(value2, true); | |||
return true; | |||
} | |||
float getNormalizedValue() const noexcept | |||
{ | |||
const float diff = maximum - minimum; | |||
return ((usingLog ? invlogscale(value) : value) - minimum) / diff; | |||
} | |||
void setRange(const float min, const float max) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(max > min,); | |||
if (value < min) | |||
{ | |||
valueTmp = value = min; | |||
widget->repaint(); | |||
} | |||
else if (value > max) | |||
{ | |||
valueTmp = value = max; | |||
widget->repaint(); | |||
} | |||
minimum = min; | |||
maximum = max; | |||
} | |||
bool setValue(const float value2, const bool sendCallback) | |||
{ | |||
if (d_isEqual(value, value2)) | |||
return false; | |||
valueTmp = value = value2; | |||
widget->repaint(); | |||
if (sendCallback && callback != nullptr) | |||
{ | |||
try { | |||
callback->knobValueChanged(widget, value); | |||
} DISTRHO_SAFE_EXCEPTION("KnobEventHandler::setValue"); | |||
} | |||
return true; | |||
} | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
KnobEventHandler::KnobEventHandler(SubWidget* const self) | |||
: pData(new PrivateData(this, self)) {} | |||
KnobEventHandler::KnobEventHandler(SubWidget* const self, const KnobEventHandler& other) | |||
: pData(new PrivateData(this, self, other.pData)) {} | |||
KnobEventHandler& KnobEventHandler::operator=(const KnobEventHandler& other) | |||
{ | |||
pData->assignFrom(other.pData); | |||
return *this; | |||
} | |||
KnobEventHandler::~KnobEventHandler() | |||
{ | |||
delete pData; | |||
} | |||
float KnobEventHandler::getValue() const noexcept | |||
{ | |||
return pData->value; | |||
} | |||
bool KnobEventHandler::setValue(const float value, const bool sendCallback) noexcept | |||
{ | |||
return pData->setValue(value, sendCallback); | |||
} | |||
float KnobEventHandler::getNormalizedValue() const noexcept | |||
{ | |||
return pData->getNormalizedValue(); | |||
} | |||
void KnobEventHandler::setDefault(const float def) noexcept | |||
{ | |||
pData->valueDef = def; | |||
pData->usingDefault = true; | |||
} | |||
void KnobEventHandler::setRange(const float min, const float max) noexcept | |||
{ | |||
pData->setRange(min, max); | |||
} | |||
void KnobEventHandler::setStep(const float step) noexcept | |||
{ | |||
pData->step = step; | |||
} | |||
void KnobEventHandler::setUsingLogScale(const bool yesNo) noexcept | |||
{ | |||
pData->usingLog = yesNo; | |||
} | |||
KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept | |||
{ | |||
return pData->orientation; | |||
} | |||
void KnobEventHandler::setOrientation(const Orientation orientation) noexcept | |||
{ | |||
if (pData->orientation == orientation) | |||
return; | |||
pData->orientation = orientation; | |||
} | |||
void KnobEventHandler::setCallback(Callback* const callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev) | |||
{ | |||
return pData->mouseEvent(ev); | |||
} | |||
bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev) | |||
{ | |||
return pData->motionEvent(ev); | |||
} | |||
bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev) | |||
{ | |||
return pData->scrollEvent(ev); | |||
} | |||
KnobEventHandler::State KnobEventHandler::getState() const noexcept | |||
{ | |||
return static_cast<State>(pData->state); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,194 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "../Image.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
Image::Image() | |||
: fRawData(nullptr), | |||
fSize(0, 0), | |||
fFormat(0), | |||
fType(0), | |||
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) | |||
: fRawData(rawData), | |||
fSize(width, height), | |||
fFormat(format), | |||
fType(type), | |||
fTextureId(0), | |||
fIsReady(false) | |||
{ | |||
glGenTextures(1, &fTextureId); | |||
} | |||
Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) | |||
: fRawData(rawData), | |||
fSize(size), | |||
fFormat(format), | |||
fType(type), | |||
fTextureId(0), | |||
fIsReady(false) | |||
{ | |||
glGenTextures(1, &fTextureId); | |||
} | |||
Image::Image(const Image& image) | |||
: fRawData(image.fRawData), | |||
fSize(image.fSize), | |||
fFormat(image.fFormat), | |||
fType(image.fType), | |||
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<uint>(width, height), format, type); | |||
} | |||
void Image::loadFromMemory(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type) noexcept | |||
{ | |||
fRawData = rawData; | |||
fSize = size; | |||
fFormat = format; | |||
fType = type; | |||
fIsReady = false; | |||
} | |||
bool Image::isValid() const noexcept | |||
{ | |||
return (fRawData != nullptr && fSize.getWidth() > 0 && fSize.getHeight() > 0); | |||
} | |||
uint Image::getWidth() const noexcept | |||
{ | |||
return fSize.getWidth(); | |||
} | |||
uint Image::getHeight() const noexcept | |||
{ | |||
return fSize.getHeight(); | |||
} | |||
const Size<uint>& Image::getSize() const noexcept | |||
{ | |||
return fSize; | |||
} | |||
const char* Image::getRawData() const noexcept | |||
{ | |||
return fRawData; | |||
} | |||
GLenum Image::getFormat() const noexcept | |||
{ | |||
return fFormat; | |||
} | |||
GLenum Image::getType() const noexcept | |||
{ | |||
return fType; | |||
} | |||
void Image::draw() | |||
{ | |||
drawAt(0, 0); | |||
} | |||
void Image::drawAt(const int x, const int y) | |||
{ | |||
drawAt(Point<int>(x, y)); | |||
} | |||
void Image::drawAt(const Point<int>& 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<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0, | |||
fFormat, fType, fRawData); | |||
fIsReady = true; | |||
} | |||
Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw(); | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
glDisable(GL_TEXTURE_2D); | |||
} | |||
// ----------------------------------------------------------------------- | |||
Image& Image::operator=(const Image& image) noexcept | |||
{ | |||
fRawData = image.fRawData; | |||
fSize = image.fSize; | |||
fFormat = image.fFormat; | |||
fType = image.fType; | |||
fIsReady = false; | |||
return *this; | |||
} | |||
bool Image::operator==(const Image& image) const noexcept | |||
{ | |||
return (fRawData == image.fRawData && fSize == image.fSize); | |||
} | |||
bool Image::operator!=(const Image& image) const noexcept | |||
{ | |||
return !operator==(image); | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,132 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "../ImageBase.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// protected constructors | |||
ImageBase::ImageBase() | |||
: rawData(nullptr), | |||
size(0, 0), | |||
format(kImageFormatNull) {} | |||
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) | |||
: rawData(rdata), | |||
size(width, height), | |||
format(fmt) {} | |||
ImageBase::ImageBase(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||
: rawData(rdata), | |||
size(s), | |||
format(fmt) {} | |||
ImageBase::ImageBase(const ImageBase& image) | |||
: rawData(image.rawData), | |||
size(image.size), | |||
format(image.format) {} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// public methods | |||
ImageBase::~ImageBase() {} | |||
bool ImageBase::isValid() const noexcept | |||
{ | |||
return (rawData != nullptr && size.isValid()); | |||
} | |||
bool ImageBase::isInvalid() const noexcept | |||
{ | |||
return (rawData == nullptr || size.isInvalid()); | |||
} | |||
uint ImageBase::getWidth() const noexcept | |||
{ | |||
return size.getWidth(); | |||
} | |||
uint ImageBase::getHeight() const noexcept | |||
{ | |||
return size.getHeight(); | |||
} | |||
const Size<uint>& ImageBase::getSize() const noexcept | |||
{ | |||
return size; | |||
} | |||
const char* ImageBase::getRawData() const noexcept | |||
{ | |||
return rawData; | |||
} | |||
ImageFormat ImageBase::getFormat() const noexcept | |||
{ | |||
return format; | |||
} | |||
void ImageBase::loadFromMemory(const char* const rdata, | |||
const uint width, | |||
const uint height, | |||
const ImageFormat fmt) noexcept | |||
{ | |||
loadFromMemory(rdata, Size<uint>(width, height), fmt); | |||
} | |||
void ImageBase::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||
{ | |||
rawData = rdata; | |||
size = s; | |||
format = fmt; | |||
} | |||
void ImageBase::draw(const GraphicsContext& context) | |||
{ | |||
drawAt(context, Point<int>(0, 0)); | |||
} | |||
void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y) | |||
{ | |||
drawAt(context, Point<int>(x, y)); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// public operators | |||
ImageBase& ImageBase::operator=(const ImageBase& image) noexcept | |||
{ | |||
rawData = image.rawData; | |||
size = image.size; | |||
format = image.format; | |||
return *this; | |||
} | |||
bool ImageBase::operator==(const ImageBase& image) const noexcept | |||
{ | |||
return (rawData == image.rawData && size == image.size && format == image.format); | |||
} | |||
bool ImageBase::operator!=(const ImageBase& image) const noexcept | |||
{ | |||
return !operator==(image); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,928 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "../ImageBaseWidgets.hpp" | |||
#include "../Color.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image) | |||
: StandaloneWindow(transientParentWindow.getApp(), transientParentWindow), | |||
img(image) | |||
{ | |||
setResizable(false); | |||
setTitle("About"); | |||
if (image.isValid()) | |||
{ | |||
setSize(image.getSize()); | |||
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |||
} | |||
done(); | |||
} | |||
template <class ImageType> | |||
ImageBaseAboutWindow<ImageType>::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image) | |||
: StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()), | |||
img(image) | |||
{ | |||
setResizable(false); | |||
setTitle("About"); | |||
if (image.isValid()) | |||
{ | |||
setSize(image.getSize()); | |||
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |||
} | |||
done(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseAboutWindow<ImageType>::setImage(const ImageType& image) | |||
{ | |||
if (img == image) | |||
return; | |||
img = image; | |||
if (image.isInvalid()) | |||
return; | |||
setSize(image.getSize()); | |||
setGeometryConstraints(image.getWidth(), image.getHeight(), true, true); | |||
} | |||
template <class ImageType> | |||
void ImageBaseAboutWindow<ImageType>::onDisplay() | |||
{ | |||
img.draw(getGraphicsContext()); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseAboutWindow<ImageType>::onKeyboard(const KeyboardEvent& ev) | |||
{ | |||
if (ev.press && ev.key == kKeyEscape) | |||
{ | |||
close(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
template <class ImageType> | |||
bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (ev.press) | |||
{ | |||
close(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { | |||
ImageBaseButton<ImageType>::Callback* callback; | |||
ImageType imageNormal; | |||
ImageType imageHover; | |||
ImageType imageDown; | |||
PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down) | |||
: callback(nullptr), | |||
imageNormal(normal), | |||
imageHover(hover), | |||
imageDown(down) {} | |||
void buttonClicked(SubWidget* widget, int button) override | |||
{ | |||
if (callback != nullptr) | |||
if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget)) | |||
callback->imageButtonClicked(imageButton, button); | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) | |||
: SubWidget(parentWidget), | |||
ButtonEventHandler(this), | |||
pData(new PrivateData(image, image, image)) | |||
{ | |||
ButtonEventHandler::setCallback(pData); | |||
setSize(image.getSize()); | |||
} | |||
template <class ImageType> | |||
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) | |||
: SubWidget(parentWidget), | |||
ButtonEventHandler(this), | |||
pData(new PrivateData(imageNormal, imageNormal, imageDown)) | |||
{ | |||
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |||
ButtonEventHandler::setCallback(pData); | |||
setSize(imageNormal.getSize()); | |||
} | |||
template <class ImageType> | |||
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) | |||
: SubWidget(parentWidget), | |||
ButtonEventHandler(this), | |||
pData(new PrivateData(imageNormal, imageHover, imageDown)) | |||
{ | |||
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); | |||
ButtonEventHandler::setCallback(pData); | |||
setSize(imageNormal.getSize()); | |||
} | |||
template <class ImageType> | |||
ImageBaseButton<ImageType>::~ImageBaseButton() | |||
{ | |||
delete pData; | |||
} | |||
template <class ImageType> | |||
void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
void ImageBaseButton<ImageType>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
const State state = ButtonEventHandler::getState(); | |||
if (state & kButtonStateActive) | |||
pData->imageDown.draw(context); | |||
else if (state & kButtonStateHover) | |||
pData->imageHover.draw(context); | |||
else | |||
pData->imageNormal.draw(context); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (SubWidget::onMouse(ev)) | |||
return true; | |||
return ButtonEventHandler::mouseEvent(ev); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |||
{ | |||
if (SubWidget::onMotion(ev)) | |||
return true; | |||
return ButtonEventHandler::motionEvent(ev); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback { | |||
ImageBaseKnob<ImageType>::Callback* callback; | |||
ImageType image; | |||
int rotationAngle; | |||
bool alwaysRepaint; | |||
bool isImgVertical; | |||
uint imgLayerWidth; | |||
uint imgLayerHeight; | |||
uint imgLayerCount; | |||
bool isReady; | |||
union { | |||
uint glTextureId; | |||
void* cairoSurface; | |||
}; | |||
explicit PrivateData(const ImageType& img) | |||
: callback(nullptr), | |||
image(img), | |||
rotationAngle(0), | |||
alwaysRepaint(false), | |||
isImgVertical(img.getHeight() > img.getWidth()), | |||
imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), | |||
imgLayerHeight(imgLayerWidth), | |||
imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), | |||
isReady(false) | |||
{ | |||
init(); | |||
} | |||
explicit PrivateData(PrivateData* const other) | |||
: callback(other->callback), | |||
image(other->image), | |||
rotationAngle(other->rotationAngle), | |||
alwaysRepaint(other->alwaysRepaint), | |||
isImgVertical(other->isImgVertical), | |||
imgLayerWidth(other->imgLayerWidth), | |||
imgLayerHeight(other->imgLayerHeight), | |||
imgLayerCount(other->imgLayerCount), | |||
isReady(false) | |||
{ | |||
init(); | |||
} | |||
void assignFrom(PrivateData* const other) | |||
{ | |||
cleanup(); | |||
image = other->image; | |||
rotationAngle = other->rotationAngle; | |||
callback = other->callback; | |||
alwaysRepaint = other->alwaysRepaint; | |||
isImgVertical = other->isImgVertical; | |||
imgLayerWidth = other->imgLayerWidth; | |||
imgLayerHeight = other->imgLayerHeight; | |||
imgLayerCount = other->imgLayerCount; | |||
isReady = false; | |||
init(); | |||
} | |||
~PrivateData() | |||
{ | |||
cleanup(); | |||
} | |||
void knobDragStarted(SubWidget* const widget) override | |||
{ | |||
if (callback != nullptr) | |||
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |||
callback->imageKnobDragStarted(imageKnob); | |||
} | |||
void knobDragFinished(SubWidget* const widget) override | |||
{ | |||
if (callback != nullptr) | |||
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |||
callback->imageKnobDragFinished(imageKnob); | |||
} | |||
void knobValueChanged(SubWidget* const widget, const float value) override | |||
{ | |||
if (rotationAngle == 0 || alwaysRepaint) | |||
isReady = false; | |||
if (callback != nullptr) | |||
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget)) | |||
callback->imageKnobValueChanged(imageKnob, value); | |||
} | |||
// implemented independently per graphics backend | |||
void init(); | |||
void cleanup(); | |||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, | |||
const ImageType& image, | |||
const Orientation orientation) noexcept | |||
: SubWidget(parentWidget), | |||
KnobEventHandler(this), | |||
pData(new PrivateData(image)) | |||
{ | |||
KnobEventHandler::setCallback(pData); | |||
setOrientation(orientation); | |||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |||
} | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | |||
: SubWidget(imageKnob.getParentWidget()), | |||
KnobEventHandler(this, imageKnob), | |||
pData(new PrivateData(imageKnob.pData)) | |||
{ | |||
KnobEventHandler::setCallback(pData); | |||
setOrientation(imageKnob.getOrientation()); | |||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |||
} | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | |||
{ | |||
KnobEventHandler::operator=(imageKnob); | |||
pData->assignFrom(imageKnob.pData); | |||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |||
return *this; | |||
} | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>::~ImageBaseKnob() | |||
{ | |||
delete pData; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(count > 1,); | |||
pData->imgLayerCount = count; | |||
if (pData->isImgVertical) | |||
pData->imgLayerHeight = pData->image.getHeight()/count; | |||
else | |||
pData->imgLayerWidth = pData->image.getWidth()/count; | |||
setSize(pData->imgLayerWidth, pData->imgLayerHeight); | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setRotationAngle(int angle) | |||
{ | |||
if (pData->rotationAngle == angle) | |||
return; | |||
pData->rotationAngle = angle; | |||
pData->isReady = false; | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept | |||
{ | |||
if (KnobEventHandler::setValue(value, sendCallback)) | |||
{ | |||
if (pData->rotationAngle == 0 || pData->alwaysRepaint) | |||
pData->isReady = false; | |||
return true; | |||
} | |||
return false; | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (SubWidget::onMouse(ev)) | |||
return true; | |||
return KnobEventHandler::mouseEvent(ev); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | |||
{ | |||
if (SubWidget::onMotion(ev)) | |||
return true; | |||
return KnobEventHandler::motionEvent(ev); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev) | |||
{ | |||
if (SubWidget::onScroll(ev)) | |||
return true; | |||
return KnobEventHandler::scrollEvent(ev); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseSlider<ImageType>::PrivateData { | |||
ImageType image; | |||
float minimum; | |||
float maximum; | |||
float step; | |||
float value; | |||
float valueDef; | |||
float valueTmp; | |||
bool usingDefault; | |||
bool dragging; | |||
bool inverted; | |||
bool valueIsSet; | |||
double startedX; | |||
double startedY; | |||
Callback* callback; | |||
Point<int> startPos; | |||
Point<int> endPos; | |||
Rectangle<double> 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.0), | |||
startedY(0.0), | |||
callback(nullptr), | |||
startPos(), | |||
endPos(), | |||
sliderArea() {} | |||
void recheckArea() noexcept | |||
{ | |||
if (startPos.getY() == endPos.getY()) | |||
{ | |||
// horizontal | |||
sliderArea = Rectangle<double>(startPos.getX(), | |||
startPos.getY(), | |||
endPos.getX() + static_cast<int>(image.getWidth()) - startPos.getX(), | |||
static_cast<int>(image.getHeight())); | |||
} | |||
else | |||
{ | |||
// vertical | |||
sliderArea = Rectangle<double>(startPos.getX(), | |||
startPos.getY(), | |||
static_cast<int>(image.getWidth()), | |||
endPos.getY() + static_cast<int>(image.getHeight()) - startPos.getY()); | |||
} | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseSlider<ImageType>::ImageBaseSlider(Widget* const parentWidget, const ImageType& image) noexcept | |||
: SubWidget(parentWidget), | |||
pData(new PrivateData(image)) | |||
{ | |||
setNeedsFullViewportDrawing(); | |||
} | |||
template <class ImageType> | |||
ImageBaseSlider<ImageType>::~ImageBaseSlider() | |||
{ | |||
delete pData; | |||
} | |||
template <class ImageType> | |||
float ImageBaseSlider<ImageType>::getValue() const noexcept | |||
{ | |||
return pData->value; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setValue(float value, bool sendCallback) noexcept | |||
{ | |||
if (! pData->valueIsSet) | |||
pData->valueIsSet = true; | |||
if (d_isEqual(pData->value, value)) | |||
return; | |||
pData->value = value; | |||
if (d_isZero(pData->step)) | |||
pData->valueTmp = value; | |||
repaint(); | |||
if (sendCallback && pData->callback != nullptr) | |||
{ | |||
try { | |||
pData->callback->imageSliderValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setValue"); | |||
} | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setStartPos(const Point<int>& startPos) noexcept | |||
{ | |||
pData->startPos = startPos; | |||
pData->recheckArea(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setStartPos(int x, int y) noexcept | |||
{ | |||
setStartPos(Point<int>(x, y)); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setEndPos(const Point<int>& endPos) noexcept | |||
{ | |||
pData->endPos = endPos; | |||
pData->recheckArea(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setEndPos(int x, int y) noexcept | |||
{ | |||
setEndPos(Point<int>(x, y)); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setInverted(bool inverted) noexcept | |||
{ | |||
if (pData->inverted == inverted) | |||
return; | |||
pData->inverted = inverted; | |||
repaint(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setDefault(float value) noexcept | |||
{ | |||
pData->valueDef = value; | |||
pData->usingDefault = true; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setRange(float min, float max) noexcept | |||
{ | |||
pData->minimum = min; | |||
pData->maximum = max; | |||
if (pData->value < min) | |||
{ | |||
pData->value = min; | |||
repaint(); | |||
if (pData->callback != nullptr && pData->valueIsSet) | |||
{ | |||
try { | |||
pData->callback->imageSliderValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange < min"); | |||
} | |||
} | |||
else if (pData->value > max) | |||
{ | |||
pData->value = max; | |||
repaint(); | |||
if (pData->callback != nullptr && pData->valueIsSet) | |||
{ | |||
try { | |||
pData->callback->imageSliderValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseSlider::setRange > max"); | |||
} | |||
} | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setStep(float step) noexcept | |||
{ | |||
pData->step = step; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::setCallback(Callback* callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSlider<ImageType>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
#if 0 // DEBUG, paints slider area | |||
Color(1.0f, 1.0f, 1.0f, 0.5f).setFor(context, true); | |||
Rectangle<int>(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 = (pData->value - pData->minimum) / (pData->maximum - pData->minimum); | |||
int x, y; | |||
if (pData->startPos.getY() == pData->endPos.getY()) | |||
{ | |||
// horizontal | |||
if (pData->inverted) | |||
x = pData->endPos.getX() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); | |||
else | |||
x = pData->startPos.getX() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getX()-pData->startPos.getX())); | |||
y = pData->startPos.getY(); | |||
} | |||
else | |||
{ | |||
// vertical | |||
x = pData->startPos.getX(); | |||
if (pData->inverted) | |||
y = pData->endPos.getY() - static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); | |||
else | |||
y = pData->startPos.getY() + static_cast<int>(normValue*static_cast<float>(pData->endPos.getY()-pData->startPos.getY())); | |||
} | |||
pData->image.drawAt(context, x, y); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseSlider<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (ev.button != 1) | |||
return false; | |||
if (ev.press) | |||
{ | |||
if (! pData->sliderArea.contains(ev.pos)) | |||
return false; | |||
if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) | |||
{ | |||
setValue(pData->valueDef, true); | |||
pData->valueTmp = pData->value; | |||
return true; | |||
} | |||
float vper; | |||
const double x = ev.pos.getX(); | |||
const double y = ev.pos.getY(); | |||
if (pData->startPos.getY() == pData->endPos.getY()) | |||
{ | |||
// horizontal | |||
vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); | |||
} | |||
else | |||
{ | |||
// vertical | |||
vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); | |||
} | |||
float value; | |||
if (pData->inverted) | |||
value = pData->maximum - vper * (pData->maximum - pData->minimum); | |||
else | |||
value = pData->minimum + vper * (pData->maximum - pData->minimum); | |||
if (value < pData->minimum) | |||
{ | |||
pData->valueTmp = value = pData->minimum; | |||
} | |||
else if (value > pData->maximum) | |||
{ | |||
pData->valueTmp = value = pData->maximum; | |||
} | |||
else if (d_isNotZero(pData->step)) | |||
{ | |||
pData->valueTmp = value; | |||
const float rest = std::fmod(value, pData->step); | |||
value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f); | |||
} | |||
pData->dragging = true; | |||
pData->startedX = x; | |||
pData->startedY = y; | |||
if (pData->callback != nullptr) | |||
pData->callback->imageSliderDragStarted(this); | |||
setValue(value, true); | |||
return true; | |||
} | |||
else if (pData->dragging) | |||
{ | |||
if (pData->callback != nullptr) | |||
pData->callback->imageSliderDragFinished(this); | |||
pData->dragging = false; | |||
return true; | |||
} | |||
return false; | |||
} | |||
template <class ImageType> | |||
bool ImageBaseSlider<ImageType>::onMotion(const MotionEvent& ev) | |||
{ | |||
if (! pData->dragging) | |||
return false; | |||
const bool horizontal = pData->startPos.getY() == pData->endPos.getY(); | |||
const double x = ev.pos.getX(); | |||
const double y = ev.pos.getY(); | |||
if ((horizontal && pData->sliderArea.containsX(x)) || (pData->sliderArea.containsY(y) && ! horizontal)) | |||
{ | |||
float vper; | |||
if (horizontal) | |||
{ | |||
// horizontal | |||
vper = float(x - pData->sliderArea.getX()) / float(pData->sliderArea.getWidth()); | |||
} | |||
else | |||
{ | |||
// vertical | |||
vper = float(y - pData->sliderArea.getY()) / float(pData->sliderArea.getHeight()); | |||
} | |||
float value; | |||
if (pData->inverted) | |||
value = pData->maximum - vper * (pData->maximum - pData->minimum); | |||
else | |||
value = pData->minimum + vper * (pData->maximum - pData->minimum); | |||
if (value < pData->minimum) | |||
{ | |||
pData->valueTmp = value = pData->minimum; | |||
} | |||
else if (value > pData->maximum) | |||
{ | |||
pData->valueTmp = value = pData->maximum; | |||
} | |||
else if (d_isNotZero(pData->step)) | |||
{ | |||
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 < pData->sliderArea.getX()) | |||
setValue(pData->inverted ? pData->maximum : pData->minimum, true); | |||
else | |||
setValue(pData->inverted ? pData->minimum : pData->maximum, true); | |||
} | |||
else | |||
{ | |||
if (y < pData->sliderArea.getY()) | |||
setValue(pData->inverted ? pData->maximum : pData->minimum, true); | |||
else | |||
setValue(pData->inverted ? pData->minimum : pData->maximum, true); | |||
} | |||
return true; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseSwitch<ImageType>::PrivateData { | |||
ImageType imageNormal; | |||
ImageType imageDown; | |||
bool isDown; | |||
Callback* callback; | |||
PrivateData(const ImageType& normal, const ImageType& down) | |||
: imageNormal(normal), | |||
imageDown(down), | |||
isDown(false), | |||
callback(nullptr) | |||
{ | |||
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | |||
} | |||
PrivateData(PrivateData* const other) | |||
: 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_COPYABLE(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseSwitch<ImageType>::ImageBaseSwitch(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept | |||
: SubWidget(parentWidget), | |||
pData(new PrivateData(imageNormal, imageDown)) | |||
{ | |||
setSize(imageNormal.getSize()); | |||
} | |||
template <class ImageType> | |||
ImageBaseSwitch<ImageType>::ImageBaseSwitch(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept | |||
: SubWidget(imageSwitch.getParentWidget()), | |||
pData(new PrivateData(imageSwitch.pData)) | |||
{ | |||
setSize(pData->imageNormal.getSize()); | |||
} | |||
template <class ImageType> | |||
ImageBaseSwitch<ImageType>& ImageBaseSwitch<ImageType>::operator=(const ImageBaseSwitch<ImageType>& imageSwitch) noexcept | |||
{ | |||
pData->assignFrom(imageSwitch.pData); | |||
setSize(pData->imageNormal.getSize()); | |||
return *this; | |||
} | |||
template <class ImageType> | |||
ImageBaseSwitch<ImageType>::~ImageBaseSwitch() | |||
{ | |||
delete pData; | |||
} | |||
template <class ImageType> | |||
bool ImageBaseSwitch<ImageType>::isDown() const noexcept | |||
{ | |||
return pData->isDown; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSwitch<ImageType>::setDown(const bool down) noexcept | |||
{ | |||
if (pData->isDown == down) | |||
return; | |||
pData->isDown = down; | |||
repaint(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseSwitch<ImageType>::setCallback(Callback* const callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
void ImageBaseSwitch<ImageType>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
if (pData->isDown) | |||
pData->imageDown.draw(context); | |||
else | |||
pData->imageNormal.draw(context); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseSwitch<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (ev.press && contains(ev.pos)) | |||
{ | |||
pData->isDown = !pData->isDown; | |||
repaint(); | |||
if (pData->callback != nullptr) | |||
pData->callback->imageSwitchClicked(this, pData->isDown); | |||
return true; | |||
} | |||
return false; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -15,7 +15,7 @@ | |||
*/ | |||
#include "../NanoVG.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
#include "SubWidgetPrivateData.hpp" | |||
#ifndef DGL_NO_SHARED_RESOURCES | |||
# include "Resources.hpp" | |||
@@ -53,6 +53,28 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | |||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||
# ifdef DGL_USE_NANOVG_FBO | |||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | |||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | |||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | |||
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | |||
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | |||
DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) | |||
DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) | |||
DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) | |||
DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) | |||
# endif | |||
# ifdef DGL_USE_OPENGL3 | |||
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) | |||
DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) | |||
DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays) | |||
DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap) | |||
DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex) | |||
DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) | |||
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
# endif | |||
# undef DGL_EXT | |||
#endif | |||
@@ -60,32 +82,61 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
// Include NanoVG OpenGL implementation | |||
//#define STB_IMAGE_STATIC | |||
#define NANOVG_GL2_IMPLEMENTATION | |||
#ifdef DGL_USE_OPENGL3 | |||
# define NANOVG_GL3_IMPLEMENTATION | |||
#else | |||
# define NANOVG_GL2_IMPLEMENTATION | |||
#endif | |||
#if defined(DISTRHO_OS_MAC) && defined(NANOVG_GL2_IMPLEMENTATION) | |||
# define glBindVertexArray glBindVertexArrayAPPLE | |||
# define glDeleteVertexArrays glDeleteVertexArraysAPPLE | |||
# define glGenVertexArrays glGenVertexArraysAPPLE | |||
#endif | |||
#include "nanovg/nanovg_gl.h" | |||
#ifdef DGL_USE_NANOVG_FBO | |||
# define NANOVG_FBO_VALID 1 | |||
# include "nanovg/nanovg_gl_utils.h" | |||
#endif | |||
#if defined(NANOVG_GL2) | |||
# define nvgCreateGL nvgCreateGL2 | |||
# define nvgCreateGLfn nvgCreateGL2 | |||
# define nvgDeleteGL nvgDeleteGL2 | |||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2 | |||
# define nvglImageHandle nvglImageHandleGL2 | |||
#elif defined(NANOVG_GL3) | |||
# define nvgCreateGL nvgCreateGL3 | |||
# define nvgCreateGLfn nvgCreateGL3 | |||
# define nvgDeleteGL nvgDeleteGL3 | |||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3 | |||
# define nvglImageHandle nvglImageHandleGL3 | |||
#elif defined(NANOVG_GLES2) | |||
# define nvgCreateGL nvgCreateGLES2 | |||
# define nvgCreateGLfn nvgCreateGLES2 | |||
# define nvgDeleteGL nvgDeleteGLES2 | |||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2 | |||
# define nvglImageHandle nvglImageHandleGLES2 | |||
#elif defined(NANOVG_GLES3) | |||
# define nvgCreateGL nvgCreateGLES3 | |||
# define nvgCreateGLfn nvgCreateGLES3 | |||
# define nvgDeleteGL nvgDeleteGLES3 | |||
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3 | |||
# define nvglImageHandle nvglImageHandleGLES3 | |||
#endif | |||
static NVGcontext* nvgCreateGL_helper(int flags) | |||
// ----------------------------------------------------------------------- | |||
START_NAMESPACE_DGL | |||
NVGcontext* nvgCreateGL(int flags) | |||
{ | |||
#if defined(DISTRHO_OS_WINDOWS) | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wcast-function-type" | |||
# endif | |||
static bool needsInit = true; | |||
if (needsInit) | |||
{ | |||
needsInit = false; | |||
# define DGL_EXT(PROC, func) \ | |||
func = (PROC) wglGetProcAddress ( #func ); \ | |||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | |||
DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); | |||
DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) | |||
DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) | |||
@@ -114,15 +165,55 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | |||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | |||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | |||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||
# ifdef DGL_USE_NANOVG_FBO | |||
DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) | |||
DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) | |||
DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) | |||
DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) | |||
DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) | |||
DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) | |||
DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) | |||
DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) | |||
DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) | |||
DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) | |||
# endif | |||
# ifdef DGL_USE_OPENGL3 | |||
DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) | |||
DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) | |||
DGL_EXT(PFNGLDELETEVERTEXARRAYSPROC, glDeleteVertexArrays) | |||
DGL_EXT(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap) | |||
DGL_EXT(PFNGLGETUNIFORMBLOCKINDEXPROC, glGetUniformBlockIndex) | |||
DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) | |||
DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
# endif | |||
# undef DGL_EXT | |||
} | |||
needsInit = false; | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic pop | |||
# endif | |||
#endif | |||
return nvgCreateGL(flags); | |||
return nvgCreateGLfn(flags); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// DGL Color class conversion | |||
START_NAMESPACE_DGL | |||
Color::Color(const NVGcolor& c) noexcept | |||
: red(c.r), green(c.g), blue(c.b), alpha(c.a) | |||
{ | |||
fixBounds(); | |||
} | |||
Color::operator NVGcolor() const noexcept | |||
{ | |||
NVGcolor nc; | |||
nc.r = red; | |||
nc.g = green; | |||
nc.b = blue; | |||
nc.a = alpha; | |||
return nc; | |||
} | |||
// ----------------------------------------------------------------------- | |||
// NanoImage | |||
@@ -153,6 +244,7 @@ NanoImage& NanoImage::operator=(const Handle& handle) | |||
fHandle.context = handle.context; | |||
fHandle.imageId = handle.imageId; | |||
_updateSize(); | |||
return *this; | |||
} | |||
@@ -220,15 +312,10 @@ NanoVG::Paint::operator NVGpaint() const noexcept | |||
// NanoVG | |||
NanoVG::NanoVG(int flags) | |||
: fContext(nvgCreateGL_helper(flags)), | |||
: fContext(nvgCreateGL(flags)), | |||
fInFrame(false), | |||
fIsSubWidget(false) {} | |||
NanoVG::NanoVG(NanoWidget* groupWidget) | |||
: fContext(groupWidget->fContext), | |||
fInFrame(false), | |||
fIsSubWidget(true) {} | |||
NanoVG::~NanoVG() | |||
{ | |||
DISTRHO_SAFE_ASSERT(! fInFrame); | |||
@@ -241,26 +328,28 @@ NanoVG::~NanoVG() | |||
void NanoVG::beginFrame(const uint width, const uint height, const float scaleFactor) | |||
{ | |||
if (fContext == nullptr) return; | |||
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,); | |||
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,); | |||
fInFrame = true; | |||
nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); | |||
if (fContext != nullptr) | |||
nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor); | |||
} | |||
void NanoVG::beginFrame(Widget* const widget) | |||
{ | |||
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<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f); | |||
if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) | |||
nvgBeginFrame(fContext, | |||
static_cast<int>(tlw->getWidth()), | |||
static_cast<int>(tlw->getHeight()), | |||
tlw->getScaleFactor()); | |||
} | |||
void NanoVG::cancelFrame() | |||
@@ -424,6 +513,12 @@ void NanoVG::globalAlpha(float alpha) | |||
nvgGlobalAlpha(fContext, alpha); | |||
} | |||
void NanoVG::globalTint(Color tint) | |||
{ | |||
if (fContext != nullptr) | |||
nvgGlobalTint(fContext, tint); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Transforms | |||
@@ -447,10 +542,8 @@ void NanoVG::translate(float x, float y) | |||
void NanoVG::rotate(float angle) | |||
{ | |||
if (fContext == nullptr) return; | |||
DISTRHO_SAFE_ASSERT_RETURN(angle > 0.0f,); | |||
nvgRotate(fContext, angle); | |||
if (fContext != nullptr) | |||
nvgRotate(fContext, angle); | |||
} | |||
void NanoVG::skewX(float angle) | |||
@@ -574,6 +667,45 @@ NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int | |||
return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<int>(dataSize))); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, | |||
ImageFlags imageFlags, ImageFormat format) | |||
{ | |||
return createImageFromRawMemory(w, h, data, static_cast<int>(imageFlags), format); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, | |||
int imageFlags, ImageFormat format) | |||
{ | |||
if (fContext == nullptr) return NanoImage::Handle(); | |||
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); | |||
NVGtexture nvgformat; | |||
switch (format) | |||
{ | |||
case kImageFormatGrayscale: | |||
nvgformat = NVG_TEXTURE_ALPHA; | |||
break; | |||
case kImageFormatBGR: | |||
nvgformat = NVG_TEXTURE_BGR; | |||
break; | |||
case kImageFormatBGRA: | |||
nvgformat = NVG_TEXTURE_BGRA; | |||
break; | |||
case kImageFormatRGB: | |||
nvgformat = NVG_TEXTURE_RGB; | |||
break; | |||
case kImageFormatRGBA: | |||
nvgformat = NVG_TEXTURE_RGBA; | |||
break; | |||
default: | |||
return NanoImage::Handle(); | |||
} | |||
return NanoImage::Handle(fContext, nvgCreateImageRaw(fContext, | |||
static_cast<int>(w), | |||
static_cast<int>(h), imageFlags, nvgformat, data)); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags) | |||
{ | |||
return createImageFromRGBA(w, h, data, static_cast<int>(imageFlags)); | |||
@@ -589,12 +721,14 @@ NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, | |||
static_cast<int>(h), imageFlags, data)); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, ImageFlags imageFlags, bool deleteTexture) | |||
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, | |||
ImageFlags imageFlags, bool deleteTexture) | |||
{ | |||
return createImageFromTextureHandle(textureId, w, h, static_cast<int>(imageFlags), deleteTexture); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, int imageFlags, bool deleteTexture) | |||
NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, | |||
int imageFlags, bool deleteTexture) | |||
{ | |||
if (fContext == nullptr) return NanoImage::Handle(); | |||
DISTRHO_SAFE_ASSERT_RETURN(textureId != 0, NanoImage::Handle()); | |||
@@ -758,26 +892,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<uchar*>(data), static_cast<int>(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); | |||
} | |||
@@ -899,77 +1033,56 @@ 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 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, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// NanoSubWidget | |||
struct NanoWidget::PrivateData { | |||
NanoWidget* const self; | |||
std::vector<NanoWidget*> subWidgets; | |||
PrivateData(NanoWidget* const s) | |||
: self(s), | |||
subWidgets() {} | |||
~PrivateData() | |||
{ | |||
subWidgets.clear(); | |||
} | |||
}; | |||
NanoWidget::NanoWidget(Window& parent, int flags) | |||
: Widget(parent), | |||
NanoVG(flags), | |||
nData(new PrivateData(this)) | |||
template <> | |||
NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parent, int flags) | |||
: SubWidget(parent), | |||
NanoVG(flags) | |||
{ | |||
pData->needsScaling = true; | |||
setNeedsViewportScaling(); | |||
} | |||
NanoWidget::NanoWidget(Widget* groupWidget, int flags) | |||
: Widget(groupWidget, true), | |||
NanoVG(flags), | |||
nData(new PrivateData(this)) | |||
{ | |||
pData->needsScaling = true; | |||
} | |||
template class NanoBaseWidget<SubWidget>; | |||
NanoWidget::NanoWidget(NanoWidget* groupWidget) | |||
: Widget(groupWidget, false), | |||
NanoVG(groupWidget), | |||
nData(new PrivateData(this)) | |||
{ | |||
pData->needsScaling = true; | |||
pData->skipDisplay = true; | |||
groupWidget->nData->subWidgets.push_back(this); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// NanoTopLevelWidget | |||
NanoWidget::~NanoWidget() | |||
{ | |||
delete nData; | |||
} | |||
template <> | |||
NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags) | |||
: TopLevelWidget(windowToMapTo), | |||
NanoVG(flags) {} | |||
void NanoWidget::onDisplay() | |||
{ | |||
NanoVG::beginFrame(getWidth(), getHeight()); | |||
onNanoDisplay(); | |||
template class NanoBaseWidget<TopLevelWidget>; | |||
for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it) | |||
{ | |||
NanoWidget* const widget(*it); | |||
widget->onNanoDisplay(); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// NanoStandaloneWindow | |||
NanoVG::endFrame(); | |||
} | |||
template <> | |||
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags) | |||
: StandaloneWindow(app), | |||
NanoVG(flags) {} | |||
template <> | |||
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags) | |||
: StandaloneWindow(app, parentWindow), | |||
NanoVG(flags) {} | |||
template class NanoBaseWidget<StandaloneWindow>; | |||
// ----------------------------------------------------------------------- | |||
@@ -0,0 +1,796 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifdef _MSC_VER | |||
// instantiated template classes whose methods are defined elsewhere | |||
# pragma warning(disable:4661) | |||
#endif | |||
#include "../OpenGL.hpp" | |||
#include "../Color.hpp" | |||
#include "../ImageWidgets.hpp" | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
#include "WindowPrivateData.hpp" | |||
// templated classes | |||
#include "ImageBaseWidgets.cpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
#ifdef DGL_USE_OPENGL3 | |||
static void notImplemented(const char* const name) | |||
{ | |||
d_stderr2("OpenGL3 function not implemented: %s", name); | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Color | |||
void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
if (includeAlpha) | |||
glColor4f(red, green, blue, alpha); | |||
else | |||
glColor3f(red, green, blue); | |||
#else | |||
notImplemented("Color::setFor"); | |||
// unused | |||
(void)includeAlpha; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Line | |||
#ifndef DGL_USE_OPENGL3 | |||
template<typename T> | |||
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||
glBegin(GL_LINES); | |||
{ | |||
glVertex2d(posStart.getX(), posStart.getY()); | |||
glVertex2d(posEnd.getX(), posEnd.getY()); | |||
} | |||
glEnd(); | |||
} | |||
#endif | |||
template<typename T> | |||
void Line<T>::draw(const GraphicsContext&, const T width) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||
glLineWidth(static_cast<GLfloat>(width)); | |||
drawLine<T>(posStart, posEnd); | |||
#else | |||
notImplemented("Line::draw"); | |||
#endif | |||
} | |||
// deprecated calls | |||
template<typename T> | |||
void Line<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawLine<T>(posStart, posEnd); | |||
#else | |||
notImplemented("Line::draw"); | |||
#endif | |||
} | |||
template class Line<double>; | |||
template class Line<float>; | |||
template class Line<int>; | |||
template class Line<uint>; | |||
template class Line<short>; | |||
template class Line<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Circle | |||
#ifndef DGL_USE_OPENGL3 | |||
template<typename T> | |||
static void drawCircle(const Point<T>& 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<numSegments; ++i) | |||
{ | |||
glVertex2d(x + origx, y + origy); | |||
t = x; | |||
x = cos * x - sin * y; | |||
y = sin * t + cos * y; | |||
} | |||
glEnd(); | |||
} | |||
#endif | |||
template<typename T> | |||
void Circle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
#else | |||
notImplemented("Circle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
#else | |||
notImplemented("Circle::drawOutline"); | |||
#endif | |||
} | |||
// deprecated calls | |||
template<typename T> | |||
void Circle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
#else | |||
notImplemented("Circle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Circle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
#else | |||
notImplemented("Circle::drawOutline"); | |||
#endif | |||
} | |||
template class Circle<double>; | |||
template class Circle<float>; | |||
template class Circle<int>; | |||
template class Circle<uint>; | |||
template class Circle<short>; | |||
template class Circle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Triangle | |||
#ifndef DGL_USE_OPENGL3 | |||
template<typename T> | |||
static void drawTriangle(const Point<T>& pos1, | |||
const Point<T>& pos2, | |||
const Point<T>& pos3, | |||
const bool outline) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |||
glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); | |||
{ | |||
glVertex2d(pos1.getX(), pos1.getY()); | |||
glVertex2d(pos2.getX(), pos2.getY()); | |||
glVertex2d(pos3.getX(), pos3.getY()); | |||
} | |||
glEnd(); | |||
} | |||
#endif | |||
template<typename T> | |||
void Triangle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawTriangle<T>(pos1, pos2, pos3, false); | |||
#else | |||
notImplemented("Triangle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
drawTriangle<T>(pos1, pos2, pos3, true); | |||
#else | |||
notImplemented("Triangle::drawOutline"); | |||
#endif | |||
} | |||
// deprecated calls | |||
template<typename T> | |||
void Triangle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawTriangle<T>(pos1, pos2, pos3, false); | |||
#else | |||
notImplemented("Triangle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Triangle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawTriangle<T>(pos1, pos2, pos3, true); | |||
#else | |||
notImplemented("Triangle::drawOutline"); | |||
#endif | |||
} | |||
template class Triangle<double>; | |||
template class Triangle<float>; | |||
template class Triangle<int>; | |||
template class Triangle<uint>; | |||
template class Triangle<short>; | |||
template class Triangle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// Rectangle | |||
#ifndef DGL_USE_OPENGL3 | |||
template<typename T> | |||
static void drawRectangle(const Rectangle<T>& rect, const bool outline) | |||
{ | |||
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(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(); | |||
} | |||
#endif | |||
template<typename T> | |||
void Rectangle<T>::draw(const GraphicsContext&) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawRectangle<T>(*this, false); | |||
#else | |||
notImplemented("Rectangle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
#ifndef DGL_USE_OPENGL3 | |||
drawRectangle<T>(*this, true); | |||
#else | |||
notImplemented("Rectangle::drawOutline"); | |||
#endif | |||
} | |||
// deprecated calls | |||
template<typename T> | |||
void Rectangle<T>::draw() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawRectangle<T>(*this, false); | |||
#else | |||
notImplemented("Rectangle::draw"); | |||
#endif | |||
} | |||
template<typename T> | |||
void Rectangle<T>::drawOutline() | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
drawRectangle<T>(*this, true); | |||
#else | |||
notImplemented("Rectangle::drawOutline"); | |||
#endif | |||
} | |||
template class Rectangle<double>; | |||
template class Rectangle<float>; | |||
template class Rectangle<int>; | |||
template class Rectangle<uint>; | |||
template class Rectangle<short>; | |||
template class Rectangle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// OpenGLImage | |||
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<GLsizei>(image.getWidth()), | |||
static_cast<GLsizei>(image.getHeight()), | |||
0, | |||
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData()); | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
glDisable(GL_TEXTURE_2D); | |||
} | |||
static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled) | |||
{ | |||
if (textureId == 0 || image.isInvalid()) | |||
return; | |||
if (! setupCalled) | |||
{ | |||
setupOpenGLImage(image, textureId); | |||
setupCalled = true; | |||
} | |||
#ifndef DGL_USE_OPENGL3 | |||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
#endif | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, textureId); | |||
#ifndef DGL_USE_OPENGL3 | |||
glBegin(GL_QUADS); | |||
{ | |||
const int x = pos.getX(); | |||
const int y = pos.getY(); | |||
const int w = static_cast<int>(image.getWidth()); | |||
const int h = static_cast<int>(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(); | |||
#endif | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
glDisable(GL_TEXTURE_2D); | |||
} | |||
OpenGLImage::OpenGLImage() | |||
: ImageBase(), | |||
textureId(0), | |||
setupCalled(false) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) | |||
: ImageBase(rdata, w, h, fmt), | |||
textureId(0), | |||
setupCalled(false) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |||
: ImageBase(rdata, s, fmt), | |||
textureId(0), | |||
setupCalled(false) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
OpenGLImage::OpenGLImage(const OpenGLImage& image) | |||
: ImageBase(image), | |||
textureId(0), | |||
setupCalled(false) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
OpenGLImage::~OpenGLImage() | |||
{ | |||
if (textureId != 0) | |||
glDeleteTextures(1, &textureId); | |||
} | |||
void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||
{ | |||
setupCalled = false; | |||
ImageBase::loadFromMemory(rdata, s, fmt); | |||
} | |||
void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||
{ | |||
drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
} | |||
OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||
{ | |||
rawData = image.rawData; | |||
size = image.size; | |||
format = image.format; | |||
setupCalled = false; | |||
return *this; | |||
} | |||
// deprecated calls | |||
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) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt) | |||
: ImageBase(rdata, s, asDISTRHOImageFormat(fmt)), | |||
textureId(0), | |||
setupCalled(false) | |||
{ | |||
glGenTextures(1, &textureId); | |||
DISTRHO_SAFE_ASSERT(textureId != 0); | |||
} | |||
void OpenGLImage::draw() | |||
{ | |||
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||
} | |||
void OpenGLImage::drawAt(const int x, const int y) | |||
{ | |||
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||
} | |||
void OpenGLImage::drawAt(const Point<int>& pos) | |||
{ | |||
drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// ImageBaseAboutWindow | |||
#if 0 | |||
template <> | |||
void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
img.draw(context); | |||
} | |||
#endif | |||
template class ImageBaseAboutWindow<OpenGLImage>; | |||
// ----------------------------------------------------------------------- | |||
// ImageBaseButton | |||
template class ImageBaseButton<OpenGLImage>; | |||
// ----------------------------------------------------------------------- | |||
// ImageBaseKnob | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||
{ | |||
glTextureId = 0; | |||
glGenTextures(1, &glTextureId); | |||
} | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||
{ | |||
if (glTextureId == 0) | |||
return; | |||
glDeleteTextures(1, &glTextureId); | |||
glTextureId = 0; | |||
} | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
const float normValue = getNormalizedValue(); | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||
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); | |||
// 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)); | |||
} | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||
pData->isReady = true; | |||
} | |||
const int w = static_cast<int>(getWidth()); | |||
const int h = static_cast<int>(getHeight()); | |||
if (pData->rotationAngle != 0) | |||
{ | |||
#ifndef DGL_USE_OPENGL3 | |||
glPushMatrix(); | |||
#endif | |||
const int w2 = w/2; | |||
const int h2 = h/2; | |||
#ifndef DGL_USE_OPENGL3 | |||
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||
#endif | |||
Rectangle<int>(-w2, -h2, w, h).draw(context); | |||
#ifndef DGL_USE_OPENGL3 | |||
glPopMatrix(); | |||
#endif | |||
} | |||
else | |||
{ | |||
Rectangle<int>(0, 0, w, h).draw(context); | |||
} | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
glDisable(GL_TEXTURE_2D); | |||
} | |||
template class ImageBaseKnob<OpenGLImage>; | |||
// ----------------------------------------------------------------------- | |||
// ImageBaseSlider | |||
template class ImageBaseSlider<OpenGLImage>; | |||
// ----------------------------------------------------------------------- | |||
// ImageBaseSwitch | |||
template class ImageBaseSwitch<OpenGLImage>; | |||
// ----------------------------------------------------------------------- | |||
void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |||
{ | |||
if (skipDrawing) | |||
return; | |||
bool needsDisableScissor = false; | |||
if (needsViewportScaling) | |||
{ | |||
// limit viewport to widget bounds | |||
const int x = absolutePos.getX(); | |||
const int w = static_cast<int>(self->getWidth()); | |||
const int h = static_cast<int>(self->getHeight()); | |||
if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0) | |||
{ | |||
glViewport(x, | |||
-static_cast<int>(height * viewportScaleFactor - height + absolutePos.getY() + 0.5), | |||
static_cast<int>(width * viewportScaleFactor + 0.5), | |||
static_cast<int>(height * viewportScaleFactor + 0.5)); | |||
} | |||
else | |||
{ | |||
const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY(); | |||
glViewport(x, y, w, h); | |||
} | |||
} | |||
else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height))) | |||
{ | |||
// full viewport size | |||
glViewport(0, | |||
-static_cast<int>(height * autoScaleFactor - height + 0.5), | |||
static_cast<int>(width * autoScaleFactor + 0.5), | |||
static_cast<int>(height * autoScaleFactor + 0.5)); | |||
} | |||
else | |||
{ | |||
// set viewport pos | |||
glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5), | |||
-static_cast<int>(std::round((height * autoScaleFactor - height) | |||
+ (absolutePos.getY() * autoScaleFactor))), | |||
static_cast<int>(std::round(width * autoScaleFactor)), | |||
static_cast<int>(std::round(height * autoScaleFactor))); | |||
// then cut the outer bounds | |||
glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5), | |||
static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY()) | |||
* autoScaleFactor)), | |||
static_cast<int>(std::round(self->getWidth() * autoScaleFactor)), | |||
static_cast<int>(std::round(self->getHeight() * autoScaleFactor))); | |||
glEnable(GL_SCISSOR_TEST); | |||
needsDisableScissor = true; | |||
} | |||
// display widget | |||
self->onDisplay(); | |||
if (needsDisableScissor) | |||
glDisable(GL_SCISSOR_TEST); | |||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||
} | |||
// ----------------------------------------------------------------------- | |||
void TopLevelWidget::PrivateData::display() | |||
{ | |||
if (! selfw->pData->visible) | |||
return; | |||
const Size<uint> size(window.getSize()); | |||
const uint width = size.getWidth(); | |||
const uint height = size.getHeight(); | |||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||
// full viewport size | |||
if (window.pData->autoScaling) | |||
{ | |||
glViewport(0, | |||
-static_cast<int>(height * autoScaleFactor - height + 0.5), | |||
static_cast<int>(width * autoScaleFactor + 0.5), | |||
static_cast<int>(height * autoScaleFactor + 0.5)); | |||
} | |||
else | |||
{ | |||
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height)); | |||
} | |||
// main widget drawing | |||
self->onDisplay(); | |||
// now draw subwidgets if there are any | |||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||
} | |||
// ----------------------------------------------------------------------- | |||
void Window::PrivateData::renderToPicture(const char* const filename, | |||
const GraphicsContext&, | |||
const uint width, | |||
const uint height) | |||
{ | |||
FILE* const f = fopen(filename, "w"); | |||
DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,); | |||
GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)]; | |||
glFlush(); | |||
glReadPixels(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGB, GL_UNSIGNED_BYTE, pixels); | |||
fprintf(f, "P3\n%d %d\n255\n", width, height); | |||
for (uint y = 0; y < height; y++) | |||
{ | |||
for (uint i, x = 0; x < width; x++) | |||
{ | |||
i = 3 * ((height - y - 1) * width + x); | |||
fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]); | |||
} | |||
fprintf(f, "\n"); | |||
} | |||
delete[] pixels; | |||
fclose(f); | |||
} | |||
// ----------------------------------------------------------------------- | |||
const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
{ | |||
return (const GraphicsContext&)graphicsContext; | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,189 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
#include "../TopLevelWidget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
SubWidget::SubWidget(Widget* const parentWidget) | |||
: Widget(parentWidget), | |||
pData(new PrivateData(this, parentWidget)) {} | |||
SubWidget::~SubWidget() | |||
{ | |||
delete pData; | |||
} | |||
template<typename T> | |||
bool SubWidget::contains(const T x, const T y) const noexcept | |||
{ | |||
return Rectangle<double>(0, 0, | |||
static_cast<double>(getWidth()), | |||
static_cast<double>(getHeight())).contains(x, y); | |||
} | |||
template<typename T> | |||
bool SubWidget::contains(const Point<T>& 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(); | |||
} | |||
Point<int> SubWidget::getAbsolutePos() const noexcept | |||
{ | |||
return pData->absolutePos; | |||
} | |||
Rectangle<int> SubWidget::getAbsoluteArea() const noexcept | |||
{ | |||
return Rectangle<int>(getAbsolutePos(), getSize().toInt()); | |||
} | |||
Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept | |||
{ | |||
const int x = getAbsoluteX(); | |||
const int y = getAbsoluteY(); | |||
if (x >= 0 && y >= 0) | |||
return Rectangle<uint>(x, y, getSize()); | |||
const int xOffset = std::min(0, x); | |||
const int yOffset = std::min(0, y); | |||
const int width = std::max(0, static_cast<int>(getWidth()) + xOffset); | |||
const int height = std::max(0, static_cast<int>(getHeight()) + yOffset); | |||
return Rectangle<uint>(0, 0, static_cast<uint>(width), static_cast<uint>(height)); | |||
} | |||
void SubWidget::setAbsoluteX(const int x) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(x, getAbsoluteY())); | |||
} | |||
void SubWidget::setAbsoluteY(const int y) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(getAbsoluteX(), y)); | |||
} | |||
void SubWidget::setAbsolutePos(const int x, const int y) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(x, y)); | |||
} | |||
void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept | |||
{ | |||
if (pData->absolutePos == pos) | |||
return; | |||
PositionChangedEvent ev; | |||
ev.oldPos = pData->absolutePos; | |||
ev.pos = pos; | |||
pData->absolutePos = pos; | |||
onPositionChanged(ev); | |||
repaint(); | |||
} | |||
Point<int> SubWidget::getMargin() const noexcept | |||
{ | |||
return pData->margin; | |||
} | |||
void SubWidget::setMargin(const int x, const int y) noexcept | |||
{ | |||
pData->margin = Point<int>(x, y); | |||
} | |||
void SubWidget::setMargin(const Point<int>& offset) noexcept | |||
{ | |||
pData->margin = offset; | |||
} | |||
Widget* SubWidget::getParentWidget() const noexcept | |||
{ | |||
return pData->parentWidget; | |||
} | |||
void SubWidget::repaint() noexcept | |||
{ | |||
if (! isVisible()) | |||
return; | |||
if (TopLevelWidget* const topw = getTopLevelWidget()) | |||
{ | |||
if (pData->needsFullViewportForDrawing) | |||
topw->repaint(); | |||
else | |||
topw->repaint(getConstrainedAbsoluteArea()); | |||
} | |||
} | |||
void SubWidget::toFront() | |||
{ | |||
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets); | |||
subwidgets.remove(this); | |||
subwidgets.push_back(this); | |||
} | |||
void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing) | |||
{ | |||
pData->needsFullViewportForDrawing = needsFullViewportForDrawing; | |||
} | |||
void SubWidget::setNeedsViewportScaling(const bool needsViewportScaling, const double autoScaleFactor) | |||
{ | |||
pData->needsViewportScaling = needsViewportScaling; | |||
pData->viewportScaleFactor = autoScaleFactor; | |||
} | |||
void SubWidget::setSkipDrawing(const bool skipDrawing) | |||
{ | |||
pData->skipDrawing = skipDrawing; | |||
} | |||
void SubWidget::onPositionChanged(const PositionChangedEvent&) | |||
{ | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Possible template data types | |||
template<> | |||
bool SubWidget::contains(const Point<double>& pos) const noexcept | |||
{ | |||
return contains(pos.getX(), pos.getY()); | |||
} | |||
// float, int, uint, short, ushort | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,45 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) | |||
: self(s), | |||
selfw((Widget*)s), | |||
parentWidget(pw), | |||
absolutePos(), | |||
margin(), | |||
needsFullViewportForDrawing(false), | |||
needsViewportScaling(false), | |||
skipDrawing(false), | |||
viewportScaleFactor(0.0) | |||
{ | |||
parentWidget->pData->subWidgets.push_back(self); | |||
} | |||
SubWidget::PrivateData::~PrivateData() | |||
{ | |||
parentWidget->pData->subWidgets.remove(self); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,50 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED | |||
#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../SubWidget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct SubWidget::PrivateData { | |||
SubWidget* const self; | |||
Widget* const selfw; | |||
Widget* const parentWidget; | |||
Point<int> absolutePos; | |||
Point<int> margin; | |||
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds | |||
bool needsViewportScaling; // needed for NanoVG | |||
bool skipDrawing; // for context reuse in NanoVG based guis | |||
double viewportScaleFactor; // auto-scaling for NanoVG | |||
explicit PrivateData(SubWidget* 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 autoScaleFactor); | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED |
@@ -0,0 +1,151 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "../Window.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
TopLevelWidget::TopLevelWidget(Window& windowToMapTo) | |||
: Widget(this), | |||
pData(new PrivateData(this, windowToMapTo)) {} | |||
TopLevelWidget::~TopLevelWidget() | |||
{ | |||
delete pData; | |||
} | |||
Application& TopLevelWidget::getApp() const noexcept | |||
{ | |||
return pData->window.getApp(); | |||
} | |||
Window& TopLevelWidget::getWindow() const noexcept | |||
{ | |||
return pData->window; | |||
} | |||
void TopLevelWidget::setWidth(const uint width) | |||
{ | |||
pData->window.setWidth(width); | |||
} | |||
void TopLevelWidget::setHeight(const uint height) | |||
{ | |||
pData->window.setHeight(height); | |||
} | |||
void TopLevelWidget::setSize(const uint width, const uint height) | |||
{ | |||
pData->window.setSize(width, height); | |||
} | |||
void TopLevelWidget::setSize(const Size<uint>& size) | |||
{ | |||
pData->window.setSize(size); | |||
} | |||
bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) | |||
{ | |||
return pData->window.setClipboard(mimeType, data, dataSize); | |||
} | |||
const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize) | |||
{ | |||
return pData->window.getClipboard(mimeType, dataSize); | |||
} | |||
bool TopLevelWidget::setCursor(const MouseCursor cursor) | |||
{ | |||
return pData->window.setCursor(cursor); | |||
} | |||
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(); | |||
} | |||
void TopLevelWidget::repaint() noexcept | |||
{ | |||
pData->window.repaint(); | |||
} | |||
void TopLevelWidget::repaint(const Rectangle<uint>& rect) noexcept | |||
{ | |||
pData->window.repaint(rect); | |||
} | |||
void TopLevelWidget::setGeometryConstraints(const uint minimumWidth, | |||
const uint minimumHeight, | |||
const bool keepAspectRatio, | |||
const bool automaticallyScale, | |||
const bool resizeNowIfAutoScaling) | |||
{ | |||
pData->window.setGeometryConstraints(minimumWidth, | |||
minimumHeight, | |||
keepAspectRatio, | |||
automaticallyScale, | |||
resizeNowIfAutoScaling); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
bool TopLevelWidget::onKeyboard(const KeyboardEvent& ev) | |||
{ | |||
return pData->keyboardEvent(ev); | |||
} | |||
bool TopLevelWidget::onCharacterInput(const CharacterInputEvent& ev) | |||
{ | |||
return pData->characterInputEvent(ev); | |||
} | |||
bool TopLevelWidget::onMouse(const MouseEvent& ev) | |||
{ | |||
return pData->mouseEvent(ev); | |||
} | |||
bool TopLevelWidget::onMotion(const MotionEvent& ev) | |||
{ | |||
return pData->motionEvent(ev); | |||
} | |||
bool TopLevelWidget::onScroll(const ScrollEvent& ev) | |||
{ | |||
return pData->scrollEvent(ev); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
void TopLevelWidget::requestSizeChange(uint, uint) | |||
{ | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,134 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
#include "WindowPrivateData.hpp" | |||
#include "pugl.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) | |||
: self(s), | |||
selfw(s), | |||
window(w) | |||
{ | |||
window.pData->topLevelWidgets.push_back(self); | |||
} | |||
TopLevelWidget::PrivateData::~PrivateData() | |||
{ | |||
window.pData->topLevelWidgets.remove(self); | |||
} | |||
bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) | |||
{ | |||
// ignore event if we are not visible | |||
if (! selfw->pData->visible) | |||
return false; | |||
// propagate event to all subwidgets recursively | |||
return selfw->pData->giveKeyboardEventForSubWidgets(ev); | |||
} | |||
bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev) | |||
{ | |||
// ignore event if we are not visible | |||
if (! selfw->pData->visible) | |||
return false; | |||
// propagate event to all subwidgets recursively | |||
return selfw->pData->giveCharacterInputEventForSubWidgets(ev); | |||
} | |||
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) | |||
{ | |||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||
} | |||
// propagate event to all subwidgets recursively | |||
return selfw->pData->giveMouseEventForSubWidgets(rev); | |||
} | |||
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) | |||
{ | |||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||
} | |||
// propagate event to all subwidgets recursively | |||
return selfw->pData->giveMotionEventForSubWidgets(rev); | |||
} | |||
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) | |||
{ | |||
const double autoScaleFactor = window.pData->autoScaleFactor; | |||
rev.pos.setX(ev.pos.getX() / autoScaleFactor); | |||
rev.pos.setY(ev.pos.getY() / autoScaleFactor); | |||
rev.absolutePos.setX(ev.absolutePos.getX() / autoScaleFactor); | |||
rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); | |||
rev.delta.setX(ev.delta.getX() / autoScaleFactor); | |||
rev.delta.setY(ev.delta.getY() / autoScaleFactor); | |||
} | |||
// propagate event to all subwidgets recursively | |||
return selfw->pData->giveScrollEventForSubWidgets(rev); | |||
} | |||
void TopLevelWidget::PrivateData::fallbackOnResize() | |||
{ | |||
puglFallbackOnResize(window.pData->view); | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,50 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||
#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../TopLevelWidget.hpp" | |||
#include <list> | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
struct TopLevelWidget::PrivateData { | |||
TopLevelWidget* const self; | |||
Widget* const selfw; | |||
Window& window; | |||
explicit PrivateData(TopLevelWidget* self, Window& window); | |||
~PrivateData(); | |||
void display(); | |||
bool keyboardEvent(const KeyboardEvent& 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) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -15,33 +15,22 @@ | |||
*/ | |||
#include "WidgetPrivateData.hpp" | |||
#include "../TopLevelWidget.hpp" | |||
#include "../Window.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Widget | |||
Widget::Widget(Window& parent) | |||
: pData(new PrivateData(this, parent, nullptr, false)) | |||
{ | |||
parent._addWidget(this); | |||
} | |||
Widget::Widget(TopLevelWidget* const topLevelWidget) | |||
: pData(new PrivateData(this, topLevelWidget)) {} | |||
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(Widget* const parentWidget) | |||
: pData(new PrivateData(this, parentWidget)) {} | |||
Widget::~Widget() | |||
{ | |||
pData->parent._removeWidget(this); | |||
delete pData; | |||
} | |||
@@ -50,13 +39,15 @@ 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->parent.repaint(); | |||
pData->visible = visible; | |||
repaint(); | |||
// FIXME check case of hiding a previously visible widget, does it trigger a repaint? | |||
} | |||
void Widget::show() | |||
@@ -79,7 +70,7 @@ uint Widget::getHeight() const noexcept | |||
return pData->size.getHeight(); | |||
} | |||
const Size<uint>& Widget::getSize() const noexcept | |||
const Size<uint> Widget::getSize() const noexcept | |||
{ | |||
return pData->size; | |||
} | |||
@@ -96,7 +87,7 @@ void Widget::setWidth(uint width) noexcept | |||
pData->size.setWidth(width); | |||
onResize(ev); | |||
pData->parent.repaint(); | |||
repaint(); | |||
} | |||
void Widget::setHeight(uint height) noexcept | |||
@@ -111,7 +102,7 @@ void Widget::setHeight(uint height) noexcept | |||
pData->size.setHeight(height); | |||
onResize(ev); | |||
pData->parent.repaint(); | |||
repaint(); | |||
} | |||
void Widget::setSize(uint width, uint height) noexcept | |||
@@ -131,79 +122,34 @@ void Widget::setSize(const Size<uint>& 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<int>& Widget::getAbsolutePos() const noexcept | |||
{ | |||
return pData->absolutePos; | |||
} | |||
void Widget::setAbsoluteX(int x) noexcept | |||
{ | |||
if (pData->absolutePos.getX() == x) | |||
return; | |||
pData->absolutePos.setX(x); | |||
pData->parent.repaint(); | |||
} | |||
void Widget::setAbsoluteY(int y) noexcept | |||
{ | |||
if (pData->absolutePos.getY() == y) | |||
return; | |||
pData->absolutePos.setY(y); | |||
pData->parent.repaint(); | |||
} | |||
void Widget::setAbsolutePos(int x, int y) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(x, y)); | |||
} | |||
void Widget::setAbsolutePos(const Point<int>& pos) noexcept | |||
{ | |||
if (pData->absolutePos == pos) | |||
return; | |||
pData->absolutePos = pos; | |||
pData->parent.repaint(); | |||
repaint(); | |||
} | |||
Application& Widget::getParentApp() const noexcept | |||
Application& Widget::getApp() const noexcept | |||
{ | |||
return pData->parent.getApp(); | |||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||
return pData->topLevelWidget->getApp(); | |||
} | |||
Window& Widget::getParentWindow() const noexcept | |||
Window& Widget::getWindow() const noexcept | |||
{ | |||
return pData->parent; | |||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||
return pData->topLevelWidget->getWindow(); | |||
} | |||
bool Widget::contains(int x, int y) const noexcept | |||
const GraphicsContext& Widget::getGraphicsContext() const noexcept | |||
{ | |||
return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight()); | |||
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr); | |||
return pData->topLevelWidget->getWindow().getGraphicsContext(); | |||
} | |||
bool Widget::contains(const Point<int>& pos) const noexcept | |||
TopLevelWidget* Widget::getTopLevelWidget() const noexcept | |||
{ | |||
return contains(pos.getX(), pos.getY()); | |||
return pData->topLevelWidget; | |||
} | |||
void Widget::repaint() noexcept | |||
{ | |||
pData->parent.repaint(); | |||
} | |||
uint Widget::getId() const noexcept | |||
@@ -216,35 +162,38 @@ void Widget::setId(uint id) noexcept | |||
pData->id = id; | |||
} | |||
bool Widget::onKeyboard(const KeyboardEvent&) | |||
bool Widget::onKeyboard(const KeyboardEvent& ev) | |||
{ | |||
return false; | |||
return pData->giveKeyboardEventForSubWidgets(ev); | |||
} | |||
bool Widget::onSpecial(const SpecialEvent&) | |||
bool Widget::onCharacterInput(const CharacterInputEvent& ev) | |||
{ | |||
return false; | |||
return pData->giveCharacterInputEventForSubWidgets(ev); | |||
} | |||
bool Widget::onMouse(const MouseEvent&) | |||
bool Widget::onMouse(const MouseEvent& ev) | |||
{ | |||
return false; | |||
MouseEvent rev = ev; | |||
return pData->giveMouseEventForSubWidgets(rev); | |||
} | |||
bool Widget::onMotion(const MotionEvent&) | |||
bool Widget::onMotion(const MotionEvent& ev) | |||
{ | |||
return false; | |||
MotionEvent rev = ev; | |||
return pData->giveMotionEventForSubWidgets(rev); | |||
} | |||
bool Widget::onScroll(const ScrollEvent&) | |||
bool Widget::onScroll(const ScrollEvent& ev) | |||
{ | |||
return false; | |||
ScrollEvent rev = ev; | |||
return pData->giveScrollEventForSubWidgets(rev); | |||
} | |||
void Widget::onResize(const ResizeEvent&) | |||
{ | |||
} | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -0,0 +1,229 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "WidgetPrivateData.hpp" | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "../TopLevelWidget.hpp" | |||
START_NAMESPACE_DGL | |||
#define FOR_EACH_SUBWIDGET(it) \ | |||
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||
#define FOR_EACH_SUBWIDGET_INV(rit) \ | |||
for (std::list<SubWidget*>::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit) | |||
// ----------------------------------------------------------------------- | |||
Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) | |||
: self(s), | |||
topLevelWidget(tlw), | |||
parentWidget(nullptr), | |||
id(0), | |||
needsScaling(false), | |||
visible(true), | |||
size(0, 0), | |||
subWidgets() {} | |||
Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) | |||
: self(s), | |||
topLevelWidget(findTopLevelWidget(pw)), | |||
parentWidget(pw), | |||
id(0), | |||
needsScaling(false), | |||
visible(true), | |||
size(0, 0), | |||
subWidgets() {} | |||
Widget::PrivateData::~PrivateData() | |||
{ | |||
subWidgets.clear(); | |||
} | |||
void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) | |||
{ | |||
if (subWidgets.size() == 0) | |||
return; | |||
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||
{ | |||
SubWidget* const subwidget(*it); | |||
if (subwidget->isVisible()) | |||
subwidget->pData->display(width, height, autoScaleFactor); | |||
} | |||
} | |||
// ----------------------------------------------------------------------- | |||
bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev) | |||
{ | |||
if (! visible) | |||
return false; | |||
if (subWidgets.size() == 0) | |||
return false; | |||
FOR_EACH_SUBWIDGET_INV(rit) | |||
{ | |||
SubWidget* const widget(*rit); | |||
if (widget->isVisible() && widget->onKeyboard(ev)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const 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(MouseEvent& ev) | |||
{ | |||
if (! visible) | |||
return false; | |||
if (subWidgets.size() == 0) | |||
return false; | |||
const double x = ev.absolutePos.getX(); | |||
const double y = ev.absolutePos.getY(); | |||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||
{ | |||
if (selfw->pData->needsViewportScaling) | |||
{ | |||
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); | |||
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); | |||
} | |||
} | |||
FOR_EACH_SUBWIDGET_INV(rit) | |||
{ | |||
SubWidget* const widget(*rit); | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onMouse(ev)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) | |||
{ | |||
if (! visible) | |||
return false; | |||
if (subWidgets.size() == 0) | |||
return false; | |||
const double x = ev.absolutePos.getX(); | |||
const double y = ev.absolutePos.getY(); | |||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||
{ | |||
if (selfw->pData->needsViewportScaling) | |||
{ | |||
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); | |||
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); | |||
} | |||
} | |||
FOR_EACH_SUBWIDGET_INV(rit) | |||
{ | |||
SubWidget* const widget(*rit); | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onMotion(ev)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) | |||
{ | |||
if (! visible) | |||
return false; | |||
if (subWidgets.size() == 0) | |||
return false; | |||
const double x = ev.absolutePos.getX(); | |||
const double y = ev.absolutePos.getY(); | |||
if (SubWidget* const selfw = dynamic_cast<SubWidget*>(self)) | |||
{ | |||
if (selfw->pData->needsViewportScaling) | |||
{ | |||
ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); | |||
ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); | |||
} | |||
} | |||
FOR_EACH_SUBWIDGET_INV(rit) | |||
{ | |||
SubWidget* const widget(*rit); | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onScroll(ev)) | |||
return true; | |||
} | |||
return false; | |||
} | |||
// ----------------------------------------------------------------------- | |||
TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw) | |||
{ | |||
if (pw->pData->topLevelWidget != nullptr) | |||
return pw->pData->topLevelWidget; | |||
if (pw->pData->parentWidget != nullptr) | |||
return findTopLevelWidget(pw->pData->parentWidget); | |||
return nullptr; | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,119 +18,43 @@ | |||
#define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../Widget.hpp" | |||
#include "../Window.hpp" | |||
#include <vector> | |||
#include <list> | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct Widget::PrivateData { | |||
Widget* const self; | |||
Window& parent; | |||
Point<int> absolutePos; | |||
Size<uint> size; | |||
std::vector<Widget*> subWidgets; | |||
TopLevelWidget* const topLevelWidget; | |||
Widget* const parentWidget; | |||
uint id; | |||
bool needsFullViewport; | |||
bool needsScaling; | |||
bool skipDisplay; | |||
bool visible; | |||
Size<uint> size; | |||
std::list<SubWidget*> subWidgets; | |||
PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets) | |||
: self(s), | |||
parent(p), | |||
absolutePos(0, 0), | |||
size(0, 0), | |||
subWidgets(), | |||
id(0), | |||
needsFullViewport(false), | |||
needsScaling(false), | |||
skipDisplay(false), | |||
visible(true) | |||
{ | |||
if (addToSubWidgets && groupWidget != nullptr) | |||
{ | |||
skipDisplay = true; | |||
groupWidget->pData->subWidgets.push_back(self); | |||
} | |||
} | |||
~PrivateData() | |||
{ | |||
subWidgets.clear(); | |||
} | |||
void display(const uint width, const uint height, 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<uint>(width, height))) | |||
{ | |||
// full viewport size | |||
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
} | |||
else if (needsScaling) | |||
{ | |||
// limit viewport to widget bounds | |||
glViewport(absolutePos.getX(), | |||
static_cast<int>(height - self->getHeight()) - absolutePos.getY(), | |||
static_cast<GLsizei>(self->getWidth()), | |||
static_cast<GLsizei>(self->getHeight())); | |||
} | |||
else | |||
{ | |||
// only set viewport pos | |||
glViewport(absolutePos.getX(), | |||
/*static_cast<int>(height - self->getHeight())*/ - absolutePos.getY(), | |||
static_cast<GLsizei>(width), | |||
static_cast<GLsizei>(height)); | |||
// then cut the outer bounds | |||
glScissor(absolutePos.getX(), | |||
static_cast<int>(height - self->getHeight()) - absolutePos.getY(), | |||
static_cast<GLsizei>(self->getWidth()), | |||
static_cast<GLsizei>(self->getHeight())); | |||
glEnable(GL_SCISSOR_TEST); | |||
needsDisableScissor = true; | |||
} | |||
// display widget | |||
self->onDisplay(); | |||
if (needsDisableScissor) | |||
{ | |||
glDisable(GL_SCISSOR_TEST); | |||
needsDisableScissor = false; | |||
} | |||
// called via TopLevelWidget | |||
explicit PrivateData(Widget* const s, TopLevelWidget* const tlw); | |||
// called via SubWidget | |||
explicit PrivateData(Widget* const s, Widget* const pw); | |||
~PrivateData(); | |||
displaySubWidgets(width, height); | |||
} | |||
void displaySubWidgets(uint width, uint height, double autoScaleFactor); | |||
void displaySubWidgets(const uint width, const uint height) | |||
{ | |||
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it) | |||
{ | |||
Widget* const widget(*it); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | |||
bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev); | |||
bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev); | |||
bool giveMouseEventForSubWidgets(MouseEvent& ev); | |||
bool giveMotionEventForSubWidgets(MotionEvent& ev); | |||
bool giveScrollEventForSubWidgets(ScrollEvent& ev); | |||
widget->pData->display(width, height, true); | |||
} | |||
} | |||
static TopLevelWidget* findTopLevelWidget(Widget* const w); | |||
DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData) | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
@@ -0,0 +1,199 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED | |||
#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../Window.hpp" | |||
#include "../Widget.hpp" | |||
#include "ApplicationPrivateData.hpp" | |||
#include "pugl.hpp" | |||
#include <list> | |||
START_NAMESPACE_DGL | |||
class TopLevelWidget; | |||
// ----------------------------------------------------------------------- | |||
struct Window::PrivateData : IdleCallback { | |||
/** 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. */ | |||
Application::PrivateData* const appData; | |||
/** Pointer to the the DGL Window class that this private data belongs to. */ | |||
Window* const self; | |||
/** Pugl view instance. */ | |||
PuglView* view; | |||
/** Pugl view instance of the transient parent window. */ | |||
PuglView* const transientParentView; | |||
/** Reserved space for graphics context. */ | |||
mutable uint8_t graphicsContext[sizeof(void*)]; | |||
/** The top-level widgets associated with this Window. */ | |||
std::list<TopLevelWidget*> topLevelWidgets; | |||
/** 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; | |||
/** Whether to ignore resize requests and feed them into the host instead. used for VST3 */ | |||
const bool usesSizeRequest; | |||
/** Scale factor to report to widgets on request, purely informational. */ | |||
double scaleFactor; | |||
/** Automatic scaling to apply on widgets, implemented internally. */ | |||
bool autoScaling; | |||
double autoScaleFactor; | |||
/** Pugl geometry constraints access. */ | |||
uint minWidth, minHeight; | |||
bool keepAspectRatio; | |||
/** Whether to ignore idle callback requests, useful for temporary windows. */ | |||
bool ignoreIdleCallbacks; | |||
/** Whether to ignore pugl events (except create and destroy), used for puglGetClipboard. */ | |||
bool ignoreEvents; | |||
/** Render to a picture file when non-null, automatically free+unset after saving. */ | |||
char* filenameToRenderInto; | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** Handle for file browser dialog operations. */ | |||
FileBrowserHandle fileBrowserHandle; | |||
#endif | |||
/** Modal window setup. */ | |||
struct Modal { | |||
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() noexcept | |||
: parent(nullptr), | |||
child(nullptr), | |||
enabled(false) {} | |||
/** Constructor for a modal window (with a parent). */ | |||
Modal(PrivateData* const p) noexcept | |||
: parent(p), | |||
child(nullptr), | |||
enabled(false) {} | |||
/** Destructor. */ | |||
~Modal() noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT(! enabled); | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(Modal) | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
} 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 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, bool isVST3); | |||
/** Destructor. */ | |||
~PrivateData() override; | |||
/** Helper initialization function called at the end of all this class constructors. */ | |||
void initPre(uint width, uint height, bool resizable); | |||
/** Helper initialization function called on the Window constructor after we are done. */ | |||
bool initPost(); | |||
/** 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. | |||
* | |||
* @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 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); | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
// file handling | |||
bool openFileBrowser(const FileBrowserOptions& options); | |||
#endif | |||
static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); | |||
// modal handling | |||
void startModal(); | |||
void stopModal(); | |||
void runAsModal(bool blockWait); | |||
// pugl events | |||
void onPuglConfigure(double width, double height); | |||
void onPuglExpose(); | |||
void onPuglClose(); | |||
void onPuglFocus(bool focus, CrossingMode mode); | |||
void onPuglKey(const Widget::KeyboardEvent& 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); | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED |
@@ -38,10 +38,15 @@ enum FONSalign { | |||
FONS_ALIGN_BASELINE = 1<<6, // Default | |||
}; | |||
enum FONSglyphBitmap { | |||
FONS_GLYPH_BITMAP_OPTIONAL = 1, | |||
FONS_GLYPH_BITMAP_REQUIRED = 2, | |||
}; | |||
enum FONSerrorCode { | |||
// Font atlas is full. | |||
FONS_ATLAS_FULL = 1, | |||
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||
// Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||
FONS_SCRATCH_FULL = 2, | |||
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. | |||
FONS_STATES_OVERFLOW = 3, | |||
@@ -78,6 +83,7 @@ struct FONStextIter { | |||
const char* next; | |||
const char* end; | |||
unsigned int utf8state; | |||
int bitmapOption; | |||
}; | |||
typedef struct FONStextIter FONStextIter; | |||
@@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s); | |||
void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | |||
// Returns current atlas size. | |||
void fonsGetAtlasSize(FONScontext* s, int* width, int* height); | |||
// Expands the atlas size. | |||
// Expands the atlas size. | |||
int fonsExpandAtlas(FONScontext* s, int width, int height); | |||
// Resets the whole stash. | |||
int fonsResetAtlas(FONScontext* stash, int width, int height); | |||
// Add fonts | |||
int fonsAddFont(FONScontext* s, const char* name, const char* path); | |||
int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData); | |||
int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); | |||
int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); | |||
int fonsGetFontByName(FONScontext* s, const char* name); | |||
// State handling | |||
@@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); | |||
void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); | |||
// Text iterator | |||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); | |||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption); | |||
int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); | |||
// Pull texture changes | |||
@@ -151,91 +157,6 @@ struct FONSttFontImpl { | |||
}; | |||
typedef struct FONSttFontImpl FONSttFontImpl; | |||
static FT_Library ftLibrary; | |||
int fons__tt_init(FONScontext *context) | |||
{ | |||
FT_Error ftError; | |||
FONS_NOTUSED(context); | |||
ftError = FT_Init_FreeType(&ftLibrary); | |||
return ftError == 0; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||
{ | |||
FT_Error ftError; | |||
FONS_NOTUSED(context); | |||
//font->font.userdata = stash; | |||
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); | |||
return ftError == 0; | |||
} | |||
void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) | |||
{ | |||
*ascent = font->font->ascender; | |||
*descent = font->font->descender; | |||
*lineGap = font->font->height - (*ascent - *descent); | |||
} | |||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | |||
{ | |||
return size / (font->font->ascender - font->font->descender); | |||
} | |||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | |||
{ | |||
return FT_Get_Char_Index(font->font, codepoint); | |||
} | |||
int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, | |||
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) | |||
{ | |||
FT_Error ftError; | |||
FT_GlyphSlot ftGlyph; | |||
FONS_NOTUSED(scale); | |||
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | |||
if (ftError) return 0; | |||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | |||
if (ftError) return 0; | |||
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance); | |||
if (ftError) return 0; | |||
ftGlyph = font->font->glyph; | |||
*lsb = ftGlyph->metrics.horiBearingX; | |||
*x0 = ftGlyph->bitmap_left; | |||
*x1 = *x0 + ftGlyph->bitmap.width; | |||
*y0 = -ftGlyph->bitmap_top; | |||
*y1 = *y0 + ftGlyph->bitmap.rows; | |||
return 1; | |||
} | |||
void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, | |||
float scaleX, float scaleY, int glyph) | |||
{ | |||
FT_GlyphSlot ftGlyph = font->font->glyph; | |||
int ftGlyphOffset = 0; | |||
int x, y; | |||
FONS_NOTUSED(outWidth); | |||
FONS_NOTUSED(outHeight); | |||
FONS_NOTUSED(scaleX); | |||
FONS_NOTUSED(scaleY); | |||
FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap | |||
for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { | |||
for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { | |||
output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; | |||
} | |||
} | |||
} | |||
int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||
{ | |||
FT_Vector ftKerning; | |||
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | |||
return ftKerning.x; | |||
} | |||
#else | |||
#define STB_TRUETYPE_IMPLEMENTATION | |||
@@ -250,61 +171,10 @@ struct FONSttFontImpl { | |||
}; | |||
typedef struct FONSttFontImpl FONSttFontImpl; | |||
int fons__tt_init(FONScontext *context) | |||
{ | |||
FONS_NOTUSED(context); | |||
return 1; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize) | |||
{ | |||
int stbError; | |||
FONS_NOTUSED(dataSize); | |||
font->font.userdata = context; | |||
stbError = stbtt_InitFont(&font->font, data, 0); | |||
return stbError; | |||
} | |||
void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) | |||
{ | |||
stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); | |||
} | |||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | |||
{ | |||
return stbtt_ScaleForPixelHeight(&font->font, size); | |||
} | |||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | |||
{ | |||
return stbtt_FindGlyphIndex(&font->font, codepoint); | |||
} | |||
int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, | |||
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) | |||
{ | |||
FONS_NOTUSED(size); | |||
stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); | |||
stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); | |||
return 1; | |||
} | |||
void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, | |||
float scaleX, float scaleY, int glyph) | |||
{ | |||
stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); | |||
} | |||
int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||
{ | |||
return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); | |||
} | |||
#endif | |||
#ifndef FONS_SCRATCH_BUF_SIZE | |||
# define FONS_SCRATCH_BUF_SIZE 16000 | |||
# define FONS_SCRATCH_BUF_SIZE 96000 | |||
#endif | |||
#ifndef FONS_HASH_LUT_SIZE | |||
# define FONS_HASH_LUT_SIZE 256 | |||
@@ -324,6 +194,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||
#ifndef FONS_MAX_STATES | |||
# define FONS_MAX_STATES 20 | |||
#endif | |||
#ifndef FONS_MAX_FALLBACKS | |||
# define FONS_MAX_FALLBACKS 20 | |||
#endif | |||
static unsigned int fons__hashint(unsigned int a) | |||
{ | |||
@@ -361,7 +234,7 @@ struct FONSfont | |||
{ | |||
FONSttFontImpl font; | |||
char name[64]; | |||
const unsigned char* data; | |||
unsigned char* data; | |||
int dataSize; | |||
unsigned char freeData; | |||
float ascender; | |||
@@ -371,6 +244,8 @@ struct FONSfont | |||
int cglyphs; | |||
int nglyphs; | |||
int lut[FONS_HASH_LUT_SIZE]; | |||
int fallbacks[FONS_MAX_FALLBACKS]; | |||
int nfallbacks; | |||
}; | |||
typedef struct FONSfont FONSfont; | |||
@@ -419,8 +294,191 @@ struct FONScontext | |||
int nstates; | |||
void (*handleError)(void* uptr, int error, int val); | |||
void* errorUptr; | |||
#ifdef FONS_USE_FREETYPE | |||
FT_Library ftLibrary; | |||
#endif | |||
}; | |||
#ifdef FONS_USE_FREETYPE | |||
int fons__tt_init(FONScontext *context) | |||
{ | |||
FT_Error ftError; | |||
ftError = FT_Init_FreeType(&context->ftLibrary); | |||
return ftError == 0; | |||
} | |||
int fons__tt_done(FONScontext *context) | |||
{ | |||
FT_Error ftError; | |||
ftError = FT_Done_FreeType(context->ftLibrary); | |||
return ftError == 0; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||
{ | |||
FT_Error ftError; | |||
//font->font.userdata = stash; | |||
ftError = FT_New_Memory_Face(context->ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); | |||
return ftError == 0; | |||
} | |||
void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) | |||
{ | |||
*ascent = font->font->ascender; | |||
*descent = font->font->descender; | |||
*lineGap = font->font->height - (*ascent - *descent); | |||
} | |||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | |||
{ | |||
#if 1 | |||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||
return size / (font->font->ascender - font->font->descender); | |||
#else | |||
return size / font->font->units_per_EM; | |||
#endif | |||
} | |||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | |||
{ | |||
return FT_Get_Char_Index(font->font, codepoint); | |||
} | |||
int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, | |||
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) | |||
{ | |||
FT_Error ftError; | |||
FT_GlyphSlot ftGlyph; | |||
FT_Fixed advFixed; | |||
FONS_NOTUSED(scale); | |||
#if 1 | |||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); | |||
#else | |||
ftError = FT_Set_Pixel_Sizes(font->font, 0, size); | |||
#endif | |||
if (ftError) return 0; | |||
#if 1 | |||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); | |||
#else | |||
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); | |||
#endif | |||
if (ftError) return 0; | |||
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); | |||
if (ftError) return 0; | |||
ftGlyph = font->font->glyph; | |||
*advance = (int)advFixed; | |||
*lsb = (int)ftGlyph->metrics.horiBearingX; | |||
*x0 = ftGlyph->bitmap_left; | |||
*x1 = *x0 + ftGlyph->bitmap.width; | |||
*y0 = -ftGlyph->bitmap_top; | |||
*y1 = *y0 + ftGlyph->bitmap.rows; | |||
return 1; | |||
} | |||
void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, | |||
float scaleX, float scaleY, int glyph) | |||
{ | |||
FT_GlyphSlot ftGlyph = font->font->glyph; | |||
int ftGlyphOffset = 0; | |||
unsigned int x, y; | |||
FONS_NOTUSED(outWidth); | |||
FONS_NOTUSED(outHeight); | |||
FONS_NOTUSED(scaleX); | |||
FONS_NOTUSED(scaleY); | |||
FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap | |||
for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { | |||
for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { | |||
output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; | |||
} | |||
} | |||
} | |||
int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||
{ | |||
FT_Vector ftKerning; | |||
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); | |||
return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer | |||
} | |||
#else | |||
int fons__tt_init(FONScontext *context) | |||
{ | |||
FONS_NOTUSED(context); | |||
return 1; | |||
} | |||
int fons__tt_done(FONScontext *context) | |||
{ | |||
FONS_NOTUSED(context); | |||
return 1; | |||
} | |||
int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) | |||
{ | |||
int offset, stbError; | |||
FONS_NOTUSED(dataSize); | |||
font->font.userdata = context; | |||
offset = stbtt_GetFontOffsetForIndex(data, fontIndex); | |||
if (offset == -1) { | |||
stbError = 0; | |||
} else { | |||
stbError = stbtt_InitFont(&font->font, data, offset); | |||
} | |||
return stbError; | |||
} | |||
void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) | |||
{ | |||
stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); | |||
} | |||
float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) | |||
{ | |||
#if 1 | |||
// Note(DPF) maintain pixel-based units for compat after nanovg update | |||
return stbtt_ScaleForPixelHeight(&font->font, size); | |||
#else | |||
return stbtt_ScaleForMappingEmToPixels(&font->font, size); | |||
#endif | |||
} | |||
int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) | |||
{ | |||
return stbtt_FindGlyphIndex(&font->font, codepoint); | |||
} | |||
int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, | |||
int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) | |||
{ | |||
FONS_NOTUSED(size); | |||
stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); | |||
stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); | |||
return 1; | |||
} | |||
void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, | |||
float scaleX, float scaleY, int glyph) | |||
{ | |||
stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); | |||
} | |||
int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) | |||
{ | |||
return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); | |||
} | |||
#endif | |||
#ifdef STB_TRUETYPE_IMPLEMENTATION | |||
static void* fons__tmpalloc(size_t size, void* up) | |||
{ | |||
unsigned char* ptr; | |||
@@ -446,6 +504,8 @@ static void fons__tmpfree(void* ptr, void* up) | |||
// empty | |||
} | |||
#endif // STB_TRUETYPE_IMPLEMENTATION | |||
// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> | |||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. | |||
@@ -751,6 +811,27 @@ static FONSstate* fons__getState(FONScontext* stash) | |||
return &stash->states[stash->nstates-1]; | |||
} | |||
int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) | |||
{ | |||
FONSfont* baseFont = stash->fonts[base]; | |||
if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { | |||
baseFont->fallbacks[baseFont->nfallbacks++] = fallback; | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
void fonsResetFallbackFont(FONScontext* stash, int base) | |||
{ | |||
int i; | |||
FONSfont* baseFont = stash->fonts[base]; | |||
baseFont->nfallbacks = 0; | |||
baseFont->nglyphs = 0; | |||
for (i = 0; i < FONS_HASH_LUT_SIZE; i++) | |||
baseFont->lut[i] = -1; | |||
} | |||
void fonsSetSize(FONScontext* stash, float size) | |||
{ | |||
fons__getState(stash)->size = size; | |||
@@ -818,7 +899,7 @@ static void fons__freeFont(FONSfont* font) | |||
{ | |||
if (font == NULL) return; | |||
if (font->glyphs) free(font->glyphs); | |||
if (font->freeData && font->data) free((void*)font->data); | |||
if (font->freeData && font->data) free(font->data); | |||
free(font); | |||
} | |||
@@ -830,6 +911,8 @@ static int fons__allocFont(FONScontext* stash) | |||
stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); | |||
if (stash->fonts == NULL) | |||
return -1; | |||
for (int i=stash->nfonts; i<stash->cfonts; ++i) | |||
stash->fonts[i] = NULL; | |||
} | |||
font = (FONSfont*)malloc(sizeof(FONSfont)); | |||
if (font == NULL) goto error; | |||
@@ -849,12 +932,12 @@ error: | |||
return FONS_INVALID; | |||
} | |||
int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) | |||
{ | |||
FILE* fp = 0; | |||
int dataSize = 0; | |||
size_t readed; | |||
unsigned char* data = NULL; | |||
size_t ignore; | |||
// Read in the font data. | |||
fp = fopen(path, "rb"); | |||
@@ -864,28 +947,30 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path) | |||
fseek(fp,0,SEEK_SET); | |||
data = (unsigned char*)malloc(dataSize); | |||
if (data == NULL) goto error; | |||
ignore = fread(data, 1, dataSize, fp); | |||
readed = fread(data, 1, dataSize, fp); | |||
fclose(fp); | |||
fp = 0; | |||
if (readed != (size_t)dataSize) goto error; | |||
return fonsAddFontMem(stash, name, data, dataSize, 1); | |||
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); | |||
error: | |||
if (data) free(data); | |||
if (fp) fclose(fp); | |||
return FONS_INVALID; | |||
FONS_NOTUSED(ignore); | |||
} | |||
int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData) | |||
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) | |||
{ | |||
int i, ascent, descent, fh, lineGap; | |||
FONSfont* font; | |||
int idx = fons__allocFont(stash); | |||
if (idx == FONS_INVALID) | |||
{ | |||
if (freeData && data) free(data); | |||
return FONS_INVALID; | |||
} | |||
font = stash->fonts[idx]; | |||
@@ -903,15 +988,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da | |||
// Init font | |||
stash->nscratch = 0; | |||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; | |||
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; | |||
// Store normalized line height. The real line height is got | |||
// by multiplying the lineh by font size. | |||
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); | |||
ascent += lineGap; | |||
fh = ascent - descent; | |||
font->ascender = (float)ascent / (float)fh; | |||
font->descender = (float)descent / (float)fh; | |||
font->lineh = (float)(fh + lineGap) / (float)fh; | |||
font->lineh = font->ascender - font->descender; | |||
return idx; | |||
@@ -938,6 +1024,8 @@ static FONSglyph* fons__allocGlyph(FONSfont* font) | |||
font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; | |||
font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); | |||
if (font->glyphs == NULL) return NULL; | |||
for (int i=font->nglyphs; i<font->cglyphs; ++i) | |||
memset(&font->glyphs[i], 0, sizeof(*font->glyphs)); | |||
} | |||
font->nglyphs++; | |||
return &font->glyphs[font->nglyphs-1]; | |||
@@ -1010,7 +1098,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int | |||
} | |||
static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, | |||
short isize, short iblur) | |||
short isize, short iblur, int bitmapOption) | |||
{ | |||
int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; | |||
float scale; | |||
@@ -1020,6 +1108,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||
int pad, added; | |||
unsigned char* bdst; | |||
unsigned char* dst; | |||
FONSfont* renderFont = font; | |||
if (isize < 2) return NULL; | |||
if (iblur > 20) iblur = 20; | |||
@@ -1032,32 +1121,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||
h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); | |||
i = font->lut[h]; | |||
while (i != -1) { | |||
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) | |||
return &font->glyphs[i]; | |||
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) { | |||
glyph = &font->glyphs[i]; | |||
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) { | |||
return glyph; | |||
} | |||
// At this point, glyph exists but the bitmap data is not yet created. | |||
break; | |||
} | |||
i = font->glyphs[i].next; | |||
} | |||
// Could not find glyph, create it. | |||
scale = fons__tt_getPixelHeightScale(&font->font, size); | |||
// Create a new glyph or rasterize bitmap data for a cached glyph. | |||
g = fons__tt_getGlyphIndex(&font->font, codepoint); | |||
fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||
// Try to find the glyph in fallback fonts. | |||
if (g == 0) { | |||
for (i = 0; i < font->nfallbacks; ++i) { | |||
FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; | |||
int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); | |||
if (fallbackIndex != 0) { | |||
g = fallbackIndex; | |||
renderFont = fallbackFont; | |||
break; | |||
} | |||
} | |||
// It is possible that we did not find a fallback glyph. | |||
// In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. | |||
} | |||
scale = fons__tt_getPixelHeightScale(&renderFont->font, size); | |||
fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); | |||
gw = x1-x0 + pad*2; | |||
gh = y1-y0 + pad*2; | |||
// Find free spot for the rect in the atlas | |||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||
if (added == 0 && stash->handleError != NULL) { | |||
// Atlas is full, let the user to resize the atlas (or not), and try again. | |||
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||
// Determines the spot to draw glyph in the atlas. | |||
if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) { | |||
// Find free spot for the rect in the atlas | |||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||
if (added == 0 && stash->handleError != NULL) { | |||
// Atlas is full, let the user to resize the atlas (or not), and try again. | |||
stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); | |||
added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); | |||
} | |||
if (added == 0) return NULL; | |||
} else { | |||
// Negative coordinate indicates there is no bitmap data created. | |||
gx = -1; | |||
gy = -1; | |||
} | |||
if (added == 0) return NULL; | |||
// Init glyph. | |||
glyph = fons__allocGlyph(font); | |||
glyph->codepoint = codepoint; | |||
glyph->size = isize; | |||
glyph->blur = iblur; | |||
if (glyph == NULL) { | |||
glyph = fons__allocGlyph(font); | |||
glyph->codepoint = codepoint; | |||
glyph->size = isize; | |||
glyph->blur = iblur; | |||
glyph->next = 0; | |||
// Insert char to hash lookup. | |||
glyph->next = font->lut[h]; | |||
font->lut[h] = font->nglyphs-1; | |||
} | |||
glyph->index = g; | |||
glyph->x0 = (short)gx; | |||
glyph->y0 = (short)gy; | |||
@@ -1066,15 +1189,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||
glyph->xadv = (short)(scale * advance * 10.0f); | |||
glyph->xoff = (short)(x0 - pad); | |||
glyph->yoff = (short)(y0 - pad); | |||
glyph->next = 0; | |||
// Insert char to hash lookup. | |||
glyph->next = font->lut[h]; | |||
font->lut[h] = font->nglyphs-1; | |||
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) { | |||
return glyph; | |||
} | |||
// Rasterize | |||
dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; | |||
fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); | |||
fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g); | |||
// Make sure there is one pixel empty border. | |||
dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | |||
@@ -1101,7 +1223,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in | |||
if (iblur > 0) { | |||
stash->nscratch = 0; | |||
bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; | |||
fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); | |||
fons__blur(stash, bdst, gw, gh, stash->params.width, iblur); | |||
} | |||
stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); | |||
@@ -1134,8 +1256,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||
y1 = (float)(glyph->y1-1); | |||
if (stash->params.flags & FONS_ZERO_TOPLEFT) { | |||
rx = (float)(int)(*x + xoff); | |||
ry = (float)(int)(*y + yoff); | |||
rx = floorf(*x + xoff); | |||
ry = floorf(*y + yoff); | |||
q->x0 = rx; | |||
q->y0 = ry; | |||
@@ -1147,8 +1269,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, | |||
q->s1 = x1 * stash->itw; | |||
q->t1 = y1 * stash->ith; | |||
} else { | |||
rx = (float)(int)(*x + xoff); | |||
ry = (float)(int)(*y - yoff); | |||
rx = floorf(*x + xoff); | |||
ry = floorf(*y - yoff); | |||
q->x0 = rx; | |||
q->y0 = ry; | |||
@@ -1226,7 +1348,7 @@ float fonsDrawText(FONScontext* stash, | |||
const char* str, const char* end) | |||
{ | |||
FONSstate* state = fons__getState(stash); | |||
unsigned int codepoint = 0; | |||
unsigned int codepoint; | |||
unsigned int utf8state = 0; | |||
FONSglyph* glyph = NULL; | |||
FONSquad q; | |||
@@ -1263,7 +1385,7 @@ float fonsDrawText(FONScontext* stash, | |||
for (; str != end; ++str) { | |||
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | |||
continue; | |||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); | |||
if (glyph != NULL) { | |||
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | |||
@@ -1286,7 +1408,7 @@ float fonsDrawText(FONScontext* stash, | |||
} | |||
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | |||
float x, float y, const char* str, const char* end) | |||
float x, float y, const char* str, const char* end, int bitmapOption) | |||
{ | |||
FONSstate* state = fons__getState(stash); | |||
float width; | |||
@@ -1326,6 +1448,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, | |||
iter->end = end; | |||
iter->codepoint = 0; | |||
iter->prevGlyphIndex = -1; | |||
iter->bitmapOption = bitmapOption; | |||
return 1; | |||
} | |||
@@ -1346,7 +1469,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) | |||
// Get glyph and quad | |||
iter->x = iter->nextx; | |||
iter->y = iter->nexty; | |||
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); | |||
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption); | |||
// If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid. | |||
if (glyph != NULL) | |||
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | |||
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; | |||
@@ -1406,12 +1530,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y) | |||
} | |||
float fonsTextBounds(FONScontext* stash, | |||
float x, float y, | |||
float x, float y, | |||
const char* str, const char* end, | |||
float* bounds) | |||
{ | |||
FONSstate* state = fons__getState(stash); | |||
unsigned int codepoint = 0; | |||
unsigned int codepoint; | |||
unsigned int utf8state = 0; | |||
FONSquad q; | |||
FONSglyph* glyph = NULL; | |||
@@ -1443,7 +1567,7 @@ float fonsTextBounds(FONScontext* stash, | |||
for (; str != end; ++str) { | |||
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) | |||
continue; | |||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); | |||
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); | |||
if (glyph != NULL) { | |||
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); | |||
if (q.x0 < minx) minx = q.x0; | |||
@@ -1567,6 +1691,7 @@ void fonsDeleteInternal(FONScontext* stash) | |||
if (stash->fonts) free(stash->fonts); | |||
if (stash->texData) free(stash->texData); | |||
if (stash->scratch) free(stash->scratch); | |||
fons__tt_done(stash); | |||
free(stash); | |||
} | |||
@@ -1594,7 +1719,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height) | |||
height = fons__maxi(height, stash->params.height); | |||
if (width == stash->params.width && height == stash->params.height) | |||
return 1; | |||
return 1; | |||
// Flush pending glyphs. | |||
fons__flush(stash); | |||
@@ -79,10 +79,46 @@ enum NVGalign { | |||
// Vertical align | |||
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. | |||
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. | |||
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. | |||
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. | |||
}; | |||
enum NVGblendFactor { | |||
NVG_ZERO = 1<<0, | |||
NVG_ONE = 1<<1, | |||
NVG_SRC_COLOR = 1<<2, | |||
NVG_ONE_MINUS_SRC_COLOR = 1<<3, | |||
NVG_DST_COLOR = 1<<4, | |||
NVG_ONE_MINUS_DST_COLOR = 1<<5, | |||
NVG_SRC_ALPHA = 1<<6, | |||
NVG_ONE_MINUS_SRC_ALPHA = 1<<7, | |||
NVG_DST_ALPHA = 1<<8, | |||
NVG_ONE_MINUS_DST_ALPHA = 1<<9, | |||
NVG_SRC_ALPHA_SATURATE = 1<<10, | |||
}; | |||
enum NVGcompositeOperation { | |||
NVG_SOURCE_OVER, | |||
NVG_SOURCE_IN, | |||
NVG_SOURCE_OUT, | |||
NVG_ATOP, | |||
NVG_DESTINATION_OVER, | |||
NVG_DESTINATION_IN, | |||
NVG_DESTINATION_OUT, | |||
NVG_DESTINATION_ATOP, | |||
NVG_LIGHTER, | |||
NVG_COPY, | |||
NVG_XOR, | |||
}; | |||
struct NVGcompositeOperationState { | |||
int srcRGB; | |||
int dstRGB; | |||
int srcAlpha; | |||
int dstAlpha; | |||
}; | |||
typedef struct NVGcompositeOperationState NVGcompositeOperationState; | |||
struct NVGglyphPosition { | |||
const char* str; // Position of the glyph in the input string. | |||
float x; // The x-coordinate of the logical glyph position. | |||
@@ -100,11 +136,20 @@ struct NVGtextRow { | |||
typedef struct NVGtextRow NVGtextRow; | |||
enum NVGimageFlags { | |||
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. | |||
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. | |||
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. | |||
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. | |||
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. | |||
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear | |||
}; | |||
enum NVGtexture { | |||
NVG_TEXTURE_ALPHA, | |||
NVG_TEXTURE_BGR, | |||
NVG_TEXTURE_BGRA, | |||
NVG_TEXTURE_RGB, | |||
NVG_TEXTURE_RGBA, | |||
}; | |||
// Begin drawing a new frame | |||
@@ -115,7 +160,7 @@ enum NVGimageFlags { | |||
// For example, GLFW returns two dimension for an opened window: window size and | |||
// frame buffer size. In that case you would set windowWidth/Height to the window size | |||
// devicePixelRatio to: frameBufferWidth / windowWidth. | |||
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); | |||
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); | |||
// Cancels drawing the current frame. | |||
void nvgCancelFrame(NVGcontext* ctx); | |||
@@ -123,6 +168,22 @@ void nvgCancelFrame(NVGcontext* ctx); | |||
// Ends drawing flushing remaining render state. | |||
void nvgEndFrame(NVGcontext* ctx); | |||
// | |||
// Composite operation | |||
// | |||
// The composite operations in NanoVG are modeled after HTML Canvas API, and | |||
// the blend func is based on OpenGL (see corresponding manuals for more info). | |||
// The colors in the blending state have premultiplied alpha. | |||
// Sets the composite operation. The op parameter should be one of NVGcompositeOperation. | |||
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); | |||
// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. | |||
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); | |||
// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. | |||
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); | |||
// | |||
// Color utils | |||
// | |||
@@ -183,7 +244,10 @@ void nvgReset(NVGcontext* ctx); | |||
// Solid color is simply defined as a color value, different kinds of paints can be created | |||
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). | |||
// | |||
// Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||
// Current render style can be saved and restored using nvgSave() and nvgRestore(). | |||
// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. | |||
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); | |||
// Sets current stroke style to a solid color. | |||
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); | |||
@@ -215,6 +279,10 @@ void nvgLineJoin(NVGcontext* ctx, int join); | |||
// Sets the transparency applied to all rendered shapes. | |||
// Already transparent paths will get proportionally more transparent as well. | |||
void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||
void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint); | |||
NVGcolor nvgGetGlobalTint(NVGcontext* ctx); | |||
void nvgAlpha(NVGcontext* ctx, float alpha); | |||
void nvgTint(NVGcontext* ctx, NVGcolor tint); | |||
// | |||
// Transforms | |||
@@ -231,7 +299,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha); | |||
// Apart from nvgResetTransform(), each transformation function first creates | |||
// specific transformation matrix and pre-multiplies the current transformation by it. | |||
// | |||
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). | |||
// Resets current transform to a identity matrix. | |||
void nvgResetTransform(NVGcontext* ctx); | |||
@@ -317,7 +385,11 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); | |||
// Creates image by loading it from the specified chunk of memory. | |||
// Returns handle to the image. | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); | |||
// Creates image from specified image data and texture format. | |||
// Returns handle to the image. | |||
int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, enum NVGtexture format, const unsigned char* data); | |||
// Creates image from specified image data. | |||
// Returns handle to the image. | |||
@@ -358,7 +430,7 @@ NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, | |||
NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, | |||
NVGcolor icol, NVGcolor ocol); | |||
// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, | |||
// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, | |||
// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. | |||
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). | |||
NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, | |||
@@ -368,7 +440,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey | |||
// Scissoring | |||
// | |||
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various | |||
// user interface cases like rendering a text edit or a timeline. | |||
// user interface cases like rendering a text edit or a timeline. | |||
// Sets the current scissor rectangle. | |||
// The scissor rectangle is transformed by the current transform. | |||
@@ -423,7 +495,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad | |||
// Closes current sub-path with a line segment. | |||
void nvgClosePath(NVGcontext* ctx); | |||
// Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||
// Sets the current sub-path winding, see NVGwinding and NVGsolidity. | |||
void nvgPathWinding(NVGcontext* ctx, int dir); | |||
// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, | |||
@@ -437,10 +509,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); | |||
// Creates new rounded rectangle shaped sub-path. | |||
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); | |||
// Creates new rounded rectangle shaped sub-path with varying radii for each corner. | |||
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); | |||
// Creates new ellipse shaped sub-path. | |||
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); | |||
// Creates new circle shaped sub-path. | |||
// Creates new circle shaped sub-path. | |||
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); | |||
// Fills the current path with current fill style. | |||
@@ -487,13 +562,31 @@ void nvgStroke(NVGcontext* ctx); | |||
// Returns handle to the font. | |||
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); | |||
// Creates image by loading it from the specified memory chunk. | |||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); | |||
// Creates font by loading it from the specified memory chunk. | |||
// Returns handle to the font. | |||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData); | |||
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); | |||
// fontIndex specifies which font face to load from a .ttf/.ttc file. | |||
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); | |||
// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. | |||
int nvgFindFont(NVGcontext* ctx, const char* name); | |||
// Adds a fallback font by handle. | |||
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); | |||
// Adds a fallback font by name. | |||
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); | |||
// Resets fallback fonts by handle. | |||
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); | |||
// Resets fallback fonts by name. | |||
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); | |||
// Sets the font size of current text style. | |||
void nvgFontSize(NVGcontext* ctx, float size); | |||
@@ -503,7 +596,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur); | |||
// Sets the letter spacing of current text style. | |||
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); | |||
// Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||
// Sets the proportional line height of current text style. The line height is specified as multiple of font size. | |||
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); | |||
// Sets the text align of current text style, see NVGalign for options. | |||
@@ -550,11 +643,6 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa | |||
// | |||
// Internal Render API | |||
// | |||
enum NVGtexture { | |||
NVG_TEXTURE_ALPHA = 0x01, | |||
NVG_TEXTURE_RGBA = 0x02, | |||
}; | |||
struct NVGscissor { | |||
float xform[6]; | |||
float extent[2]; | |||
@@ -583,23 +671,23 @@ typedef struct NVGpath NVGpath; | |||
struct NVGparams { | |||
void* userPtr; | |||
int edgeAntiAlias; | |||
int (*renderCreate)(void* uptr); | |||
int (*renderCreate)(void* uptr, void* otherUptr); | |||
int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||
int (*renderDeleteTexture)(void* uptr, int image); | |||
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); | |||
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); | |||
void (*renderViewport)(void* uptr, int width, int height); | |||
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); | |||
void (*renderCancel)(void* uptr); | |||
void (*renderFlush)(void* uptr); | |||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); | |||
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); | |||
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); | |||
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); | |||
void (*renderDelete)(void* uptr); | |||
}; | |||
typedef struct NVGparams NVGparams; | |||
// Constructor and destructor, called by the render back-end. | |||
NVGcontext* nvgCreateInternal(NVGparams* params); | |||
NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other); | |||
void nvgDeleteInternal(NVGcontext* ctx); | |||
NVGparams* nvgInternalParams(NVGcontext* ctx); | |||
@@ -57,29 +57,45 @@ enum NVGcreateFlags { | |||
#if defined NANOVG_GL2 | |||
NVGcontext* nvgCreateGL2(int flags); | |||
NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags); | |||
void nvgDeleteGL2(NVGcontext* ctx); | |||
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); | |||
#endif | |||
#if defined NANOVG_GL3 | |||
NVGcontext* nvgCreateGL3(int flags); | |||
NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags); | |||
void nvgDeleteGL3(NVGcontext* ctx); | |||
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); | |||
#endif | |||
#if defined NANOVG_GLES2 | |||
NVGcontext* nvgCreateGLES2(int flags); | |||
NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags); | |||
void nvgDeleteGLES2(NVGcontext* ctx); | |||
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); | |||
#endif | |||
#if defined NANOVG_GLES3 | |||
NVGcontext* nvgCreateGLES3(int flags); | |||
NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags); | |||
void nvgDeleteGLES3(NVGcontext* ctx); | |||
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); | |||
#endif | |||
// These are additional flags on top of NVGimageFlags. | |||
@@ -87,10 +103,6 @@ enum NVGimageFlagsGL { | |||
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. | |||
}; | |||
int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); | |||
GLuint nvglImageHandle(NVGcontext* ctx, int image); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
@@ -142,6 +154,15 @@ struct GLNVGtexture { | |||
}; | |||
typedef struct GLNVGtexture GLNVGtexture; | |||
struct GLNVGblend | |||
{ | |||
GLenum srcRGB; | |||
GLenum dstRGB; | |||
GLenum srcAlpha; | |||
GLenum dstAlpha; | |||
}; | |||
typedef struct GLNVGblend GLNVGblend; | |||
enum GLNVGcallType { | |||
GLNVG_NONE = 0, | |||
GLNVG_FILL, | |||
@@ -158,6 +179,7 @@ struct GLNVGcall { | |||
int triangleOffset; | |||
int triangleCount; | |||
int uniformOffset; | |||
GLNVGblend blendFunc; | |||
}; | |||
typedef struct GLNVGcall GLNVGcall; | |||
@@ -210,13 +232,19 @@ struct GLNVGfragUniforms { | |||
}; | |||
typedef struct GLNVGfragUniforms GLNVGfragUniforms; | |||
struct GLNVGcontext { | |||
GLNVGshader shader; | |||
struct GLNVGtextureContext { // Textures; shared between shared NanoVG contexts. | |||
int refCount; | |||
GLNVGtexture* textures; | |||
float view[2]; | |||
int ntextures; | |||
int ctextures; | |||
int textureId; | |||
}; | |||
typedef struct GLNVGtextureContext GLNVGtextureContext; | |||
struct GLNVGcontext { | |||
GLNVGshader shader; | |||
GLNVGtextureContext* textureContext; | |||
float view[2]; | |||
GLuint vertBuf; | |||
#if defined NANOVG_GL3 | |||
GLuint vertArr; | |||
@@ -248,7 +276,10 @@ struct GLNVGcontext { | |||
GLenum stencilFunc; | |||
GLint stencilFuncRef; | |||
GLuint stencilFuncMask; | |||
GLNVGblend blendFunc; | |||
#endif | |||
int dummyTex; | |||
}; | |||
typedef struct GLNVGcontext GLNVGcontext; | |||
@@ -298,7 +329,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||
if ((gl->stencilFunc != func) || | |||
(gl->stencilFuncRef != ref) || | |||
(gl->stencilFuncMask != mask)) { | |||
gl->stencilFunc = func; | |||
gl->stencilFuncRef = ref; | |||
gl->stencilFuncMask = mask; | |||
@@ -308,53 +339,68 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint | |||
glStencilFunc(func, ref, mask); | |||
#endif | |||
} | |||
static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) | |||
{ | |||
#if NANOVG_GL_USE_STATE_FILTER | |||
if ((gl->blendFunc.srcRGB != blend->srcRGB) || | |||
(gl->blendFunc.dstRGB != blend->dstRGB) || | |||
(gl->blendFunc.srcAlpha != blend->srcAlpha) || | |||
(gl->blendFunc.dstAlpha != blend->dstAlpha)) { | |||
gl->blendFunc = *blend; | |||
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||
} | |||
#else | |||
glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); | |||
#endif | |||
} | |||
static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) | |||
{ | |||
GLNVGtexture* tex = NULL; | |||
int i; | |||
for (i = 0; i < gl->ntextures; i++) { | |||
if (gl->textures[i].id == 0) { | |||
tex = &gl->textures[i]; | |||
for (i = 0; i < gl->textureContext->ntextures; i++) { | |||
if (gl->textureContext->textures[i].id == 0) { | |||
tex = &gl->textureContext->textures[i]; | |||
break; | |||
} | |||
} | |||
if (tex == NULL) { | |||
if (gl->ntextures+1 > gl->ctextures) { | |||
if (gl->textureContext->ntextures+1 > gl->textureContext->ctextures) { | |||
GLNVGtexture* textures; | |||
int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate | |||
textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); | |||
int ctextures = glnvg__maxi(gl->textureContext->ntextures+1, 4) + gl->textureContext->ctextures/2; // 1.5x Overallocate | |||
textures = (GLNVGtexture*)realloc(gl->textureContext->textures, sizeof(GLNVGtexture)*ctextures); | |||
if (textures == NULL) return NULL; | |||
gl->textures = textures; | |||
gl->ctextures = ctextures; | |||
gl->textureContext->textures = textures; | |||
gl->textureContext->ctextures = ctextures; | |||
} | |||
tex = &gl->textures[gl->ntextures++]; | |||
tex = &gl->textureContext->textures[gl->textureContext->ntextures++]; | |||
} | |||
memset(tex, 0, sizeof(*tex)); | |||
tex->id = ++gl->textureId; | |||
tex->id = ++gl->textureContext->textureId; | |||
return tex; | |||
} | |||
static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) | |||
{ | |||
int i; | |||
for (i = 0; i < gl->ntextures; i++) | |||
if (gl->textures[i].id == id) | |||
return &gl->textures[i]; | |||
for (i = 0; i < gl->textureContext->ntextures; i++) | |||
if (gl->textureContext->textures[i].id == id) | |||
return &gl->textureContext->textures[i]; | |||
return NULL; | |||
} | |||
static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||
{ | |||
int i; | |||
for (i = 0; i < gl->ntextures; i++) { | |||
if (gl->textures[i].id == id) { | |||
if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |||
glDeleteTextures(1, &gl->textures[i].tex); | |||
memset(&gl->textures[i], 0, sizeof(gl->textures[i])); | |||
for (i = 0; i < gl->textureContext->ntextures; i++) { | |||
if (gl->textureContext->textures[i].id == id) { | |||
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |||
glDeleteTextures(1, &gl->textureContext->textures[i].tex); | |||
memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); | |||
return 1; | |||
} | |||
} | |||
@@ -363,8 +409,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) | |||
static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) | |||
{ | |||
char str[512+1]; | |||
int len = 0; | |||
GLchar str[512+1]; | |||
GLsizei len = 0; | |||
glGetShaderInfoLog(shader, 512, &len, str); | |||
if (len > 512) len = 512; | |||
str[len] = '\0'; | |||
@@ -373,8 +419,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* | |||
static void glnvg__dumpProgramError(GLuint prog, const char* name) | |||
{ | |||
char str[512+1]; | |||
int len = 0; | |||
GLchar str[512+1]; | |||
GLsizei len = 0; | |||
glGetProgramInfoLog(prog, 512, &len, str); | |||
if (len > 512) len = 512; | |||
str[len] = '\0'; | |||
@@ -466,9 +512,22 @@ static void glnvg__getUniforms(GLNVGshader* shader) | |||
#endif | |||
} | |||
static int glnvg__renderCreate(void* uptr) | |||
static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); | |||
static int glnvg__renderCreate(void* uptr, void* otherUptr) // Share the textures of GLNVGcontext 'otherUptr' if it's non-NULL. | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
if (otherUptr) { | |||
GLNVGcontext* other = (GLNVGcontext*)otherUptr; | |||
gl->textureContext = other->textureContext; | |||
gl->textureContext->refCount++; | |||
} else { | |||
gl->textureContext = (GLNVGtextureContext*)malloc(sizeof(GLNVGtextureContext)); | |||
memset(gl->textureContext, 0, sizeof(GLNVGtextureContext)); | |||
gl->textureContext->refCount = 1; | |||
} | |||
int align = 4; | |||
// TODO: mediump float may not be enough for GLES2 in iOS. | |||
@@ -514,7 +573,7 @@ static int glnvg__renderCreate(void* uptr) | |||
" gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" | |||
"}\n"; | |||
static const char* fillFragShader = | |||
static const char* fillFragShader = | |||
"#ifdef GL_ES\n" | |||
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" | |||
" precision highp float;\n" | |||
@@ -592,6 +651,7 @@ static int glnvg__renderCreate(void* uptr) | |||
" float scissor = scissorMask(fpos);\n" | |||
"#ifdef EDGE_AA\n" | |||
" float strokeAlpha = strokeMask();\n" | |||
" if (strokeAlpha < strokeThr) discard;\n" | |||
"#else\n" | |||
" float strokeAlpha = 1.0;\n" | |||
"#endif\n" | |||
@@ -631,9 +691,6 @@ static int glnvg__renderCreate(void* uptr) | |||
" color *= scissor;\n" | |||
" result = color * innerCol;\n" | |||
" }\n" | |||
"#ifdef EDGE_AA\n" | |||
" if (strokeAlpha < strokeThr) discard;\n" | |||
"#endif\n" | |||
"#ifdef NANOVG_GL3\n" | |||
" outColor = result;\n" | |||
"#else\n" | |||
@@ -663,11 +720,15 @@ static int glnvg__renderCreate(void* uptr) | |||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||
// Create UBOs | |||
glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); | |||
glGenBuffers(1, &gl->fragBuf); | |||
glGenBuffers(1, &gl->fragBuf); | |||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); | |||
#endif | |||
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; | |||
// Some platforms does not allow to have samples to unset textures. | |||
// Create empty one which is bound when there's no texture specified. | |||
gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); | |||
glnvg__checkError(gl, "create done"); | |||
glFinish(); | |||
@@ -690,7 +751,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); | |||
imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||
} | |||
// No mips. | |||
// No mips. | |||
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | |||
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); | |||
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; | |||
@@ -719,23 +780,50 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
} | |||
#endif | |||
if (type == NVG_TEXTURE_RGBA) | |||
switch (type) | |||
{ | |||
case NVG_TEXTURE_BGR: | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_BGRA: | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_RGB: | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_RGBA: | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
else | |||
#if defined(NANOVG_GLES2) | |||
break; | |||
default: | |||
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2) | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
#elif defined(NANOVG_GLES3) | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
#else | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
#endif | |||
break; | |||
} | |||
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); | |||
} else { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |||
} | |||
} else { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||
} else { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
} | |||
} | |||
if (imageFlags & NVG_IMAGE_NEAREST) { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||
} else { | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
} | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
if (imageFlags & NVG_IMAGE_REPEATX) | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |||
@@ -790,22 +878,50 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||
glPixelStorei(GL_UNPACK_SKIP_ROWS, y); | |||
#else | |||
// No support for all of skip, need to update a whole row at a time. | |||
if (tex->type == NVG_TEXTURE_RGBA) | |||
switch (tex->type) | |||
{ | |||
case NVG_TEXTURE_BGR: | |||
data += y*tex->width*3; | |||
break; | |||
case NVG_TEXTURE_BGRA: | |||
data += y*tex->width*4; | |||
else | |||
break; | |||
case NVG_TEXTURE_RGB: | |||
data += y*tex->width*3; | |||
break; | |||
case NVG_TEXTURE_RGBA: | |||
data += y*tex->width*4; | |||
break; | |||
default: | |||
data += y*tex->width; | |||
break; | |||
} | |||
x = 0; | |||
w = tex->width; | |||
#endif | |||
if (tex->type == NVG_TEXTURE_RGBA) | |||
switch (tex->type) | |||
{ | |||
case NVG_TEXTURE_BGR: | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_BGRA: | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_RGB: | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
break; | |||
case NVG_TEXTURE_RGBA: | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
else | |||
#ifdef NANOVG_GLES2 | |||
break; | |||
default: | |||
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2) | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
#else | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | |||
#endif | |||
break; | |||
} | |||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | |||
#ifndef NANOVG_GLES2 | |||
@@ -887,19 +1003,46 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai | |||
tex = glnvg__findTexture(gl, paint->image); | |||
if (tex == NULL) return 0; | |||
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { | |||
float flipped[6]; | |||
nvgTransformScale(flipped, 1.0f, -1.0f); | |||
nvgTransformMultiply(flipped, paint->xform); | |||
nvgTransformInverse(invxform, flipped); | |||
float m1[6], m2[6]; | |||
nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); | |||
nvgTransformMultiply(m1, paint->xform); | |||
nvgTransformScale(m2, 1.0f, -1.0f); | |||
nvgTransformMultiply(m2, m1); | |||
nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); | |||
nvgTransformMultiply(m1, m2); | |||
nvgTransformInverse(invxform, m1); | |||
} else { | |||
nvgTransformInverse(invxform, paint->xform); | |||
} | |||
frag->type = NSVG_SHADER_FILLIMG; | |||
if (tex->type == NVG_TEXTURE_RGBA) | |||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||
switch (tex->type) | |||
{ | |||
case NVG_TEXTURE_BGR: | |||
case NVG_TEXTURE_BGRA: | |||
case NVG_TEXTURE_RGB: | |||
case NVG_TEXTURE_RGBA: | |||
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; | |||
else | |||
break; | |||
default: | |||
frag->texType = 2; | |||
break; | |||
} | |||
#else | |||
switch (tex->type) | |||
{ | |||
case NVG_TEXTURE_BGR: | |||
case NVG_TEXTURE_BGRA: | |||
case NVG_TEXTURE_RGB: | |||
case NVG_TEXTURE_RGBA: | |||
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; | |||
break; | |||
default: | |||
frag->texType = 2.0f; | |||
break; | |||
} | |||
#endif | |||
// printf("frag->texType = %d\n", frag->texType); | |||
} else { | |||
frag->type = NSVG_SHADER_FILLGRAD; | |||
@@ -917,6 +1060,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); | |||
static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||
{ | |||
GLNVGtexture* tex = NULL; | |||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); | |||
#else | |||
@@ -925,19 +1069,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) | |||
#endif | |||
if (image != 0) { | |||
GLNVGtexture* tex = glnvg__findTexture(gl, image); | |||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||
glnvg__checkError(gl, "tex paint tex"); | |||
} else { | |||
glnvg__bindTexture(gl, 0); | |||
tex = glnvg__findTexture(gl, image); | |||
} | |||
// If no image is set, use empty texture | |||
if (tex == NULL) { | |||
tex = glnvg__findTexture(gl, gl->dummyTex); | |||
} | |||
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); | |||
glnvg__checkError(gl, "tex paint tex"); | |||
} | |||
static void glnvg__renderViewport(void* uptr, int width, int height) | |||
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) | |||
{ | |||
NVG_NOTUSED(devicePixelRatio); | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
gl->view[0] = (float)width; | |||
gl->view[1] = (float)height; | |||
gl->view[0] = width; | |||
gl->view[1] = height; | |||
} | |||
static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | |||
@@ -979,7 +1126,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) | |||
// Draw fill | |||
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); | |||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | |||
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); | |||
glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); | |||
glDisable(GL_STENCIL_TEST); | |||
} | |||
@@ -992,12 +1139,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) | |||
glnvg__setUniforms(gl, call->uniformOffset, call->image); | |||
glnvg__checkError(gl, "convex fill"); | |||
for (i = 0; i < npaths; i++) | |||
for (i = 0; i < npaths; i++) { | |||
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); | |||
if (gl->flags & NVG_ANTIALIAS) { | |||
// Draw fringes | |||
for (i = 0; i < npaths; i++) | |||
if (paths[i].strokeCount > 0) { | |||
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | |||
} | |||
} | |||
} | |||
@@ -1026,7 +1173,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) | |||
for (i = 0; i < npaths; i++) | |||
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); | |||
// Clear stencil buffer. | |||
// Clear stencil buffer. | |||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | |||
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); | |||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); | |||
@@ -1064,6 +1211,50 @@ static void glnvg__renderCancel(void* uptr) { | |||
gl->nuniforms = 0; | |||
} | |||
static GLenum glnvg_convertBlendFuncFactor(int factor) | |||
{ | |||
if (factor == NVG_ZERO) | |||
return GL_ZERO; | |||
if (factor == NVG_ONE) | |||
return GL_ONE; | |||
if (factor == NVG_SRC_COLOR) | |||
return GL_SRC_COLOR; | |||
if (factor == NVG_ONE_MINUS_SRC_COLOR) | |||
return GL_ONE_MINUS_SRC_COLOR; | |||
if (factor == NVG_DST_COLOR) | |||
return GL_DST_COLOR; | |||
if (factor == NVG_ONE_MINUS_DST_COLOR) | |||
return GL_ONE_MINUS_DST_COLOR; | |||
if (factor == NVG_SRC_ALPHA) | |||
return GL_SRC_ALPHA; | |||
if (factor == NVG_ONE_MINUS_SRC_ALPHA) | |||
return GL_ONE_MINUS_SRC_ALPHA; | |||
if (factor == NVG_DST_ALPHA) | |||
return GL_DST_ALPHA; | |||
if (factor == NVG_ONE_MINUS_DST_ALPHA) | |||
return GL_ONE_MINUS_DST_ALPHA; | |||
if (factor == NVG_SRC_ALPHA_SATURATE) | |||
return GL_SRC_ALPHA_SATURATE; | |||
return GL_INVALID_ENUM; | |||
} | |||
static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) | |||
{ | |||
GLNVGblend blend; | |||
blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); | |||
blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); | |||
blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); | |||
blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); | |||
if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) | |||
{ | |||
blend.srcRGB = GL_ONE; | |||
blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; | |||
blend.srcAlpha = GL_ONE; | |||
blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; | |||
} | |||
return blend; | |||
} | |||
static void glnvg__renderFlush(void* uptr) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
@@ -1074,7 +1265,6 @@ static void glnvg__renderFlush(void* uptr) | |||
// Setup require GL state. | |||
glUseProgram(gl->shader.prog); | |||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||
glEnable(GL_CULL_FACE); | |||
glCullFace(GL_BACK); | |||
glFrontFace(GL_CCW); | |||
@@ -1093,6 +1283,10 @@ static void glnvg__renderFlush(void* uptr) | |||
gl->stencilFunc = GL_ALWAYS; | |||
gl->stencilFuncRef = 0; | |||
gl->stencilFuncMask = 0xffffffff; | |||
gl->blendFunc.srcRGB = GL_INVALID_ENUM; | |||
gl->blendFunc.srcAlpha = GL_INVALID_ENUM; | |||
gl->blendFunc.dstRGB = GL_INVALID_ENUM; | |||
gl->blendFunc.dstAlpha = GL_INVALID_ENUM; | |||
#endif | |||
#if NANOVG_GL_USE_UNIFORMBUFFER | |||
@@ -1122,6 +1316,7 @@ static void glnvg__renderFlush(void* uptr) | |||
for (i = 0; i < gl->ncalls; i++) { | |||
GLNVGcall* call = &gl->calls[i]; | |||
glnvg__blendFuncSeparate(gl,&call->blendFunc); | |||
if (call->type == GLNVG_FILL) | |||
glnvg__fill(gl, call); | |||
else if (call->type == GLNVG_CONVEXFILL) | |||
@@ -1136,9 +1331,9 @@ static void glnvg__renderFlush(void* uptr) | |||
glDisableVertexAttribArray(1); | |||
#if defined NANOVG_GL3 | |||
glBindVertexArray(0); | |||
#endif | |||
#endif | |||
glDisable(GL_CULL_FACE); | |||
glBindBuffer(GL_ARRAY_BUFFER, 0); | |||
glBindBuffer(GL_ARRAY_BUFFER, 0); | |||
glUseProgram(0); | |||
glnvg__bindTexture(gl, 0); | |||
} | |||
@@ -1237,7 +1432,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) | |||
vtx->v = v; | |||
} | |||
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||
const float* bounds, const NVGpath* paths, int npaths) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
@@ -1249,16 +1444,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||
if (call == NULL) return; | |||
call->type = GLNVG_FILL; | |||
call->triangleCount = 4; | |||
call->pathOffset = glnvg__allocPaths(gl, npaths); | |||
if (call->pathOffset == -1) goto error; | |||
call->pathCount = npaths; | |||
call->image = paint->image; | |||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||
if (npaths == 1 && paths[0].convex) | |||
{ | |||
call->type = GLNVG_CONVEXFILL; | |||
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill | |||
} | |||
// Allocate vertices for all the paths. | |||
maxverts = glnvg__maxVertCount(paths, npaths) + 6; | |||
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; | |||
offset = glnvg__allocVerts(gl, maxverts); | |||
if (offset == -1) goto error; | |||
@@ -1280,20 +1480,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||
} | |||
} | |||
// Quad | |||
call->triangleOffset = offset; | |||
call->triangleCount = 6; | |||
quad = &gl->verts[call->triangleOffset]; | |||
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f); | |||
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f); | |||
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f); | |||
glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f); | |||
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f); | |||
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f); | |||
// Setup uniforms for draw calls | |||
if (call->type == GLNVG_FILL) { | |||
// Quad | |||
call->triangleOffset = offset; | |||
quad = &gl->verts[call->triangleOffset]; | |||
glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); | |||
glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); | |||
glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); | |||
glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); | |||
call->uniformOffset = glnvg__allocFragUniforms(gl, 2); | |||
if (call->uniformOffset == -1) goto error; | |||
// Simple shader for stencil | |||
@@ -1318,7 +1514,7 @@ error: | |||
if (gl->ncalls > 0) gl->ncalls--; | |||
} | |||
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, | |||
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, | |||
float strokeWidth, const NVGpath* paths, int npaths) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
@@ -1332,6 +1528,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor | |||
if (call->pathOffset == -1) goto error; | |||
call->pathCount = npaths; | |||
call->image = paint->image; | |||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||
// Allocate vertices for all the paths. | |||
maxverts = glnvg__maxVertCount(paths, npaths); | |||
@@ -1373,8 +1570,8 @@ error: | |||
if (gl->ncalls > 0) gl->ncalls--; | |||
} | |||
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor, | |||
const NVGvertex* verts, int nverts) | |||
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, | |||
const NVGvertex* verts, int nverts, float fringe) | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)uptr; | |||
GLNVGcall* call = glnvg__allocCall(gl); | |||
@@ -1384,6 +1581,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||
call->type = GLNVG_TRIANGLES; | |||
call->image = paint->image; | |||
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); | |||
// Allocate vertices for all the paths. | |||
call->triangleOffset = glnvg__allocVerts(gl, nverts); | |||
@@ -1396,7 +1594,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis | |||
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); | |||
if (call->uniformOffset == -1) goto error; | |||
frag = nvg__fragUniformPtr(gl, call->uniformOffset); | |||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); | |||
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); | |||
frag->type = NSVG_SHADER_IMG; | |||
return; | |||
@@ -1426,11 +1624,14 @@ static void glnvg__renderDelete(void* uptr) | |||
if (gl->vertBuf != 0) | |||
glDeleteBuffers(1, &gl->vertBuf); | |||
for (i = 0; i < gl->ntextures; i++) { | |||
if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |||
glDeleteTextures(1, &gl->textures[i].tex); | |||
if (gl->textureContext != NULL && --gl->textureContext->refCount == 0) { | |||
for (i = 0; i < gl->textureContext->ntextures; i++) { | |||
if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) | |||
glDeleteTextures(1, &gl->textureContext->textures[i].tex); | |||
} | |||
free(gl->textureContext->textures); | |||
free(gl->textureContext); | |||
} | |||
free(gl->textures); | |||
free(gl->paths); | |||
free(gl->verts); | |||
@@ -1450,6 +1651,28 @@ NVGcontext* nvgCreateGLES2(int flags) | |||
#elif defined NANOVG_GLES3 | |||
NVGcontext* nvgCreateGLES3(int flags) | |||
#endif | |||
{ | |||
#if defined NANOVG_GL2 | |||
return nvgCreateSharedGL2(NULL, flags); | |||
#elif defined NANOVG_GL3 | |||
return nvgCreateSharedGL3(NULL, flags); | |||
#elif defined NANOVG_GLES2 | |||
return nvgCreateSharedGLES2(NULL, flags); | |||
#elif defined NANOVG_GLES3 | |||
return nvgCreateSharedGLES3(NULL, flags); | |||
#endif | |||
} | |||
// Share the fonts and textures of 'other' if it's non-NULL. | |||
#if defined NANOVG_GL2 | |||
NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags) | |||
#elif defined NANOVG_GL3 | |||
NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags) | |||
#elif defined NANOVG_GLES2 | |||
NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags) | |||
#elif defined NANOVG_GLES3 | |||
NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags) | |||
#endif | |||
{ | |||
NVGparams params; | |||
NVGcontext* ctx = NULL; | |||
@@ -1475,7 +1698,7 @@ NVGcontext* nvgCreateGLES3(int flags) | |||
gl->flags = flags; | |||
ctx = nvgCreateInternal(¶ms); | |||
ctx = nvgCreateInternal(¶ms, other); | |||
if (ctx == NULL) goto error; | |||
return ctx; | |||
@@ -1499,7 +1722,15 @@ void nvgDeleteGLES3(NVGcontext* ctx) | |||
nvgDeleteInternal(ctx); | |||
} | |||
int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||
#if defined NANOVG_GL2 | |||
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||
#elif defined NANOVG_GL3 | |||
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||
#elif defined NANOVG_GLES2 | |||
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||
#elif defined NANOVG_GLES3 | |||
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) | |||
#endif | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | |||
GLNVGtexture* tex = glnvg__allocTexture(gl); | |||
@@ -1515,7 +1746,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i | |||
return tex->id; | |||
} | |||
GLuint nvglImageHandle(NVGcontext* ctx, int image) | |||
#if defined NANOVG_GL2 | |||
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image) | |||
#elif defined NANOVG_GL3 | |||
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image) | |||
#elif defined NANOVG_GLES2 | |||
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image) | |||
#elif defined NANOVG_GLES3 | |||
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image) | |||
#endif | |||
{ | |||
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; | |||
GLNVGtexture* tex = glnvg__findTexture(gl, image); | |||
@@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer; | |||
// Helper function to create GL frame buffer to render to. | |||
void nvgluBindFramebuffer(NVGLUframebuffer* fb); | |||
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); | |||
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb); | |||
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb); | |||
#endif // NANOVG_GL_UTILS_H | |||
@@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||
memset(fb, 0, sizeof(NVGLUframebuffer)); | |||
fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); | |||
fb->texture = nvglImageHandle(ctx, fb->image); | |||
#if defined NANOVG_GL2 | |||
fb->texture = nvglImageHandleGL2(ctx, fb->image); | |||
#elif defined NANOVG_GL3 | |||
fb->texture = nvglImageHandleGL3(ctx, fb->image); | |||
#elif defined NANOVG_GLES2 | |||
fb->texture = nvglImageHandleGLES2(ctx, fb->image); | |||
#elif defined NANOVG_GLES3 | |||
fb->texture = nvglImageHandleGLES3(ctx, fb->image); | |||
#endif | |||
fb->ctx = ctx; | |||
// frame buffer object | |||
glGenFramebuffers(1, &fb->fbo); | |||
@@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | |||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | |||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; | |||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | |||
#ifdef GL_DEPTH24_STENCIL8 | |||
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback. | |||
// Some graphics cards require a depth buffer along with a stencil. | |||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); | |||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); | |||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); | |||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) | |||
#endif // GL_DEPTH24_STENCIL8 | |||
goto error; | |||
} | |||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | |||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | |||
@@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag | |||
error: | |||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); | |||
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); | |||
nvgluDeleteFramebuffer(ctx, fb); | |||
nvgluDeleteFramebuffer(fb); | |||
return NULL; | |||
#else | |||
NVG_NOTUSED(ctx); | |||
@@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb) | |||
#endif | |||
} | |||
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb) | |||
{ | |||
#ifdef NANOVG_FBO_VALID | |||
if (fb == NULL) return; | |||
@@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) | |||
if (fb->rbo != 0) | |||
glDeleteRenderbuffers(1, &fb->rbo); | |||
if (fb->image >= 0) | |||
nvgDeleteImage(ctx, fb->image); | |||
nvgDeleteImage(fb->ctx, fb->image); | |||
fb->ctx = NULL; | |||
fb->fbo = 0; | |||
fb->rbo = 0; | |||
fb->texture = 0; | |||
fb->image = -1; | |||
free(fb); | |||
#else | |||
NVG_NOTUSED(ctx); | |||
NVG_NOTUSED(fb); | |||
#endif | |||
} | |||
@@ -0,0 +1 @@ | |||
Subproject commit b1d9703ecbdb0a033fe0b9acdf58b90f7d81a8e5 |
@@ -0,0 +1,675 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "pugl.hpp" | |||
/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ | |||
#include <cassert> | |||
#include <cmath> | |||
#include <cstdlib> | |||
#include <cstring> | |||
#include <ctime> | |||
#if defined(DISTRHO_OS_MAC) | |||
# import <Cocoa/Cocoa.h> | |||
# include <dlfcn.h> | |||
# include <mach/mach_time.h> | |||
# ifdef DGL_CAIRO | |||
# include <cairo.h> | |||
# include <cairo-quartz.h> | |||
# endif | |||
# ifdef DGL_OPENGL | |||
# include <OpenGL/gl.h> | |||
# endif | |||
# ifdef DGL_VULKAN | |||
# import <QuartzCore/CAMetalLayer.h> | |||
# include <vulkan/vulkan_core.h> | |||
# include <vulkan/vulkan_macos.h> | |||
# endif | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
# include <wctype.h> | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
# include <windowsx.h> | |||
# ifdef DGL_CAIRO | |||
# include <cairo.h> | |||
# include <cairo-win32.h> | |||
# endif | |||
# ifdef DGL_OPENGL | |||
# include <GL/gl.h> | |||
# endif | |||
# ifdef DGL_VULKAN | |||
# include <vulkan/vulkan.h> | |||
# include <vulkan/vulkan_win32.h> | |||
# endif | |||
#else | |||
# include <dlfcn.h> | |||
# include <unistd.h> | |||
# include <sys/select.h> | |||
# include <sys/time.h> | |||
# include <X11/X.h> | |||
# include <X11/Xatom.h> | |||
# include <X11/Xlib.h> | |||
# include <X11/Xresource.h> | |||
# include <X11/Xutil.h> | |||
# include <X11/keysym.h> | |||
# ifdef HAVE_XCURSOR | |||
# include <X11/Xcursor/Xcursor.h> | |||
# include <X11/cursorfont.h> | |||
# endif | |||
# ifdef HAVE_XRANDR | |||
# include <X11/extensions/Xrandr.h> | |||
# endif | |||
# ifdef HAVE_XSYNC | |||
# include <X11/extensions/sync.h> | |||
# include <X11/extensions/syncconst.h> | |||
# endif | |||
# ifdef DGL_CAIRO | |||
# include <cairo.h> | |||
# include <cairo-xlib.h> | |||
# endif | |||
# ifdef DGL_OPENGL | |||
# include <GL/gl.h> | |||
# include <GL/glx.h> | |||
# endif | |||
# ifdef DGL_VULKAN | |||
# include <vulkan/vulkan_core.h> | |||
# include <vulkan/vulkan_xlib.h> | |||
# endif | |||
#endif | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# ifdef DISTRHO_OS_MAC | |||
# import "../../distrho/extra/FileBrowserDialog.cpp" | |||
# else | |||
# include "../../distrho/extra/FileBrowserDialog.cpp" | |||
# endif | |||
#endif | |||
#ifndef DISTRHO_OS_MAC | |||
START_NAMESPACE_DGL | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#if defined(DISTRHO_OS_MAC) | |||
# 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 | |||
# ifndef __MAC_10_9 | |||
# define NSModalResponseOK NSOKButton | |||
# endif | |||
# pragma clang diagnostic push | |||
# pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
# 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 | |||
# pragma clang diagnostic pop | |||
#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" | |||
# 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" | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// expose backend enter | |||
bool puglBackendEnter(PuglView* const view) | |||
{ | |||
return view->backend->enter(view, nullptr) == PUGL_SUCCESS; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// expose backend leave | |||
void puglBackendLeave(PuglView* const view) | |||
{ | |||
view->backend->leave(view, nullptr); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// clear minimum size to 0 | |||
void puglClearMinSize(PuglView* const view) | |||
{ | |||
view->minWidth = 0; | |||
view->minHeight = 0; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// missing in pugl, directly returns transient parent | |||
PuglNativeView puglGetTransientParent(const PuglView* const view) | |||
{ | |||
return view->transientParent; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// missing in pugl, directly returns title char* pointer | |||
const char* puglGetWindowTitle(const PuglView* const view) | |||
{ | |||
return view->title; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// get global scale factor | |||
double puglGetDesktopScaleFactor(const PuglView* const view) | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
if (NSWindow* const window = view->impl->window ? view->impl->window | |||
: [view->impl->wrapperView window]) | |||
return [window screen].backingScaleFactor; | |||
return [NSScreen mainScreen].backingScaleFactor; | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
if (const HMODULE Shcore = LoadLibraryA("Shcore.dll")) | |||
{ | |||
typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*); | |||
typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wcast-function-type" | |||
# endif | |||
const PFN_GetProcessDpiAwareness GetProcessDpiAwareness | |||
= (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness"); | |||
const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor | |||
= (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor"); | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic pop | |||
# endif | |||
DWORD dpiAware = 0; | |||
if (GetProcessDpiAwareness && GetScaleFactorForMonitor | |||
&& GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0) | |||
{ | |||
const HMONITOR hMon = MonitorFromWindow(view->impl->hwnd, MONITOR_DEFAULTTOPRIMARY); | |||
DWORD scaleFactor = 0; | |||
if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0) | |||
{ | |||
FreeLibrary(Shcore); | |||
return static_cast<double>(scaleFactor) / 100.0; | |||
} | |||
} | |||
FreeLibrary(Shcore); | |||
} | |||
#elif defined(HAVE_X11) | |||
XrmInitialize(); | |||
if (char* const rms = XResourceManagerString(view->world->impl->display)) | |||
{ | |||
if (const XrmDatabase sdb = XrmGetStringDatabase(rms)) | |||
{ | |||
char* type = nullptr; | |||
XrmValue ret; | |||
if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret) | |||
&& ret.addr != nullptr | |||
&& type != nullptr | |||
&& std::strncmp("String", type, 6) == 0) | |||
{ | |||
if (const double dpi = std::atof(ret.addr)) | |||
return dpi / 96; | |||
} | |||
} | |||
} | |||
#else | |||
// unused | |||
(void)view; | |||
#endif | |||
return 1.0; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// bring view window into the foreground, aka "raise" window | |||
void puglRaiseWindow(PuglView* const view) | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
if (NSWindow* const window = view->impl->window ? view->impl->window | |||
: [view->impl->wrapperView window]) | |||
[window orderFrontRegardless]; | |||
#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* const view) | |||
{ | |||
#ifdef DGL_CAIRO | |||
puglSetBackend(view, puglCairoBackend()); | |||
#endif | |||
#ifdef DGL_OPENGL | |||
puglSetBackend(view, puglGlBackend()); | |||
#endif | |||
#ifdef DGL_VULKAN | |||
puglSetBackend(view, puglVulkanBackend()); | |||
#endif | |||
if (view->backend == nullptr) | |||
puglSetBackend(view, puglStubBackend()); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Combine puglSetMinSize and puglSetAspectRatio | |||
PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect) | |||
{ | |||
view->minWidth = (int)width; | |||
view->minHeight = (int)height; | |||
if (aspect) { | |||
view->minAspectX = (int)width; | |||
view->minAspectY = (int)height; | |||
view->maxAspectX = (int)width; | |||
view->maxAspectY = (int)height; | |||
} | |||
#if defined(DISTRHO_OS_MAC) | |||
puglSetMinSize(view, width, height); | |||
if (aspect) { | |||
puglSetAspectRatio(view, width, height, width, height); | |||
} | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
// nothing | |||
#else | |||
if (const PuglStatus status = updateSizeHints(view)) | |||
return status; | |||
XFlush(view->impl->display); | |||
#endif | |||
return PUGL_SUCCESS; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// set window offset without changing size | |||
PuglStatus puglSetWindowOffset(PuglView* const view, const int x, const int y) | |||
{ | |||
// TODO custom setFrame version | |||
PuglRect rect = puglGetFrame(view); | |||
rect.x = x; | |||
rect.y = y; | |||
return puglSetFrame(view, rect); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// set window size with default size and without changing frame x/y position | |||
PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height) | |||
{ | |||
view->defaultWidth = width; | |||
view->defaultHeight = height; | |||
view->frame.width = width; | |||
view->frame.height = height; | |||
#if defined(DISTRHO_OS_MAC) | |||
// replace setFrame with setFrameSize | |||
PuglInternals* const impl = view->impl; | |||
const PuglRect frame = view->frame; | |||
const NSRect framePx = rectToNsRect(frame); | |||
const NSRect framePt = nsRectToPoints(view, framePx); | |||
if (impl->window) | |||
{ | |||
// Resize window to fit new content rect | |||
const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
[impl->window setFrame:winFrame display:NO]; | |||
} | |||
// Resize views | |||
const NSSize sizePx = NSMakeSize(frame.width, frame.height); | |||
const NSSize sizePt = [impl->drawView convertSizeFromBacking:sizePx]; | |||
[impl->wrapperView setFrameSize:(impl->window ? sizePt : framePt.size)]; | |||
[impl->drawView setFrameSize:sizePt]; | |||
#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, | |||
(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)) | |||
{ | |||
// make sure to return context back to ourselves | |||
view->backend->enter(view, nullptr); | |||
return PUGL_SUCCESS; | |||
} | |||
return PUGL_UNKNOWN_ERROR; | |||
} | |||
#else | |||
// matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow | |||
if (view->impl->win) | |||
{ | |||
Display* const display = view->impl->display; | |||
if (! XResizeWindow(display, view->impl->win, width, height)) | |||
return PUGL_UNKNOWN_ERROR; | |||
if (const PuglStatus status = updateSizeHints(view)) | |||
return status; | |||
XFlush(display); | |||
} | |||
#endif | |||
return PUGL_SUCCESS; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// DGL specific, build-specific drawing prepare | |||
void puglOnDisplayPrepare(PuglView*) | |||
{ | |||
#ifdef DGL_OPENGL | |||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||
glLoadIdentity(); | |||
#endif | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// DGL specific, build-specific fallback resize | |||
void puglFallbackOnResize(PuglView* const 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<GLdouble>(view->frame.width), static_cast<GLdouble>(view->frame.height), 0.0, 0.0, 1.0); | |||
glViewport(0, 0, static_cast<GLsizei>(view->frame.width), static_cast<GLsizei>(view->frame.height)); | |||
glMatrixMode(GL_MODELVIEW); | |||
glLoadIdentity(); | |||
#endif | |||
} | |||
#if defined(DISTRHO_OS_MAC) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// macOS specific, allow standalone window to gain focus | |||
void puglMacOSActivateApp() | |||
{ | |||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | |||
[NSApp activateIgnoringOtherApps:YES]; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// macOS specific, add another view's window as child | |||
PuglStatus | |||
puglMacOSAddChildWindow(PuglView* const view, PuglView* const child) | |||
{ | |||
if (NSWindow* const viewWindow = view->impl->window ? view->impl->window | |||
: [view->impl->wrapperView window]) | |||
{ | |||
if (NSWindow* const childWindow = child->impl->window ? child->impl->window | |||
: [child->impl->wrapperView window]) | |||
{ | |||
[viewWindow addChildWindow:childWindow ordered:NSWindowAbove]; | |||
return PUGL_SUCCESS; | |||
} | |||
} | |||
return PUGL_FAILURE; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// macOS specific, remove another view's window as child | |||
PuglStatus | |||
puglMacOSRemoveChildWindow(PuglView* const view, PuglView* const child) | |||
{ | |||
if (NSWindow* const viewWindow = view->impl->window ? view->impl->window | |||
: [view->impl->wrapperView window]) | |||
{ | |||
if (NSWindow* const childWindow = child->impl->window ? child->impl->window | |||
: [child->impl->wrapperView window]) | |||
{ | |||
[viewWindow removeChildWindow:childWindow]; | |||
return PUGL_SUCCESS; | |||
} | |||
} | |||
return PUGL_FAILURE; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// macOS specific, center view based on parent coordinates (if there is one) | |||
void puglMacOSShowCentered(PuglView* const view) | |||
{ | |||
if (puglShow(view) != PUGL_SUCCESS) | |||
return; | |||
if (view->transientParent != 0) | |||
{ | |||
NSWindow* const transientWindow = [(NSView*)view->transientParent window]; | |||
DISTRHO_SAFE_ASSERT_RETURN(transientWindow != nullptr,); | |||
const NSRect ourFrame = [view->impl->window frame]; | |||
const NSRect transientFrame = [transientWindow frame]; | |||
const int x = transientFrame.origin.x + transientFrame.size.width / 2 - ourFrame.size.width / 2; | |||
const int y = transientFrame.origin.y + transientFrame.size.height / 2 + ourFrame.size.height / 2; | |||
[view->impl->window setFrameTopLeftPoint:NSMakePoint(x, y)]; | |||
} | |||
else | |||
{ | |||
[view->impl->window center]; | |||
} | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#elif defined(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 puglWin32ShowCentered(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 | |||
{ | |||
#ifdef DGL_WINDOWS_ICON_ID | |||
WNDCLASSEX wClass; | |||
std::memset(&wClass, 0, sizeof(wClass)); | |||
const HINSTANCE hInstance = GetModuleHandle(nullptr); | |||
if (GetClassInfoEx(hInstance, view->world->className, &wClass)) | |||
wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)); | |||
SetClassLongPtr(impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); | |||
#endif | |||
MONITORINFO mInfo; | |||
std::memset(&mInfo, 0, sizeof(mInfo)); | |||
mInfo.cbSize = sizeof(mInfo); | |||
if (GetMonitorInfo(MonitorFromWindow(impl->hwnd, MONITOR_DEFAULTTOPRIMARY), &mInfo)) | |||
SetWindowPos(impl->hwnd, | |||
HWND_TOP, | |||
mInfo.rcWork.left + (mInfo.rcWork.right - view->frame.width) / 2, | |||
mInfo.rcWork.top + (mInfo.rcWork.bottom - view->frame.height) / 2, | |||
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); | |||
else | |||
ShowWindow(impl->hwnd, SW_NORMAL); | |||
} | |||
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); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#elif defined(HAVE_X11) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// X11 specific, safer way to grab focus | |||
PuglStatus puglX11GrabFocus(const PuglView* const view) | |||
{ | |||
const PuglInternals* const impl = view->impl; | |||
XWindowAttributes wa; | |||
std::memset(&wa, 0, sizeof(wa)); | |||
DISTRHO_SAFE_ASSERT_RETURN(XGetWindowAttributes(impl->display, impl->win, &wa), PUGL_UNKNOWN_ERROR); | |||
if (wa.map_state == IsViewable) | |||
{ | |||
XRaiseWindow(impl->display, impl->win); | |||
XSetInputFocus(impl->display, impl->win, RevertToPointerRoot, CurrentTime); | |||
XSync(impl->display, False); | |||
} | |||
return PUGL_SUCCESS; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// X11 specific, set dialog window type and pid hints | |||
void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) | |||
{ | |||
const 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); | |||
Atom _wts[2]; | |||
int numAtoms = 0; | |||
if (! isStandalone) | |||
_wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | |||
_wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False); | |||
XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#endif // HAVE_X11 | |||
#ifndef DISTRHO_OS_MAC | |||
END_NAMESPACE_DGL | |||
#endif |
@@ -0,0 +1,154 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DGL_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 <cstddef> | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
# include <cstdbool> | |||
# include <cstdint> | |||
#else | |||
# include <stdbool.h> | |||
# include <stdint.h> | |||
#endif | |||
#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 | |||
PUGL_API bool | |||
puglBackendEnter(PuglView* view); | |||
// expose backend leave | |||
PUGL_API void | |||
puglBackendLeave(PuglView* view); | |||
// clear minimum size to 0 | |||
PUGL_API void | |||
puglClearMinSize(PuglView* view); | |||
// missing in pugl, directly returns transient parent | |||
PUGL_API PuglNativeView | |||
puglGetTransientParent(const PuglView* view); | |||
// missing in pugl, directly returns title char* pointer | |||
PUGL_API const char* | |||
puglGetWindowTitle(const PuglView* view); | |||
// get global scale factor | |||
PUGL_API double | |||
puglGetDesktopScaleFactor(const PuglView* view); | |||
// bring view window into the foreground, aka "raise" window | |||
PUGL_API void | |||
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, uint width, uint height, bool aspect); | |||
// set window offset without changing size | |||
PUGL_API PuglStatus | |||
puglSetWindowOffset(PuglView* view, int x, int y); | |||
// set window size with default size and without changing frame x/y position | |||
PUGL_API PuglStatus | |||
puglSetWindowSize(PuglView* view, uint width, uint height); | |||
// DGL specific, build-specific drawing prepare | |||
PUGL_API void | |||
puglOnDisplayPrepare(PuglView* view); | |||
// DGL specific, build-specific fallback resize | |||
PUGL_API void | |||
puglFallbackOnResize(PuglView* view); | |||
#if defined(DISTRHO_OS_MAC) | |||
// macOS specific, allow standalone window to gain focus | |||
PUGL_API void | |||
puglMacOSActivateApp(); | |||
// macOS specific, add another view's window as child | |||
PUGL_API PuglStatus | |||
puglMacOSAddChildWindow(PuglView* view, PuglView* child); | |||
// macOS specific, remove another view's window as child | |||
PUGL_API PuglStatus | |||
puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); | |||
// macOS specific, center view based on parent coordinates (if there is one) | |||
PUGL_API void | |||
puglMacOSShowCentered(PuglView* view); | |||
#elif defined(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 | |||
puglWin32ShowCentered(PuglView* view); | |||
// win32 specific, set or unset WS_SIZEBOX style flag | |||
PUGL_API void | |||
puglWin32SetWindowResizable(PuglView* view, bool resizable); | |||
#elif defined(HAVE_X11) | |||
// X11 specific, safer way to grab focus | |||
PUGL_API PuglStatus | |||
puglX11GrabFocus(const PuglView* view); | |||
// X11 specific, set dialog window type and pid hints | |||
PUGL_API void | |||
puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); | |||
#endif | |||
PUGL_END_DECLS | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#ifndef DISTRHO_OS_MAC | |||
END_NAMESPACE_DGL | |||
#endif | |||
#endif // DGL_PUGL_HPP_INCLUDED |
@@ -0,0 +1 @@ | |||
pugl.cpp |
@@ -1,121 +0,0 @@ | |||
/* | |||
Copyright 2014 David Robillard <http://drobilla.net> | |||
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. | |||
*/ | |||
#ifndef PUGL_COMMON_H_INCLUDED | |||
#define PUGL_COMMON_H_INCLUDED | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/** | |||
@addtogroup pugl | |||
@{ | |||
*/ | |||
/** | |||
A Pugl view. | |||
*/ | |||
typedef struct PuglViewImpl PuglView; | |||
/** | |||
A native window handle. | |||
On X11, this is a Window. | |||
On OSX, this is an NSView*. | |||
On Windows, this is a HWND. | |||
*/ | |||
typedef intptr_t PuglNativeWindow; | |||
/** | |||
Handle for opaque user data. | |||
*/ | |||
typedef void* PuglHandle; | |||
/** | |||
Return status code. | |||
*/ | |||
typedef enum { | |||
PUGL_SUCCESS = 0 | |||
} PuglStatus; | |||
/** | |||
Drawing context type. | |||
*/ | |||
typedef enum { | |||
PUGL_GL, | |||
PUGL_CAIRO | |||
} PuglContextType; | |||
/** | |||
Convenience symbols for ASCII control characters. | |||
*/ | |||
typedef enum { | |||
PUGL_CHAR_BACKSPACE = 0x08, | |||
PUGL_CHAR_ESCAPE = 0x1B, | |||
PUGL_CHAR_DELETE = 0x7F | |||
} PuglChar; | |||
/** | |||
Keyboard modifier flags. | |||
*/ | |||
typedef enum { | |||
PUGL_MOD_SHIFT = 1 << 0, /**< Shift key */ | |||
PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||
PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||
PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
} PuglMod; | |||
/** | |||
Special (non-Unicode) keyboard keys. | |||
*/ | |||
typedef enum { | |||
PUGL_KEY_F1 = 1, | |||
PUGL_KEY_F2, | |||
PUGL_KEY_F3, | |||
PUGL_KEY_F4, | |||
PUGL_KEY_F5, | |||
PUGL_KEY_F6, | |||
PUGL_KEY_F7, | |||
PUGL_KEY_F8, | |||
PUGL_KEY_F9, | |||
PUGL_KEY_F10, | |||
PUGL_KEY_F11, | |||
PUGL_KEY_F12, | |||
PUGL_KEY_LEFT, | |||
PUGL_KEY_UP, | |||
PUGL_KEY_RIGHT, | |||
PUGL_KEY_DOWN, | |||
PUGL_KEY_PAGE_UP, | |||
PUGL_KEY_PAGE_DOWN, | |||
PUGL_KEY_HOME, | |||
PUGL_KEY_END, | |||
PUGL_KEY_INSERT, | |||
PUGL_KEY_SHIFT, | |||
PUGL_KEY_CTRL, | |||
PUGL_KEY_ALT, | |||
PUGL_KEY_SUPER | |||
} PuglKey; | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* PUGL_COMMON_H_INCLUDED */ |
@@ -1,41 +0,0 @@ | |||
/* | |||
Copyright 2014 David Robillard <http://drobilla.net> | |||
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. | |||
*/ | |||
#ifndef PUGL_EVENT_H_INCLUDED | |||
#define PUGL_EVENT_H_INCLUDED | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
#include "pugl/common.h" | |||
/** | |||
@addtogroup pugl | |||
@{ | |||
*/ | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* PUGL_EVENT_H_INCLUDED */ |
@@ -1,32 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
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 gl.h Portable header wrapper for gl.h. | |||
Unfortunately, GL includes vary across platforms so this header allows for | |||
pure portable programs. | |||
*/ | |||
#ifdef __APPLE__ | |||
# include "OpenGL/gl.h" | |||
#else | |||
# ifdef _WIN32 | |||
# include <windows.h> /* Broken Windows GL headers require this */ | |||
# endif | |||
# include "GL/gl.h" | |||
#endif | |||
@@ -1,32 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
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 glu.h Portable header wrapper for glu.h. | |||
Unfortunately, GL includes vary across platforms so this header allows for | |||
pure portable programs. | |||
*/ | |||
#ifdef __APPLE__ | |||
# include "OpenGL/glu.h" | |||
#else | |||
# ifdef _WIN32 | |||
# include <windows.h> /* Broken Windows GL headers require this */ | |||
# endif | |||
# include "GL/glu.h" | |||
#endif | |||
@@ -1,385 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
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 API for Pugl, a minimal portable API for OpenGL. | |||
*/ | |||
#ifndef PUGL_H_INCLUDED | |||
#define PUGL_H_INCLUDED | |||
#include <stdint.h> | |||
#include "pugl/common.h" | |||
#include "pugl/event.h" | |||
#ifdef PUGL_SHARED | |||
# ifdef _WIN32 | |||
# define PUGL_LIB_IMPORT __declspec(dllimport) | |||
# define PUGL_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef PUGL_INTERNAL | |||
# define PUGL_API PUGL_LIB_EXPORT | |||
# else | |||
# define PUGL_API PUGL_LIB_IMPORT | |||
# endif | |||
#else | |||
# ifdef _WIN32 | |||
# define PUGL_API | |||
# else | |||
# define PUGL_API __attribute__((visibility("hidden"))) | |||
# endif | |||
#endif | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
/** | |||
@defgroup pugl Pugl | |||
A minimal portable API for OpenGL. | |||
@{ | |||
*/ | |||
/** | |||
A function called when the window is closed. | |||
*/ | |||
typedef void (*PuglCloseFunc)(PuglView* view); | |||
/** | |||
A function called to draw the view contents with OpenGL. | |||
*/ | |||
typedef void (*PuglDisplayFunc)(PuglView* view); | |||
/** | |||
A function called when a key is pressed or released. | |||
@param view The view the event occured in. | |||
@param press True if the key was pressed, false if released. | |||
@param key Unicode point of the key pressed. | |||
@return 0 if event was handled, otherwise send event to parent window. | |||
*/ | |||
typedef int (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); | |||
/** | |||
A function called when the pointer moves. | |||
@param view The view the event occured in. | |||
@param x The window-relative x coordinate of the pointer. | |||
@param y The window-relative y coordinate of the pointer. | |||
*/ | |||
typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); | |||
/** | |||
A function called when a mouse button is pressed or released. | |||
@param view The view the event occured in. | |||
@param button The button number (1 = left, 2 = middle, 3 = right). | |||
@param press True if the key was pressed, false if released. | |||
@param x The window-relative x coordinate of the pointer. | |||
@param y The window-relative y coordinate of the pointer. | |||
*/ | |||
typedef void (*PuglMouseFunc)( | |||
PuglView* view, int button, bool press, int x, int y); | |||
/** | |||
A function called when the view is resized. | |||
@param view The view being resized. | |||
@param width The new view width. | |||
@param height The new view height. | |||
*/ | |||
typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | |||
/** | |||
A function called on scrolling (e.g. mouse wheel or track pad). | |||
The distances used here are in "lines", a single tick of a clicking mouse | |||
wheel. For example, @p dy = 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. | |||
@param view The view being scrolled. | |||
@param x The window-relative x coordinate of the pointer. | |||
@param y The window-relative y coordinate of the pointer. | |||
@param dx The scroll x distance. | |||
@param dx The scroll y distance. | |||
*/ | |||
typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy); | |||
/** | |||
A function called when a special key is pressed or released. | |||
This callback allows the use of keys that do not have unicode points. | |||
Note that some are non-printable keys. | |||
@param view The view the event occured in. | |||
@param press True if the key was pressed, false if released. | |||
@param key The key pressed. | |||
@return 0 if event was handled, otherwise send event to parent window. | |||
*/ | |||
typedef int (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | |||
/** | |||
A function called when a filename is selected via file-browser. | |||
@param view The view the event occured in. | |||
@param filename The selected file name or NULL if the dialog was canceled. | |||
*/ | |||
typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); | |||
/** | |||
@name Initialization | |||
Configuration functions which must be called before creating a window. | |||
@{ | |||
*/ | |||
/** | |||
Create a Pugl context. | |||
To create a window, call the various puglInit* functions as necessary, then | |||
call puglCreateWindow(). | |||
*/ | |||
PUGL_API PuglView* | |||
puglInit(void); | |||
/** | |||
Set the parent window before creating a window (for embedding). | |||
*/ | |||
PUGL_API void | |||
puglInitWindowParent(PuglView* view, PuglNativeWindow parent); | |||
/** | |||
Set the window size before creating a window. | |||
*/ | |||
PUGL_API void | |||
puglInitWindowSize(PuglView* view, int width, int height); | |||
/** | |||
Set the minimum window size before creating a window. | |||
*/ | |||
PUGL_API void | |||
puglInitWindowMinSize(PuglView* view, int width, int height); | |||
/** | |||
Enable or disable resizing before creating a window. | |||
*/ | |||
PUGL_API void | |||
puglInitUserResizable(PuglView* view, bool resizable); | |||
/** | |||
Set transient parent before creating a window. | |||
On X11, parent_id must be a Window. | |||
On OSX, parent_id must be an NSView*. | |||
*/ | |||
PUGL_API void | |||
puglInitTransientFor(PuglView* view, uintptr_t parent); | |||
/** | |||
Set the context type before creating a window. | |||
*/ | |||
PUGL_API void | |||
puglInitContextType(PuglView* view, PuglContextType type); | |||
/** | |||
@} | |||
*/ | |||
/** | |||
@name Windows | |||
Window management functions. | |||
@{ | |||
*/ | |||
/** | |||
Create a window with the settings given by the various puglInit functions. | |||
@return 1 (pugl does not currently support multiple windows). | |||
*/ | |||
PUGL_API int | |||
puglCreateWindow(PuglView* view, const char* title); | |||
/** | |||
Show the current window. | |||
*/ | |||
PUGL_API void | |||
puglShowWindow(PuglView* view); | |||
/** | |||
Hide the current window. | |||
*/ | |||
PUGL_API void | |||
puglHideWindow(PuglView* view); | |||
/** | |||
Return the native window handle. | |||
*/ | |||
PUGL_API PuglNativeWindow | |||
puglGetNativeWindow(PuglView* view); | |||
/** | |||
@} | |||
*/ | |||
/** | |||
Set the handle to be passed to all callbacks. | |||
This is generally a pointer to a struct which contains all necessary state. | |||
Everything needed in callbacks should be here, not in static variables. | |||
Note the lack of this facility makes GLUT unsuitable for plugins or | |||
non-trivial programs; this mistake is largely why Pugl exists. | |||
*/ | |||
PUGL_API void | |||
puglSetHandle(PuglView* view, PuglHandle handle); | |||
/** | |||
Get the handle to be passed to all callbacks. | |||
*/ | |||
PUGL_API PuglHandle | |||
puglGetHandle(PuglView* view); | |||
/** | |||
Get the drawing context. | |||
For PUGL_GL contexts, this is unused and returns NULL. | |||
For PUGL_CAIRO contexts, this returns a pointer to a cairo_t. | |||
*/ | |||
PUGL_API void* | |||
puglGetContext(PuglView* view); | |||
/** | |||
Return the timestamp (if any) of the currently-processing event. | |||
*/ | |||
PUGL_API uint32_t | |||
puglGetEventTimestamp(PuglView* view); | |||
/** | |||
Get the currently active modifiers (PuglMod flags). | |||
This should only be called from an event handler. | |||
*/ | |||
PUGL_API int | |||
puglGetModifiers(PuglView* view); | |||
/** | |||
Ignore synthetic repeated key events. | |||
*/ | |||
PUGL_API void | |||
puglIgnoreKeyRepeat(PuglView* view, bool ignore); | |||
/** | |||
@name Event Callbacks | |||
Functions to set event callbacks for handling user input. | |||
@{ | |||
*/ | |||
/** | |||
Set the function to call when the window is closed. | |||
*/ | |||
PUGL_API void | |||
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); | |||
/** | |||
Set the display function which should draw the UI using GL. | |||
*/ | |||
PUGL_API void | |||
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); | |||
/** | |||
Set the function to call on keyboard events. | |||
*/ | |||
PUGL_API void | |||
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); | |||
/** | |||
Set the function to call on mouse motion. | |||
*/ | |||
PUGL_API void | |||
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); | |||
/** | |||
Set the function to call on mouse button events. | |||
*/ | |||
PUGL_API void | |||
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); | |||
/** | |||
Set the function to call on scroll events. | |||
*/ | |||
PUGL_API void | |||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); | |||
/** | |||
Set the function to call on special events. | |||
*/ | |||
PUGL_API void | |||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||
/** | |||
Set the function to call when the window size changes. | |||
*/ | |||
PUGL_API void | |||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||
/** | |||
Set the function to call on file-browser selections. | |||
*/ | |||
PUGL_API void | |||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); | |||
/** | |||
@} | |||
*/ | |||
/** | |||
Grab the input focus. | |||
*/ | |||
PUGL_API void | |||
puglGrabFocus(PuglView* view); | |||
/** | |||
Process all pending window events. | |||
This handles input events as well as rendering, so it should be called | |||
regularly and rapidly enough to keep the UI responsive. | |||
*/ | |||
PUGL_API PuglStatus | |||
puglProcessEvents(PuglView* view); | |||
/** | |||
Request a redisplay on the next call to puglProcessEvents(). | |||
*/ | |||
PUGL_API void | |||
puglPostRedisplay(PuglView* view); | |||
/** | |||
Destroy a GL window. | |||
*/ | |||
PUGL_API void | |||
puglDestroy(PuglView* view); | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* PUGL_H_INCLUDED */ |
@@ -1,352 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
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_internal.h Private platform-independent definitions. | |||
Note this file contains function definitions, so it must be compiled into | |||
the final binary exactly once. Each platform specific implementation file | |||
including it once should achieve this. | |||
If you are copying the pugl code into your source tree, the following | |||
symbols can be defined to tweak pugl behaviour: | |||
PUGL_HAVE_CAIRO: Include Cairo support code. | |||
PUGL_HAVE_GL: Include OpenGL support code. | |||
PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | |||
PUGL_VERBOSE: Print GL information to console. | |||
*/ | |||
#include "pugl/pugl.h" | |||
#include "pugl/event.h" | |||
#ifdef PUGL_VERBOSE | |||
# include <stdio.h> | |||
# define PUGL_LOG(str) fprintf(stderr, "pugl: " str) | |||
# define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__) | |||
#else | |||
# define PUGL_LOG(str) | |||
# define PUGL_LOGF(fmt, ...) | |||
#endif | |||
typedef struct PuglInternalsImpl PuglInternals; | |||
struct PuglViewImpl { | |||
PuglHandle handle; | |||
PuglCloseFunc closeFunc; | |||
PuglDisplayFunc displayFunc; | |||
PuglKeyboardFunc keyboardFunc; | |||
PuglMotionFunc motionFunc; | |||
PuglMouseFunc mouseFunc; | |||
PuglReshapeFunc reshapeFunc; | |||
PuglScrollFunc scrollFunc; | |||
PuglSpecialFunc specialFunc; | |||
PuglFileSelectedFunc fileSelectedFunc; | |||
PuglInternals* impl; | |||
PuglNativeWindow parent; | |||
PuglContextType ctx_type; | |||
uintptr_t transient_parent; | |||
int width; | |||
int height; | |||
int min_width; | |||
int min_height; | |||
int mods; | |||
bool mouse_in_view; | |||
bool ignoreKeyRepeat; | |||
bool redisplay; | |||
bool resizable; | |||
uint32_t event_timestamp_ms; | |||
}; | |||
PuglInternals* puglInitInternals(void); | |||
PuglView* | |||
puglInit(void) | |||
{ | |||
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||
if (!view) { | |||
return NULL; | |||
} | |||
PuglInternals* impl = puglInitInternals(); | |||
if (!impl) { | |||
free(view); | |||
return NULL; | |||
} | |||
view->impl = impl; | |||
view->width = 640; | |||
view->height = 480; | |||
return view; | |||
} | |||
void | |||
puglInitWindowSize(PuglView* view, int width, int height) | |||
{ | |||
view->width = width; | |||
view->height = height; | |||
} | |||
void | |||
puglInitWindowMinSize(PuglView* view, int width, int height) | |||
{ | |||
view->min_width = width; | |||
view->min_height = height; | |||
} | |||
void | |||
puglInitWindowParent(PuglView* view, PuglNativeWindow parent) | |||
{ | |||
view->parent = parent; | |||
} | |||
void | |||
puglInitUserResizable(PuglView* view, bool resizable) | |||
{ | |||
view->resizable = resizable; | |||
} | |||
void | |||
puglInitTransientFor(PuglView* view, uintptr_t parent) | |||
{ | |||
view->transient_parent = parent; | |||
} | |||
void | |||
puglInitContextType(PuglView* view, PuglContextType type) | |||
{ | |||
view->ctx_type = type; | |||
} | |||
void | |||
puglSetHandle(PuglView* view, PuglHandle handle) | |||
{ | |||
view->handle = handle; | |||
} | |||
PuglHandle | |||
puglGetHandle(PuglView* view) | |||
{ | |||
return view->handle; | |||
} | |||
uint32_t | |||
puglGetEventTimestamp(PuglView* view) | |||
{ | |||
return view->event_timestamp_ms; | |||
} | |||
int | |||
puglGetModifiers(PuglView* view) | |||
{ | |||
return view->mods; | |||
} | |||
void | |||
puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||
{ | |||
view->ignoreKeyRepeat = ignore; | |||
} | |||
void | |||
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) | |||
{ | |||
view->closeFunc = closeFunc; | |||
} | |||
void | |||
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) | |||
{ | |||
view->displayFunc = displayFunc; | |||
} | |||
void | |||
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) | |||
{ | |||
view->keyboardFunc = keyboardFunc; | |||
} | |||
void | |||
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) | |||
{ | |||
view->motionFunc = motionFunc; | |||
} | |||
void | |||
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) | |||
{ | |||
view->mouseFunc = mouseFunc; | |||
} | |||
void | |||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) | |||
{ | |||
view->reshapeFunc = reshapeFunc; | |||
} | |||
void | |||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | |||
{ | |||
view->scrollFunc = scrollFunc; | |||
} | |||
void | |||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||
{ | |||
view->specialFunc = specialFunc; | |||
} | |||
void | |||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) | |||
{ | |||
view->fileSelectedFunc = fileSelectedFunc; | |||
} | |||
void | |||
puglEnterContext(PuglView* view); | |||
void | |||
puglLeaveContext(PuglView* view, bool flush); | |||
#if 0 | |||
/** Return the code point for buf, or the replacement character on error. */ | |||
static uint32_t | |||
puglDecodeUTF8(const uint8_t* buf) | |||
{ | |||
#define FAIL_IF(cond) { if (cond) return 0xFFFD; } | |||
/* http://en.wikipedia.org/wiki/UTF-8 */ | |||
if (buf[0] < 0x80) { | |||
return buf[0]; | |||
} else if (buf[0] < 0xC2) { | |||
return 0xFFFD; | |||
} else if (buf[0] < 0xE0) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
return (buf[0] << 6) + buf[1] - 0x3080; | |||
} else if (buf[0] < 0xF0) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0); | |||
FAIL_IF((buf[2] & 0xC0) != 0x80); | |||
return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080; | |||
} else if (buf[0] < 0xF5) { | |||
FAIL_IF((buf[1] & 0xC0) != 0x80); | |||
FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90); | |||
FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90); | |||
FAIL_IF((buf[2] & 0xC0) != 0x80); | |||
FAIL_IF((buf[3] & 0xC0) != 0x80); | |||
return ((buf[0] << 18) + | |||
(buf[1] << 12) + | |||
(buf[2] << 6) + | |||
buf[3] - 0x3C82080); | |||
} | |||
return 0xFFFD; | |||
} | |||
#endif | |||
static void | |||
puglDefaultReshape(PuglView* view, int width, int height) | |||
{ | |||
glMatrixMode(GL_PROJECTION); | |||
glLoadIdentity(); | |||
glOrtho(0, width, height, 0, 0, 1); | |||
glViewport(0, 0, width, height); | |||
glMatrixMode(GL_MODELVIEW); | |||
glLoadIdentity(); | |||
return; | |||
// unused | |||
(void)view; | |||
} | |||
#if 0 | |||
static void | |||
puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
{ | |||
if (view->eventFunc) { | |||
view->eventFunc(view, event); | |||
} | |||
switch (event->type) { | |||
case PUGL_CONFIGURE: | |||
puglEnterContext(view); | |||
view->width = event->configure.width; | |||
view->height = event->configure.height; | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, view->width, view->height); | |||
} | |||
puglLeaveContext(view, false); | |||
break; | |||
case PUGL_EXPOSE: | |||
if (event->expose.count == 0) { | |||
puglEnterContext(view); | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
view->redisplay = false; | |||
puglLeaveContext(view, true); | |||
} | |||
break; | |||
case PUGL_MOTION_NOTIFY: | |||
view->event_timestamp_ms = event->motion.time; | |||
view->mods = event->motion.state; | |||
if (view->motionFunc) { | |||
view->motionFunc(view, event->motion.x, event->motion.y); | |||
} | |||
break; | |||
case PUGL_SCROLL: | |||
if (view->scrollFunc) { | |||
view->scrollFunc(view, | |||
event->scroll.x, event->scroll.y, | |||
event->scroll.dx, event->scroll.dy); | |||
} | |||
break; | |||
case PUGL_BUTTON_PRESS: | |||
case PUGL_BUTTON_RELEASE: | |||
view->event_timestamp_ms = event->button.time; | |||
view->mods = event->button.state; | |||
if (view->mouseFunc) { | |||
view->mouseFunc(view, | |||
event->button.button, | |||
event->type == PUGL_BUTTON_PRESS, | |||
event->button.x, | |||
event->button.y); | |||
} | |||
break; | |||
case PUGL_KEY_PRESS: | |||
case PUGL_KEY_RELEASE: | |||
view->event_timestamp_ms = event->key.time; | |||
view->mods = event->key.state; | |||
if (event->key.special && view->specialFunc) { | |||
view->specialFunc(view, | |||
event->type == PUGL_KEY_PRESS, | |||
event->key.special); | |||
} else if (event->key.character && view->keyboardFunc) { | |||
view->keyboardFunc(view, | |||
event->type == PUGL_KEY_PRESS, | |||
event->key.character); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
#endif |
@@ -1,576 +0,0 @@ | |||
/* | |||
Copyright 2012 David Robillard <http://drobilla.net> | |||
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_osx.m OSX/Cocoa Pugl Implementation. | |||
*/ | |||
#include <stdlib.h> | |||
#import <Cocoa/Cocoa.h> | |||
#include "pugl_internal.h" | |||
@interface PuglWindow : NSWindow | |||
{ | |||
@public | |||
PuglView* puglview; | |||
} | |||
- (id) initWithContentRect:(NSRect)contentRect | |||
styleMask:(unsigned int)aStyle | |||
backing:(NSBackingStoreType)bufferingType | |||
defer:(BOOL)flag; | |||
- (void) setPuglview:(PuglView*)view; | |||
- (BOOL) canBecomeKeyWindow; | |||
- (BOOL) windowShouldClose:(id)sender; | |||
@end | |||
@implementation PuglWindow | |||
- (id)initWithContentRect:(NSRect)contentRect | |||
styleMask:(unsigned int)aStyle | |||
backing:(NSBackingStoreType)bufferingType | |||
defer:(BOOL)flag | |||
{ | |||
NSWindow* result = [super initWithContentRect:contentRect | |||
styleMask:(NSClosableWindowMask | | |||
NSTitledWindowMask | | |||
NSResizableWindowMask) | |||
backing:NSBackingStoreBuffered defer:NO]; | |||
[result setAcceptsMouseMovedEvents:YES]; | |||
[result setLevel: CGShieldingWindowLevel() + 1]; | |||
return (PuglWindow*)result; | |||
// unused | |||
(void)aStyle; (void)bufferingType; (void)flag; | |||
} | |||
- (void)setPuglview:(PuglView*)view | |||
{ | |||
puglview = view; | |||
[self setContentSize:NSMakeSize(view->width, view->height)]; | |||
} | |||
- (BOOL)canBecomeKeyWindow | |||
{ | |||
return YES; | |||
} | |||
- (BOOL)windowShouldClose:(id)sender | |||
{ | |||
if (puglview->closeFunc) | |||
puglview->closeFunc(puglview); | |||
return YES; | |||
// unused | |||
(void)sender; | |||
} | |||
@end | |||
static void | |||
puglDisplay(PuglView* view) | |||
{ | |||
view->redisplay = false; | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
} | |||
@interface PuglOpenGLView : NSOpenGLView | |||
{ | |||
@public | |||
PuglView* puglview; | |||
NSTrackingArea* trackingArea; | |||
bool doubleBuffered; | |||
} | |||
- (BOOL) acceptsFirstMouse:(NSEvent*)e; | |||
- (BOOL) acceptsFirstResponder; | |||
- (BOOL) isFlipped; | |||
- (BOOL) isOpaque; | |||
- (BOOL) preservesContentInLiveResize; | |||
- (id) initWithFrame:(NSRect)frame; | |||
- (void) reshape; | |||
- (void) drawRect:(NSRect)r; | |||
- (void) cursorUpdate:(NSEvent*)e; | |||
- (void) updateTrackingAreas; | |||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow; | |||
- (void) mouseMoved:(NSEvent*)event; | |||
- (void) mouseDragged:(NSEvent*)event; | |||
- (void) rightMouseDragged:(NSEvent*)event; | |||
- (void) otherMouseDragged:(NSEvent*)event; | |||
- (void) mouseDown:(NSEvent*)event; | |||
- (void) rightMouseDown:(NSEvent*)event; | |||
- (void) otherMouseDown:(NSEvent*)event; | |||
- (void) mouseUp:(NSEvent*)event; | |||
- (void) rightMouseUp:(NSEvent*)event; | |||
- (void) otherMouseUp:(NSEvent*)event; | |||
- (void) scrollWheel:(NSEvent*)event; | |||
- (void) keyDown:(NSEvent*)event; | |||
- (void) keyUp:(NSEvent*)event; | |||
- (void) flagsChanged:(NSEvent*)event; | |||
@end | |||
@implementation PuglOpenGLView | |||
- (BOOL) acceptsFirstMouse:(NSEvent*)e | |||
{ | |||
return YES; | |||
// unused | |||
(void)e; | |||
} | |||
- (BOOL) acceptsFirstResponder | |||
{ | |||
return YES; | |||
} | |||
- (BOOL) isFlipped | |||
{ | |||
return YES; | |||
} | |||
- (BOOL) isOpaque | |||
{ | |||
return YES; | |||
} | |||
- (BOOL) preservesContentInLiveResize | |||
{ | |||
return NO; | |||
} | |||
- (id) initWithFrame:(NSRect)frame | |||
{ | |||
puglview = nil; | |||
trackingArea = nil; | |||
doubleBuffered = true; | |||
NSOpenGLPixelFormatAttribute pixelAttribs[] = { | |||
NSOpenGLPFAColorSize, 24, | |||
NSOpenGLPFAAlphaSize, 8, | |||
NSOpenGLPFADepthSize, 16, | |||
NSOpenGLPFADoubleBuffer, | |||
NSOpenGLPFAAccelerated, | |||
0 | |||
}; | |||
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] | |||
initWithAttributes:pixelAttribs]; | |||
if (pixelFormat) { | |||
self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||
[pixelFormat release]; | |||
printf("Is doubleBuffered? TRUE\n"); | |||
} else { | |||
self = [super initWithFrame:frame]; | |||
doubleBuffered = false; | |||
printf("Is doubleBuffered? FALSE\n"); | |||
} | |||
if (self) { | |||
GLint swapInterval = 1; | |||
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; | |||
[self reshape]; | |||
} | |||
return self; | |||
} | |||
- (void) reshape | |||
{ | |||
if (!puglview) { | |||
/* NOTE: Apparently reshape gets called when the GC gets around to | |||
deleting the view (?), so we must have reset puglview to NULL when | |||
this comes around. | |||
*/ | |||
return; | |||
} | |||
[[self openGLContext] update]; | |||
NSRect bounds = [self bounds]; | |||
int width = bounds.size.width; | |||
int height = bounds.size.height; | |||
puglEnterContext(puglview); | |||
if (puglview->reshapeFunc) { | |||
puglview->reshapeFunc(puglview, width, height); | |||
} else { | |||
puglDefaultReshape(puglview, width, height); | |||
} | |||
puglLeaveContext(puglview, false); | |||
puglview->width = width; | |||
puglview->height = height; | |||
} | |||
- (void) drawRect:(NSRect)r | |||
{ | |||
puglEnterContext(puglview); | |||
puglDisplay(puglview); | |||
puglLeaveContext(puglview, true); | |||
// unused | |||
return; (void)r; | |||
} | |||
- (void) cursorUpdate:(NSEvent*)e | |||
{ | |||
[[NSCursor arrowCursor] set]; | |||
// unused | |||
return; (void)e; | |||
} | |||
- (void) updateTrackingAreas | |||
{ | |||
static const int opts = NSTrackingMouseEnteredAndExited | |||
| NSTrackingMouseMoved | |||
| NSTrackingEnabledDuringMouseDrag | |||
| NSTrackingInVisibleRect | |||
| NSTrackingActiveAlways | |||
| NSTrackingCursorUpdate; | |||
if (trackingArea != nil) { | |||
[self removeTrackingArea:trackingArea]; | |||
[trackingArea release]; | |||
} | |||
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] | |||
options:opts | |||
owner:self | |||
userInfo:nil]; | |||
[self addTrackingArea:trackingArea]; | |||
[super updateTrackingAreas]; | |||
} | |||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow | |||
{ | |||
if (newWindow != nil) { | |||
[newWindow setAcceptsMouseMovedEvents:YES]; | |||
[newWindow makeFirstResponder:self]; | |||
} | |||
[super viewWillMoveToWindow:newWindow]; | |||
} | |||
static unsigned | |||
getModifiers(PuglView* view, NSEvent* ev) | |||
{ | |||
const unsigned modifierFlags = [ev modifierFlags]; | |||
view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); | |||
unsigned mods = 0; | |||
mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; | |||
mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; | |||
mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; | |||
mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; | |||
return mods; | |||
} | |||
static int | |||
getFixedAppKitButton(NSInteger button) | |||
{ | |||
switch (button) { | |||
case 0: return 1; | |||
case 1: return 3; | |||
case 2: return 2; | |||
default: return button; | |||
} | |||
} | |||
- (void) mouseMoved:(NSEvent*)event | |||
{ | |||
if (puglview->motionFunc) { | |||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->motionFunc(puglview, loc.x, loc.y); | |||
} | |||
} | |||
- (void) mouseDragged:(NSEvent*)event | |||
{ | |||
[self mouseMoved:event]; | |||
} | |||
- (void) rightMouseDragged:(NSEvent*)event | |||
{ | |||
[self mouseDragged:event]; | |||
} | |||
- (void) otherMouseDragged:(NSEvent*)event | |||
{ | |||
[self mouseDragged:event]; | |||
} | |||
- (void) mouseDown:(NSEvent*)event | |||
{ | |||
if (puglview->mouseFunc) { | |||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y); | |||
} | |||
} | |||
- (void) rightMouseDown:(NSEvent*)event | |||
{ | |||
[self mouseDown:event]; | |||
} | |||
- (void) otherMouseDown:(NSEvent*)event | |||
{ | |||
[self mouseDown:event]; | |||
} | |||
- (void) mouseUp:(NSEvent*)event | |||
{ | |||
if (puglview->mouseFunc) { | |||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y); | |||
} | |||
} | |||
- (void) rightMouseUp:(NSEvent*)event | |||
{ | |||
[self mouseUp:event]; | |||
} | |||
- (void) otherMouseUp:(NSEvent*)event | |||
{ | |||
[self mouseUp:event]; | |||
} | |||
- (void) scrollWheel:(NSEvent*)event | |||
{ | |||
if (puglview->scrollFunc) { | |||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->scrollFunc(puglview, | |||
loc.x, loc.y, | |||
[event deltaX], [event deltaY]); | |||
} | |||
} | |||
- (void) keyDown:(NSEvent*)event | |||
{ | |||
if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { | |||
NSString* chars = [event characters]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); | |||
} | |||
} | |||
- (void) keyUp:(NSEvent*)event | |||
{ | |||
if (puglview->keyboardFunc) { | |||
NSString* chars = [event characters]; | |||
puglview->mods = getModifiers(puglview, event); | |||
puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); | |||
} | |||
} | |||
- (void) flagsChanged:(NSEvent*)event | |||
{ | |||
if (puglview->specialFunc) { | |||
const unsigned mods = getModifiers(puglview, event); | |||
if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { | |||
puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); | |||
} else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { | |||
puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); | |||
} else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { | |||
puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); | |||
} else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { | |||
puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); | |||
} | |||
puglview->mods = mods; | |||
} | |||
} | |||
@end | |||
struct PuglInternalsImpl { | |||
PuglOpenGLView* glview; | |||
id window; | |||
}; | |||
PuglInternals* | |||
puglInitInternals() | |||
{ | |||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
} | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
[[view->impl->glview openGLContext] makeCurrentContext]; | |||
} | |||
#endif | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL && flush) { | |||
if (view->impl->glview->doubleBuffered) { | |||
[[view->impl->glview openGLContext] flushBuffer]; | |||
} else { | |||
glFlush(); | |||
} | |||
//[NSOpenGLContext clearCurrentContext]; | |||
} | |||
#endif | |||
} | |||
int | |||
puglCreateWindow(PuglView* view, const char* title) | |||
{ | |||
PuglInternals* impl = view->impl; | |||
[NSAutoreleasePool new]; | |||
[NSApplication sharedApplication]; | |||
impl->glview = [PuglOpenGLView new]; | |||
if (!impl->glview) { | |||
return 1; | |||
} | |||
impl->glview->puglview = view; | |||
if (view->resizable) { | |||
[impl->glview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | |||
} | |||
if (view->parent) { | |||
[impl->glview retain]; | |||
NSView* pview = (NSView*)view->parent; | |||
[pview addSubview:impl->glview]; | |||
return 0; | |||
} | |||
id window = [[PuglWindow new]retain]; | |||
if (title) { | |||
NSString* titleString = [[NSString alloc] | |||
initWithBytes:title | |||
length:strlen(title) | |||
encoding:NSUTF8StringEncoding]; | |||
[window setTitle:titleString]; | |||
} | |||
[window setPuglview:view]; | |||
[window setContentView:impl->glview]; | |||
[window makeFirstResponder:impl->glview]; | |||
[window makeKeyAndOrderFront:window]; | |||
// wait for first puglShowWindow | |||
[window setIsVisible:NO]; | |||
[NSApp activateIgnoringOtherApps:YES]; | |||
[window center]; | |||
impl->window = window; | |||
return 0; | |||
} | |||
void | |||
puglShowWindow(PuglView* view) | |||
{ | |||
PuglInternals* impl = view->impl; | |||
if (impl->window) { | |||
[impl->window setIsVisible:YES]; | |||
} else { | |||
[view->impl->glview setHidden:NO]; | |||
} | |||
} | |||
void | |||
puglHideWindow(PuglView* view) | |||
{ | |||
PuglInternals* impl = view->impl; | |||
if (impl->window) { | |||
[impl->window setIsVisible:NO]; | |||
} else { | |||
[impl->glview setHidden:YES]; | |||
} | |||
} | |||
void | |||
puglDestroy(PuglView* view) | |||
{ | |||
view->impl->glview->puglview = NULL; | |||
if (view->impl->window) { | |||
[view->impl->window close]; | |||
[view->impl->glview release]; | |||
[view->impl->window release]; | |||
} else { | |||
[view->impl->glview release]; | |||
} | |||
free(view->impl); | |||
free(view); | |||
} | |||
PuglStatus | |||
puglProcessEvents(PuglView* view) | |||
{ | |||
return PUGL_SUCCESS; | |||
// unused | |||
(void)view; | |||
} | |||
void | |||
puglPostRedisplay(PuglView* view) | |||
{ | |||
view->redisplay = true; | |||
[view->impl->glview setNeedsDisplay:YES]; | |||
} | |||
PuglNativeWindow | |||
puglGetNativeWindow(PuglView* view) | |||
{ | |||
return (PuglNativeWindow)view->impl->glview; | |||
} | |||
void* | |||
puglGetContext(PuglView* view) | |||
{ | |||
return NULL; | |||
// unused | |||
(void)view; | |||
} |
@@ -1,489 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
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_win.cpp Windows/WGL Pugl Implementation. | |||
*/ | |||
#include <winsock2.h> | |||
#include <windows.h> | |||
#include <windowsx.h> | |||
#include <GL/gl.h> | |||
#include <ctime> | |||
#include <cstdio> | |||
#include <cstdlib> | |||
#include "pugl/pugl_internal.h" | |||
#ifndef WM_MOUSEWHEEL | |||
# define WM_MOUSEWHEEL 0x020A | |||
#endif | |||
#ifndef WM_MOUSEHWHEEL | |||
# define WM_MOUSEHWHEEL 0x020E | |||
#endif | |||
#ifndef WHEEL_DELTA | |||
# define WHEEL_DELTA 120 | |||
#endif | |||
#ifndef GWLP_USERDATA | |||
# define GWLP_USERDATA (-21) | |||
#endif | |||
#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) | |||
HINSTANCE hInstance = NULL; | |||
struct PuglInternalsImpl { | |||
HWND hwnd; | |||
HDC hdc; | |||
HGLRC hglrc; | |||
WNDCLASS wc; | |||
}; | |||
LRESULT CALLBACK | |||
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
#if 0 | |||
extern "C" { | |||
BOOL WINAPI | |||
DllMain(HINSTANCE hInst, DWORD, LPVOID) | |||
{ | |||
hInstance = hInst; | |||
return 1; | |||
} | |||
} // extern "C" | |||
#endif | |||
PuglInternals* | |||
puglInitInternals() | |||
{ | |||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
} | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
} | |||
#endif | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
if (flush) { | |||
glFlush(); | |||
SwapBuffers(view->impl->hdc); | |||
} | |||
wglMakeCurrent(NULL, NULL); | |||
} | |||
#endif | |||
} | |||
int | |||
puglCreateWindow(PuglView* view, const char* title) | |||
{ | |||
PuglInternals* impl = view->impl; | |||
if (!title) { | |||
title = "Window"; | |||
} | |||
// FIXME: This is nasty, and pugl should not have static anything. | |||
// Should class be a parameter? Does this make sense on other platforms? | |||
static int wc_count = 0; | |||
char classNameBuf[256]; | |||
std::srand((std::time(NULL))); | |||
#ifdef __WINE__ | |||
std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||
#else | |||
_snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||
#endif | |||
classNameBuf[sizeof(classNameBuf)-1] = '\0'; | |||
impl->wc.style = CS_OWNDC; | |||
impl->wc.lpfnWndProc = wndProc; | |||
impl->wc.cbClsExtra = 0; | |||
impl->wc.cbWndExtra = 0; | |||
impl->wc.hInstance = hInstance; | |||
impl->wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); | |||
impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW); | |||
impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||
impl->wc.lpszMenuName = NULL; | |||
impl->wc.lpszClassName = strdup(classNameBuf); | |||
if (!RegisterClass(&impl->wc)) { | |||
free((void*)impl->wc.lpszClassName); | |||
free(impl); | |||
free(view); | |||
return 1; | |||
} | |||
int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
if (view->resizable) { | |||
winFlags |= WS_SIZEBOX; | |||
if (view->min_width > 0 && view->min_height > 0) { | |||
// Adjust the minimum window size to accomodate requested view size | |||
RECT mr = { 0, 0, view->min_width, view->min_height }; | |||
AdjustWindowRectEx(&mr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||
view->min_width = mr.right - mr.left; | |||
view->min_height = mr.bottom - mr.top; | |||
} | |||
} | |||
// Adjust the window size to accomodate requested view size | |||
RECT wr = { 0, 0, view->width, view->height }; | |||
AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||
impl->hwnd = CreateWindowEx( | |||
WS_EX_TOPMOST, | |||
classNameBuf, title, | |||
view->parent ? (WS_CHILD | WS_VISIBLE) : winFlags, | |||
CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, | |||
(HWND)view->parent, NULL, hInstance, NULL); | |||
if (!impl->hwnd) { | |||
UnregisterClass(impl->wc.lpszClassName, NULL); | |||
free((void*)impl->wc.lpszClassName); | |||
free(impl); | |||
free(view); | |||
return 1; | |||
} | |||
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | |||
impl->hdc = GetDC(impl->hwnd); | |||
PIXELFORMATDESCRIPTOR pfd; | |||
ZeroMemory(&pfd, sizeof(pfd)); | |||
pfd.nSize = sizeof(pfd); | |||
pfd.nVersion = 1; | |||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; | |||
pfd.iPixelType = PFD_TYPE_RGBA; | |||
pfd.cColorBits = 24; | |||
pfd.cDepthBits = 16; | |||
pfd.iLayerType = PFD_MAIN_PLANE; | |||
int format = ChoosePixelFormat(impl->hdc, &pfd); | |||
SetPixelFormat(impl->hdc, format, &pfd); | |||
impl->hglrc = wglCreateContext(impl->hdc); | |||
if (!impl->hglrc) { | |||
ReleaseDC (impl->hwnd, impl->hdc); | |||
DestroyWindow (impl->hwnd); | |||
UnregisterClass (impl->wc.lpszClassName, NULL); | |||
free((void*)impl->wc.lpszClassName); | |||
free(impl); | |||
free(view); | |||
return 1; | |||
} | |||
return PUGL_SUCCESS; | |||
} | |||
void | |||
puglShowWindow(PuglView* view) | |||
{ | |||
ShowWindow(view->impl->hwnd, SW_SHOWNORMAL); | |||
} | |||
void | |||
puglHideWindow(PuglView* view) | |||
{ | |||
ShowWindow(view->impl->hwnd, SW_HIDE); | |||
} | |||
void | |||
puglDestroy(PuglView* view) | |||
{ | |||
wglMakeCurrent(NULL, NULL); | |||
wglDeleteContext(view->impl->hglrc); | |||
ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||
DestroyWindow(view->impl->hwnd); | |||
UnregisterClass(view->impl->wc.lpszClassName, NULL); | |||
free((void*)view->impl->wc.lpszClassName); | |||
free(view->impl); | |||
free(view); | |||
} | |||
static void | |||
puglReshape(PuglView* view, int width, int height) | |||
{ | |||
puglEnterContext(view); | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, width, height); | |||
} else { | |||
puglDefaultReshape(view, width, height); | |||
} | |||
view->width = width; | |||
view->height = height; | |||
} | |||
static void | |||
puglDisplay(PuglView* view) | |||
{ | |||
puglEnterContext(view); | |||
view->redisplay = false; | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
puglLeaveContext(view, true); | |||
} | |||
static PuglKey | |||
keySymToSpecial(int sym) | |||
{ | |||
switch (sym) { | |||
case VK_F1: return PUGL_KEY_F1; | |||
case VK_F2: return PUGL_KEY_F2; | |||
case VK_F3: return PUGL_KEY_F3; | |||
case VK_F4: return PUGL_KEY_F4; | |||
case VK_F5: return PUGL_KEY_F5; | |||
case VK_F6: return PUGL_KEY_F6; | |||
case VK_F7: return PUGL_KEY_F7; | |||
case VK_F8: return PUGL_KEY_F8; | |||
case VK_F9: return PUGL_KEY_F9; | |||
case VK_F10: return PUGL_KEY_F10; | |||
case VK_F11: return PUGL_KEY_F11; | |||
case VK_F12: return PUGL_KEY_F12; | |||
case VK_LEFT: return PUGL_KEY_LEFT; | |||
case VK_UP: return PUGL_KEY_UP; | |||
case VK_RIGHT: return PUGL_KEY_RIGHT; | |||
case VK_DOWN: return PUGL_KEY_DOWN; | |||
case VK_PRIOR: return PUGL_KEY_PAGE_UP; | |||
case VK_NEXT: return PUGL_KEY_PAGE_DOWN; | |||
case VK_HOME: return PUGL_KEY_HOME; | |||
case VK_END: return PUGL_KEY_END; | |||
case VK_INSERT: return PUGL_KEY_INSERT; | |||
case VK_SHIFT: return PUGL_KEY_SHIFT; | |||
case VK_CONTROL: return PUGL_KEY_CTRL; | |||
case VK_MENU: return PUGL_KEY_ALT; | |||
case VK_LWIN: return PUGL_KEY_SUPER; | |||
case VK_RWIN: return PUGL_KEY_SUPER; | |||
} | |||
return (PuglKey)0; | |||
} | |||
static void | |||
processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) | |||
{ | |||
view->event_timestamp_ms = GetMessageTime(); | |||
if (press) { | |||
SetCapture(view->impl->hwnd); | |||
} else { | |||
ReleaseCapture(); | |||
} | |||
if (view->mouseFunc) { | |||
view->mouseFunc(view, button, press, | |||
GET_X_LPARAM(lParam), | |||
GET_Y_LPARAM(lParam)); | |||
} | |||
} | |||
static void | |||
setModifiers(PuglView* view) | |||
{ | |||
view->mods = 0; | |||
view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; | |||
view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; | |||
view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; | |||
view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||
view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||
} | |||
static LRESULT | |||
handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
{ | |||
PAINTSTRUCT ps; | |||
PuglKey key; | |||
RECT rect; | |||
MINMAXINFO* mmi; | |||
setModifiers(view); | |||
switch (message) { | |||
case WM_CREATE: | |||
case WM_SHOWWINDOW: | |||
case WM_SIZE: | |||
GetClientRect(view->impl->hwnd, &rect); | |||
puglReshape(view, rect.right, rect.bottom); | |||
view->width = rect.right; | |||
view->height = rect.bottom; | |||
break; | |||
case WM_GETMINMAXINFO: | |||
mmi = (MINMAXINFO*)lParam; | |||
mmi->ptMinTrackSize.x = view->min_width; | |||
mmi->ptMinTrackSize.y = view->min_height; | |||
break; | |||
case WM_PAINT: | |||
BeginPaint(view->impl->hwnd, &ps); | |||
puglDisplay(view); | |||
EndPaint(view->impl->hwnd, &ps); | |||
break; | |||
case WM_MOUSEMOVE: | |||
if (view->motionFunc) { | |||
view->event_timestamp_ms = GetMessageTime(); | |||
view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |||
} | |||
break; | |||
case WM_LBUTTONDOWN: | |||
processMouseEvent(view, 1, true, lParam); | |||
break; | |||
case WM_MBUTTONDOWN: | |||
processMouseEvent(view, 2, true, lParam); | |||
break; | |||
case WM_RBUTTONDOWN: | |||
processMouseEvent(view, 3, true, lParam); | |||
break; | |||
case WM_LBUTTONUP: | |||
processMouseEvent(view, 1, false, lParam); | |||
break; | |||
case WM_MBUTTONUP: | |||
processMouseEvent(view, 2, false, lParam); | |||
break; | |||
case WM_RBUTTONUP: | |||
processMouseEvent(view, 3, false, lParam); | |||
break; | |||
case WM_MOUSEWHEEL: | |||
if (view->scrollFunc) { | |||
view->event_timestamp_ms = GetMessageTime(); | |||
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||
ScreenToClient(view->impl->hwnd, &pt); | |||
view->scrollFunc( | |||
view, pt.x, pt.y, | |||
0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); | |||
} | |||
break; | |||
case WM_MOUSEHWHEEL: | |||
if (view->scrollFunc) { | |||
view->event_timestamp_ms = GetMessageTime(); | |||
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||
ScreenToClient(view->impl->hwnd, &pt); | |||
view->scrollFunc( | |||
view, pt.x, pt.y, | |||
GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); | |||
} | |||
break; | |||
case WM_KEYDOWN: | |||
if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { | |||
break; | |||
} // else nobreak | |||
case WM_KEYUP: | |||
view->event_timestamp_ms = GetMessageTime(); | |||
if ((key = keySymToSpecial(wParam))) { | |||
if (view->specialFunc) { | |||
view->specialFunc(view, message == WM_KEYDOWN, key); | |||
} | |||
} else if (view->keyboardFunc) { | |||
static BYTE kbs[256]; | |||
if (GetKeyboardState(kbs) != FALSE) { | |||
char lb[2]; | |||
UINT scanCode = (lParam >> 8) & 0xFFFFFF00; | |||
if ( 1 == ToAscii(wParam, scanCode, kbs, (LPWORD)lb, 0)) { | |||
view->keyboardFunc(view, message == WM_KEYDOWN, (char)lb[0]); | |||
} | |||
} | |||
} | |||
break; | |||
case WM_QUIT: | |||
case PUGL_LOCAL_CLOSE_MSG: | |||
if (view->closeFunc) { | |||
view->closeFunc(view); | |||
view->redisplay = false; | |||
} | |||
break; | |||
default: | |||
return DefWindowProc( | |||
view->impl->hwnd, message, wParam, lParam); | |||
} | |||
return 0; | |||
} | |||
void | |||
puglGrabFocus(PuglView* /*view*/) | |||
{ | |||
// TODO | |||
} | |||
PuglStatus | |||
puglProcessEvents(PuglView* view) | |||
{ | |||
MSG msg; | |||
while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { | |||
handleMessage(view, msg.message, msg.wParam, msg.lParam); | |||
} | |||
if (view->redisplay) { | |||
InvalidateRect(view->impl->hwnd, NULL, FALSE); | |||
} | |||
return PUGL_SUCCESS; | |||
} | |||
LRESULT CALLBACK | |||
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
{ | |||
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | |||
switch (message) { | |||
case WM_CREATE: | |||
PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | |||
return 0; | |||
case WM_CLOSE: | |||
PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); | |||
return 0; | |||
case WM_DESTROY: | |||
return 0; | |||
default: | |||
if (view && hwnd == view->impl->hwnd) { | |||
return handleMessage(view, message, wParam, lParam); | |||
} else { | |||
return DefWindowProc(hwnd, message, wParam, lParam); | |||
} | |||
} | |||
} | |||
void | |||
puglPostRedisplay(PuglView* view) | |||
{ | |||
view->redisplay = true; | |||
} | |||
PuglNativeWindow | |||
puglGetNativeWindow(PuglView* view) | |||
{ | |||
return (PuglNativeWindow)view->impl->hwnd; | |||
} | |||
void* | |||
puglGetContext(PuglView* /*view*/) | |||
{ | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
// TODO | |||
} | |||
#endif | |||
return NULL; | |||
} |
@@ -1,636 +0,0 @@ | |||
/* | |||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
Copyright 2013 Robin Gareus <robin@gareus.org> | |||
Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||
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_x11.c X11 Pugl Implementation. | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <X11/Xatom.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#include <X11/keysym.h> | |||
#ifdef PUGL_HAVE_GL | |||
#include <GL/gl.h> | |||
#include <GL/glx.h> | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
#include <cairo/cairo.h> | |||
#include <cairo/cairo-xlib.h> | |||
#endif | |||
#include "pugl/pugl_internal.h" | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
#define SOFD_HAVE_X11 | |||
#include "../sofd/libsofd.h" | |||
#include "../sofd/libsofd.c" | |||
#endif | |||
struct PuglInternalsImpl { | |||
Display* display; | |||
int screen; | |||
Window win; | |||
XIM xim; | |||
XIC xic; | |||
#ifdef PUGL_HAVE_CAIRO | |||
cairo_t* cr; | |||
cairo_surface_t* surface; | |||
#endif | |||
#ifdef PUGL_HAVE_GL | |||
GLXContext ctx; | |||
Bool doubleBuffered; | |||
#endif | |||
}; | |||
PuglInternals* | |||
puglInitInternals(void) | |||
{ | |||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
} | |||
static XVisualInfo* | |||
getVisual(PuglView* view) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
XVisualInfo* vi = NULL; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
/** | |||
Attributes for single-buffered RGBA with at least | |||
4 bits per color and a 16 bit depth buffer. | |||
*/ | |||
int attrListSgl[] = { | |||
GLX_RGBA, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
/** | |||
Attributes for double-buffered RGBA with at least | |||
4 bits per color and a 16 bit depth buffer. | |||
*/ | |||
int attrListDbl[] = { | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_ARB_multisample, 1, | |||
None | |||
}; | |||
/** | |||
Attributes for double-buffered RGBA with multi-sampling | |||
(antialiasing) | |||
*/ | |||
int attrListDblMS[] = { | |||
GLX_RGBA, | |||
GLX_DOUBLEBUFFER, | |||
GLX_RED_SIZE, 4, | |||
GLX_GREEN_SIZE, 4, | |||
GLX_BLUE_SIZE, 4, | |||
GLX_ALPHA_SIZE, 4, | |||
GLX_DEPTH_SIZE, 16, | |||
GLX_SAMPLE_BUFFERS, 1, | |||
GLX_SAMPLES, 4, | |||
None | |||
}; | |||
impl->doubleBuffered = True; | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||
if (vi == NULL) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||
PUGL_LOG("multisampling (antialiasing) is not available\n"); | |||
} | |||
if (vi == NULL) { | |||
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||
impl->doubleBuffered = False; | |||
PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n"); | |||
} | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
XVisualInfo pat; | |||
int n; | |||
pat.screen = impl->screen; | |||
vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); | |||
} | |||
#endif | |||
return vi; | |||
} | |||
static bool | |||
createContext(PuglView* view, XVisualInfo* vi) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
return (impl->ctx != NULL); | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
impl->surface = cairo_xlib_surface_create( | |||
impl->display, impl->win, vi->visual, view->width, view->height); | |||
if (impl->surface == NULL) { | |||
PUGL_LOG("failed to create cairo surface\n"); | |||
return false; | |||
} | |||
impl->cr = cairo_create(impl->surface); | |||
if (impl->cr == NULL) { | |||
cairo_surface_destroy(impl->surface); | |||
impl->surface = NULL; | |||
PUGL_LOG("failed to create cairo context\n"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
static void | |||
destroyContext(PuglView* view) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
glXDestroyContext(impl->display, impl->ctx); | |||
impl->ctx = NULL; | |||
} | |||
#endif | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
cairo_destroy(impl->cr); | |||
impl->cr = NULL; | |||
cairo_surface_destroy(impl->surface); | |||
impl->surface = NULL; | |||
} | |||
#endif | |||
} | |||
void | |||
puglEnterContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
} | |||
#endif | |||
} | |||
void | |||
puglLeaveContext(PuglView* view, bool flush) | |||
{ | |||
#ifdef PUGL_HAVE_GL | |||
if (view->ctx_type == PUGL_GL) { | |||
if (flush) { | |||
glFlush(); | |||
if (view->impl->doubleBuffered) { | |||
glXSwapBuffers(view->impl->display, view->impl->win); | |||
} | |||
} | |||
glXMakeCurrent(view->impl->display, None, NULL); | |||
} | |||
#endif | |||
} | |||
int | |||
puglCreateWindow(PuglView* view, const char* title) | |||
{ | |||
PuglInternals* const impl = view->impl; | |||
impl->display = XOpenDisplay(NULL); | |||
impl->screen = DefaultScreen(impl->display); | |||
XVisualInfo* const vi = getVisual(view); | |||
if (!vi) { | |||
XCloseDisplay(impl->display); | |||
impl->display = NULL; | |||
return 1; | |||
} | |||
#ifdef PUGL_HAVE_GL | |||
int glxMajor, glxMinor; | |||
glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||
#endif | |||
Window xParent = view->parent | |||
? (Window)view->parent | |||
: RootWindow(impl->display, impl->screen); | |||
Colormap cmap = XCreateColormap( | |||
impl->display, xParent, vi->visual, AllocNone); | |||
XSetWindowAttributes attr; | |||
memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||
attr.background_pixel = BlackPixel(impl->display, impl->screen); | |||
attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||
attr.colormap = cmap; | |||
attr.event_mask = (ExposureMask | StructureNotifyMask | | |||
EnterWindowMask | LeaveWindowMask | | |||
KeyPressMask | KeyReleaseMask | | |||
ButtonPressMask | ButtonReleaseMask | | |||
PointerMotionMask | FocusChangeMask); | |||
impl->win = XCreateWindow( | |||
impl->display, xParent, | |||
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
if (!createContext(view, vi)) { | |||
XDestroyWindow(impl->display, impl->win); | |||
impl->win = 0; | |||
XCloseDisplay(impl->display); | |||
impl->display = NULL; | |||
return 1; | |||
} | |||
XSizeHints sizeHints; | |||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||
if (!view->resizable) { | |||
sizeHints.flags = PMinSize|PMaxSize; | |||
sizeHints.min_width = view->width; | |||
sizeHints.min_height = view->height; | |||
sizeHints.max_width = view->width; | |||
sizeHints.max_height = view->height; | |||
XSetNormalHints(impl->display, impl->win, &sizeHints); | |||
} else if (view->min_width > 0 && view->min_height > 0) { | |||
sizeHints.flags = PMinSize; | |||
sizeHints.min_width = view->min_width; | |||
sizeHints.min_height = view->min_height; | |||
XSetNormalHints(impl->display, impl->win, &sizeHints); | |||
} | |||
if (title) { | |||
XStoreName(impl->display, impl->win, title); | |||
} | |||
if (!view->parent) { | |||
Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||
} | |||
if (glXIsDirect(impl->display, impl->ctx)) { | |||
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||
} else { | |||
PUGL_LOG("No DRI available\n"); | |||
} | |||
XFree(vi); | |||
return PUGL_SUCCESS; | |||
} | |||
void | |||
puglShowWindow(PuglView* view) | |||
{ | |||
XMapRaised(view->impl->display, view->impl->win); | |||
} | |||
void | |||
puglHideWindow(PuglView* view) | |||
{ | |||
XUnmapWindow(view->impl->display, view->impl->win); | |||
} | |||
void | |||
puglDestroy(PuglView* view) | |||
{ | |||
if (!view) { | |||
return; | |||
} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
x_fib_close(view->impl->display); | |||
#endif | |||
destroyContext(view); | |||
XDestroyWindow(view->impl->display, view->impl->win); | |||
XCloseDisplay(view->impl->display); | |||
free(view->impl); | |||
free(view); | |||
} | |||
static void | |||
puglReshape(PuglView* view, int width, int height) | |||
{ | |||
puglEnterContext(view); | |||
if (view->reshapeFunc) { | |||
view->reshapeFunc(view, width, height); | |||
} else { | |||
puglDefaultReshape(view, width, height); | |||
} | |||
puglLeaveContext(view, false); | |||
view->width = width; | |||
view->height = height; | |||
} | |||
static void | |||
puglDisplay(PuglView* view) | |||
{ | |||
puglEnterContext(view); | |||
view->redisplay = false; | |||
if (view->displayFunc) { | |||
view->displayFunc(view); | |||
} | |||
puglLeaveContext(view, true); | |||
} | |||
static PuglKey | |||
keySymToSpecial(KeySym sym) | |||
{ | |||
switch (sym) { | |||
case XK_F1: return PUGL_KEY_F1; | |||
case XK_F2: return PUGL_KEY_F2; | |||
case XK_F3: return PUGL_KEY_F3; | |||
case XK_F4: return PUGL_KEY_F4; | |||
case XK_F5: return PUGL_KEY_F5; | |||
case XK_F6: return PUGL_KEY_F6; | |||
case XK_F7: return PUGL_KEY_F7; | |||
case XK_F8: return PUGL_KEY_F8; | |||
case XK_F9: return PUGL_KEY_F9; | |||
case XK_F10: return PUGL_KEY_F10; | |||
case XK_F11: return PUGL_KEY_F11; | |||
case XK_F12: return PUGL_KEY_F12; | |||
case XK_Left: return PUGL_KEY_LEFT; | |||
case XK_Up: return PUGL_KEY_UP; | |||
case XK_Right: return PUGL_KEY_RIGHT; | |||
case XK_Down: return PUGL_KEY_DOWN; | |||
case XK_Page_Up: return PUGL_KEY_PAGE_UP; | |||
case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; | |||
case XK_Home: return PUGL_KEY_HOME; | |||
case XK_End: return PUGL_KEY_END; | |||
case XK_Insert: return PUGL_KEY_INSERT; | |||
case XK_Shift_L: return PUGL_KEY_SHIFT; | |||
case XK_Shift_R: return PUGL_KEY_SHIFT; | |||
case XK_Control_L: return PUGL_KEY_CTRL; | |||
case XK_Control_R: return PUGL_KEY_CTRL; | |||
case XK_Alt_L: return PUGL_KEY_ALT; | |||
case XK_Alt_R: return PUGL_KEY_ALT; | |||
case XK_Super_L: return PUGL_KEY_SUPER; | |||
case XK_Super_R: return PUGL_KEY_SUPER; | |||
} | |||
return (PuglKey)0; | |||
} | |||
static void | |||
setModifiers(PuglView* view, unsigned xstate, unsigned xtime) | |||
{ | |||
view->event_timestamp_ms = xtime; | |||
view->mods = 0; | |||
view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; | |||
view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; | |||
view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; | |||
view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; | |||
} | |||
static void | |||
dispatchKey(PuglView* view, XEvent* event, bool press) | |||
{ | |||
KeySym sym; | |||
char str[5]; | |||
PuglKey special; | |||
const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); | |||
if (sym == XK_Escape && view->closeFunc && !press && !view->parent) { | |||
view->closeFunc(view); | |||
view->redisplay = false; | |||
return; | |||
} | |||
if (n == 0 && sym == 0) { | |||
goto send_event; | |||
return; | |||
} | |||
if (n > 1) { | |||
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); | |||
goto send_event; | |||
return; | |||
} | |||
special = keySymToSpecial(sym); | |||
if (special && view->specialFunc) { | |||
if (view->specialFunc(view, press, special) == 0) { | |||
return; | |||
} | |||
} else if (!special && view->keyboardFunc) { | |||
if (view->keyboardFunc(view, press, str[0]) == 0) { | |||
return; | |||
} | |||
} | |||
send_event: | |||
if (view->parent != 0) { | |||
event->xkey.time = 0; // purposefully set an invalid time, used for feedback detection on bad hosts | |||
event->xany.window = view->parent; | |||
XSendEvent(view->impl->display, view->parent, False, NoEventMask, event); | |||
} | |||
} | |||
PuglStatus | |||
puglProcessEvents(PuglView* view) | |||
{ | |||
XEvent event; | |||
while (XPending(view->impl->display) > 0) { | |||
XNextEvent(view->impl->display, &event); | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
if (x_fib_handle_events(view->impl->display, &event)) { | |||
const int status = x_fib_status(); | |||
if (status > 0) { | |||
char* const filename = x_fib_filename(); | |||
x_fib_close(view->impl->display); | |||
if (view->fileSelectedFunc) { | |||
view->fileSelectedFunc(view, filename); | |||
} | |||
free(filename); | |||
} else if (status < 0) { | |||
x_fib_close(view->impl->display); | |||
if (view->fileSelectedFunc) { | |||
view->fileSelectedFunc(view, NULL); | |||
} | |||
} | |||
break; | |||
} | |||
#endif | |||
if (event.xany.window != view->impl->win && | |||
(view->parent == 0 || event.xany.window != (Window)view->parent)) { | |||
continue; | |||
} | |||
if ((event.type == KeyPress || event.type == KeyRelease) && event.xkey.time == 0) { | |||
continue; | |||
} | |||
switch (event.type) { | |||
case MapNotify: | |||
puglReshape(view, view->width, view->height); | |||
break; | |||
case ConfigureNotify: | |||
if ((event.xconfigure.width != view->width) || | |||
(event.xconfigure.height != view->height)) { | |||
puglReshape(view, | |||
event.xconfigure.width, | |||
event.xconfigure.height); | |||
} | |||
break; | |||
case Expose: | |||
if (event.xexpose.count != 0) { | |||
break; | |||
} | |||
puglDisplay(view); | |||
break; | |||
case MotionNotify: | |||
setModifiers(view, event.xmotion.state, event.xmotion.time); | |||
if (view->motionFunc) { | |||
view->motionFunc(view, event.xmotion.x, event.xmotion.y); | |||
} | |||
break; | |||
case ButtonPress: | |||
setModifiers(view, event.xbutton.state, event.xbutton.time); | |||
if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { | |||
if (view->scrollFunc) { | |||
float dx = 0, dy = 0; | |||
switch (event.xbutton.button) { | |||
case 4: dy = 1.0f; break; | |||
case 5: dy = -1.0f; break; | |||
case 6: dx = -1.0f; break; | |||
case 7: dx = 1.0f; break; | |||
} | |||
view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy); | |||
} | |||
break; | |||
} | |||
// nobreak | |||
case ButtonRelease: | |||
setModifiers(view, event.xbutton.state, event.xbutton.time); | |||
if (view->mouseFunc && | |||
(event.xbutton.button < 4 || event.xbutton.button > 7)) { | |||
view->mouseFunc(view, | |||
event.xbutton.button, event.type == ButtonPress, | |||
event.xbutton.x, event.xbutton.y); | |||
} | |||
break; | |||
case KeyPress: | |||
setModifiers(view, event.xkey.state, event.xkey.time); | |||
dispatchKey(view, &event, true); | |||
break; | |||
case KeyRelease: { | |||
setModifiers(view, event.xkey.state, event.xkey.time); | |||
bool repeated = false; | |||
if (view->ignoreKeyRepeat && | |||
XEventsQueued(view->impl->display, QueuedAfterReading)) { | |||
XEvent next; | |||
XPeekEvent(view->impl->display, &next); | |||
if (next.type == KeyPress && | |||
next.xkey.time == event.xkey.time && | |||
next.xkey.keycode == event.xkey.keycode) { | |||
XNextEvent(view->impl->display, &event); | |||
repeated = true; | |||
} | |||
} | |||
if (!repeated) { | |||
dispatchKey(view, &event, false); | |||
} | |||
} break; | |||
case ClientMessage: { | |||
char* type = XGetAtomName(view->impl->display, | |||
event.xclient.message_type); | |||
if (!strcmp(type, "WM_PROTOCOLS")) { | |||
if (view->closeFunc) { | |||
view->closeFunc(view); | |||
view->redisplay = false; | |||
} | |||
} | |||
XFree(type); | |||
} break; | |||
#ifdef PUGL_GRAB_FOCUS | |||
case EnterNotify: | |||
XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); | |||
break; | |||
#endif | |||
default: | |||
break; | |||
} | |||
} | |||
if (view->redisplay) { | |||
puglDisplay(view); | |||
} | |||
return PUGL_SUCCESS; | |||
} | |||
void | |||
puglPostRedisplay(PuglView* view) | |||
{ | |||
view->redisplay = true; | |||
} | |||
PuglNativeWindow | |||
puglGetNativeWindow(PuglView* view) | |||
{ | |||
return view->impl->win; | |||
} | |||
void* | |||
puglGetContext(PuglView* view) | |||
{ | |||
#ifdef PUGL_HAVE_CAIRO | |||
if (view->ctx_type == PUGL_CAIRO) { | |||
return view->impl->cr; | |||
} | |||
#endif | |||
return NULL; | |||
// may be unused | |||
(void)view; | |||
} |
@@ -1,570 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifdef DOXYGEN | |||
#include "src/DistrhoDefines.h" | |||
START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Intro */ | |||
/** | |||
@mainpage DISTRHO %Plugin Framework | |||
DISTRHO %Plugin Framework (or @b DPF for short) | |||
is a plugin framework designed to make development of new plugins an easy and enjoyable task.@n | |||
It allows developers to create plugins with custom UIs using a simple C++ API.@n | |||
The framework facilitates exporting various different plugin formats from the same code-base. | |||
DPF can build for LADSPA, DSSI, LV2 and VST2 formats.@n | |||
A JACK/Standalone mode is also available, allowing you to quickly test plugins. | |||
@section Macros | |||
You start by creating a "DistrhoPluginInfo.h" file describing the plugin via macros, see @ref PluginMacros.@n | |||
This file is included in the main DPF code to select which features to activate for each plugin format. | |||
For example, a plugin (with %UI) that use states will require LV2 hosts to support Atom and Worker extensions for | |||
message passing from the %UI to the plugin.@n | |||
If your plugin does not make use of states, the Worker extension is not set as a required feature. | |||
@section Plugin | |||
The next step is to create your plugin code by subclassing DPF's Plugin class.@n | |||
You need to pass the number of parameters in the constructor and also the number of programs and states, if any. | |||
Here's an example of an audio plugin that simply mutes the host output: | |||
@code | |||
class MutePlugin : public Plugin | |||
{ | |||
public: | |||
/** | |||
Plugin class constructor. | |||
*/ | |||
MutePlugin() | |||
: Plugin(0, 0, 0) // 0 parameters, 0 programs and 0 states | |||
{ | |||
} | |||
protected: | |||
/* ---------------------------------------------------------------------------------------- | |||
* Information */ | |||
/** | |||
Get the plugin label. | |||
This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. | |||
*/ | |||
const char* getLabel() const override | |||
{ | |||
return "Mute"; | |||
} | |||
/** | |||
Get the plugin author/maker. | |||
*/ | |||
const char* getMaker() const override | |||
{ | |||
return "DPF"; | |||
} | |||
/** | |||
Get the plugin license name (a single line of text). | |||
For commercial plugins this should return some short copyright information. | |||
*/ | |||
const char* getLicense() const override | |||
{ | |||
return "MIT"; | |||
} | |||
/** | |||
Get the plugin version, in hexadecimal. | |||
*/ | |||
uint32_t getVersion() const override | |||
{ | |||
return d_version(1, 0, 0); | |||
} | |||
/** | |||
Get the plugin unique Id. | |||
This value is used by LADSPA, DSSI and VST plugin formats. | |||
*/ | |||
int64_t getUniqueId() const override | |||
{ | |||
return d_cconst('M', 'u', 't', 'e'); | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* This example has no parameters, so skip parameter stuff */ | |||
void initParameter(uint32_t, Parameter&) override {} | |||
float getParameterValue(uint32_t) const override { return 0.0f; } | |||
void setParameterValue(uint32_t, float) override {} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Audio/MIDI Processing */ | |||
/** | |||
Run/process function for plugins without MIDI input. | |||
NOTE: Some parameters might be null if there are no audio inputs or outputs. | |||
*/ | |||
void run(const float**, float** outputs, uint32_t frames) override | |||
{ | |||
// get the left and right audio outputs | |||
float* const outL = outputs[0]; | |||
float* const outR = outputs[1]; | |||
// mute audio | |||
std::memset(outL, 0, sizeof(float)*frames); | |||
std::memset(outR, 0, sizeof(float)*frames); | |||
} | |||
}; | |||
@endcode | |||
See the Plugin class for more information and to understand what each function does. | |||
@section Parameters | |||
A plugin is nothing without parameters.@n | |||
In DPF parameters can be inputs or outputs.@n | |||
They have hints to describe how they behave plus a name and a symbol identifying them.@n | |||
Parameters also have 'ranges' – a minimum, maximum and default value. | |||
Input parameters are "read-only": the plugin can read them but not change them. | |||
(the exception being when changing programs, more on that below)@n | |||
It's the host responsibility to save, restore and set input parameters. | |||
Output parameters can be changed at anytime by the plugin.@n | |||
The host will simply read their values and not change them. | |||
Here's an example of an audio plugin that has 1 input parameter: | |||
@code | |||
class GainPlugin : public Plugin | |||
{ | |||
public: | |||
/** | |||
Plugin class constructor. | |||
You must set all parameter values to their defaults, matching ParameterRanges::def. | |||
*/ | |||
GainPlugin() | |||
: Plugin(1, 0, 0), // 1 parameter, 0 programs and 0 states | |||
fGain(1.0f) | |||
{ | |||
} | |||
protected: | |||
/* ---------------------------------------------------------------------------------------- | |||
* Information */ | |||
const char* getLabel() const override | |||
{ | |||
return "Gain"; | |||
} | |||
const char* getMaker() const override | |||
{ | |||
return "DPF"; | |||
} | |||
const char* getLicense() const override | |||
{ | |||
return "MIT"; | |||
} | |||
uint32_t getVersion() const override | |||
{ | |||
return d_version(1, 0, 0); | |||
} | |||
int64_t getUniqueId() const override | |||
{ | |||
return d_cconst('G', 'a', 'i', 'n'); | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Init */ | |||
/** | |||
Initialize a parameter. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initParameter(uint32_t index, Parameter& parameter) override | |||
{ | |||
// we only have one parameter so we can skip checking the index | |||
parameter.hints = kParameterIsAutomable; | |||
parameter.name = "Gain"; | |||
parameter.symbol = "gain"; | |||
parameter.ranges.min = 0.0f; | |||
parameter.ranges.max = 2.0f; | |||
parameter.ranges.def = 1.0f; | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Internal data */ | |||
/** | |||
Get the current value of a parameter. | |||
*/ | |||
float getParameterValue(uint32_t index) const override | |||
{ | |||
// same as before, ignore index check | |||
return fGain; | |||
} | |||
/** | |||
Change a parameter value. | |||
*/ | |||
void setParameterValue(uint32_t index, float value) override | |||
{ | |||
// same as before, ignore index check | |||
fGain = value; | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Audio/MIDI Processing */ | |||
void run(const float**, float** outputs, uint32_t frames) override | |||
{ | |||
// get the mono input and output | |||
const float* const in = inputs[0]; | |||
/* */ float* const out = outputs[0]; | |||
// apply gain against all samples | |||
for (uint32_t i=0; i < frames; ++i) | |||
out[i] = in[i] * fGain; | |||
} | |||
private: | |||
float fGain; | |||
}; | |||
@endcode | |||
See the Parameter struct for more information about parameters. | |||
@section Programs | |||
Programs in DPF refer to plugin-side presets (usually called "factory presets"), | |||
an initial set of presets provided by plugin authors included in the actual plugin. | |||
To use programs you must first enable them by setting @ref DISTRHO_PLUGIN_WANT_PROGRAMS to 1 in your DistrhoPluginInfo.h file.@n | |||
When enabled you'll need to override 2 new function in your plugin code, | |||
Plugin::initProgramName(uint32_t, String&) and Plugin::loadProgram(uint32_t). | |||
Here's an example of a plugin with a "default" program: | |||
@code | |||
class PluginWithPresets : public Plugin | |||
{ | |||
public: | |||
PluginWithPresets() | |||
: Plugin(2, 1, 0), // 2 parameters, 1 program and 0 states | |||
fGainL(1.0f), | |||
fGainR(1.0f), | |||
{ | |||
} | |||
protected: | |||
/* ---------------------------------------------------------------------------------------- | |||
* Information */ | |||
const char* getLabel() const override | |||
{ | |||
return "Prog"; | |||
} | |||
const char* getMaker() const override | |||
{ | |||
return "DPF"; | |||
} | |||
const char* getLicense() const override | |||
{ | |||
return "MIT"; | |||
} | |||
uint32_t getVersion() const override | |||
{ | |||
return d_version(1, 0, 0); | |||
} | |||
int64_t getUniqueId() const override | |||
{ | |||
return d_cconst('P', 'r', 'o', 'g'); | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Init */ | |||
/** | |||
Initialize a parameter. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initParameter(uint32_t index, Parameter& parameter) override | |||
{ | |||
parameter.hints = kParameterIsAutomable; | |||
parameter.ranges.min = 0.0f; | |||
parameter.ranges.max = 2.0f; | |||
parameter.ranges.def = 1.0f; | |||
switch (index) | |||
{ | |||
case 0; | |||
parameter.name = "Gain Right"; | |||
parameter.symbol = "gainR"; | |||
break; | |||
case 1; | |||
parameter.name = "Gain Left"; | |||
parameter.symbol = "gainL"; | |||
break; | |||
} | |||
} | |||
/** | |||
Set the name of the program @a index. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initProgramName(uint32_t index, String& programName) | |||
{ | |||
switch(index) | |||
{ | |||
case 0: | |||
programName = "Default"; | |||
break; | |||
} | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Internal data */ | |||
/** | |||
Get the current value of a parameter. | |||
*/ | |||
float getParameterValue(uint32_t index) const override | |||
{ | |||
switch (index) | |||
{ | |||
case 0; | |||
return fGainL; | |||
case 1; | |||
return fGainR; | |||
} | |||
} | |||
/** | |||
Change a parameter value. | |||
*/ | |||
void setParameterValue(uint32_t index, float value) override | |||
{ | |||
switch (index) | |||
{ | |||
case 0; | |||
fGainL = value; | |||
break; | |||
case 1; | |||
fGainR = value; | |||
break; | |||
} | |||
} | |||
/** | |||
Load a program. | |||
*/ | |||
void loadProgram(uint32_t index) | |||
{ | |||
switch(index) | |||
{ | |||
case 0: | |||
fGainL = 1.0f; | |||
fGainR = 1.0f; | |||
break; | |||
} | |||
} | |||
/* ---------------------------------------------------------------------------------------- | |||
* Audio/MIDI Processing */ | |||
void run(const float**, float** outputs, uint32_t frames) override | |||
{ | |||
// get the left and right audio buffers | |||
const float* const inL = inputs[0]; | |||
const float* const inR = inputs[0]; | |||
/* */ float* const outL = outputs[0]; | |||
/* */ float* const outR = outputs[0]; | |||
// apply gain against all samples | |||
for (uint32_t i=0; i < frames; ++i) | |||
{ | |||
outL[i] = inL[i] * fGainL; | |||
outR[i] = inR[i] * fGainR; | |||
} | |||
} | |||
private: | |||
float fGainL, fGainR; | |||
}; | |||
@endcode | |||
@section States | |||
describe them | |||
@section MIDI | |||
describe them | |||
@section Latency | |||
describe it | |||
@section Time-Position | |||
describe it | |||
@section UI | |||
describe them | |||
*/ | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Plugin Macros */ | |||
/** | |||
@defgroup PluginMacros Plugin Macros | |||
C Macros that describe your plugin. (defined in the "DistrhoPluginInfo.h" file) | |||
With these macros you can tell the host what features your plugin requires.@n | |||
Depending on which macros you enable, new functions will be available to call and/or override. | |||
All values are either integer or strings.@n | |||
For boolean-like values 1 means 'on' and 0 means 'off'. | |||
The values defined in this group are for documentation purposes only.@n | |||
All macros are disabled by default. | |||
Only 4 macros are required, they are: | |||
- @ref DISTRHO_PLUGIN_NAME | |||
- @ref DISTRHO_PLUGIN_NUM_INPUTS | |||
- @ref DISTRHO_PLUGIN_NUM_OUTPUTS | |||
- @ref DISTRHO_PLUGIN_URI | |||
@{ | |||
*/ | |||
/** | |||
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 "Plugin Name" | |||
/** | |||
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 "urn:distrho:name" | |||
/** | |||
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 1 | |||
/** | |||
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 1 | |||
/** | |||
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 1 | |||
/** | |||
Wherever the plugin wants MIDI output. | |||
@see Plugin::writeMidiEvent(const MidiEvent&) | |||
*/ | |||
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 | |||
/** | |||
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 1 | |||
/** | |||
Wherever the plugin wants time position information from the host. | |||
@see Plugin::getTimePosition() | |||
*/ | |||
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 | |||
/** | |||
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 1 | |||
/** | |||
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" | |||
/** @} */ | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DOXYGEN |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -35,15 +35,43 @@ START_NAMESPACE_DISTRHO | |||
*/ | |||
/** | |||
Audio port can be used as control voltage (LV2 only). | |||
Audio port can be used as control voltage (LV2 and JACK standalone only). | |||
*/ | |||
static const uint32_t kAudioPortIsCV = 0x1; | |||
/** | |||
Audio port should be used as sidechan (LV2 only). | |||
Audio port should be used as sidechan (LV2 and VST3 only). | |||
This hint should not be used with CV style ports. | |||
@note non-sidechain audio ports must exist in the plugin if this flag is set. | |||
*/ | |||
static const uint32_t kAudioPortIsSidechain = 0x2; | |||
/** | |||
CV port has bipolar range (-1 to +1, or -5 to +5 if scaled). | |||
This is merely a hint to tell the host what value range to expect. | |||
*/ | |||
static const uint32_t kCVPortHasBipolarRange = 0x10; | |||
/** | |||
CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled). | |||
This is merely a hint to tell the host what value range to expect. | |||
*/ | |||
static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20; | |||
/** | |||
CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled). | |||
This is merely a hint to tell the host what value range to expect. | |||
*/ | |||
static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40; | |||
/** | |||
CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar). | |||
One other range flag is required if this flag is set. | |||
When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform. | |||
*/ | |||
static const uint32_t kCVPortHasScaledRange = 0x80; | |||
/** @} */ | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
@@ -58,10 +86,14 @@ static const uint32_t kAudioPortIsSidechain = 0x2; | |||
*/ | |||
/** | |||
Parameter is automable (real-time safe). | |||
Parameter is automatable (real-time safe). | |||
@see Plugin::setParameterValue(uint32_t, float) | |||
*/ | |||
static const uint32_t kParameterIsAutomable = 0x01; | |||
static const uint32_t kParameterIsAutomatable = 0x01; | |||
/** It was a typo, sorry.. */ | |||
DISTRHO_DEPRECATED_BY("kParameterIsAutomatable") | |||
static const uint32_t kParameterIsAutomable = kParameterIsAutomatable; | |||
/** | |||
Parameter value is boolean.@n | |||
@@ -83,9 +115,12 @@ static const uint32_t kParameterIsLogarithmic = 0x08; | |||
Parameter is of output type.@n | |||
When unset, parameter is assumed to be of input type. | |||
Parameter inputs are changed by the host and must not be changed by the plugin.@n | |||
The only exception being when changing programs, see Plugin::loadProgram().@n | |||
Parameter inputs are changed by the host and typically should not be changed by the plugin.@n | |||
One exception is when changing programs, see Plugin::loadProgram().@n | |||
The other exception is with parameter change requests, see Plugin::requestParameterValueChange().@n | |||
Outputs are changed by the plugin and never modified by the host. | |||
If you are targetting VST2, make sure to order your parameters so that all inputs are before any outputs. | |||
*/ | |||
static const uint32_t kParameterIsOutput = 0x10; | |||
@@ -100,6 +135,52 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; | |||
/** @} */ | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* State Hints */ | |||
/** | |||
@defgroup StateHints State Hints | |||
Various state hints. | |||
@see State::hints | |||
@{ | |||
*/ | |||
/** | |||
State is visible and readable by hosts that support string-type plugin parameters. | |||
*/ | |||
static const uint32_t kStateIsHostReadable = 0x01; | |||
/** | |||
State is writable by the host, allowing users to arbitrarily change the state.@n | |||
For obvious reasons a writable state is also readable by the host. | |||
*/ | |||
static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable; | |||
/** | |||
State is a filename path instead of a regular string.@n | |||
The readable and writable hints are required for filenames to work, and thus are automatically set. | |||
*/ | |||
static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable; | |||
/** | |||
State is a base64 encoded string. | |||
*/ | |||
static const uint32_t kStateIsBase64Blob = 0x08; | |||
/** | |||
State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes. | |||
*/ | |||
static const uint32_t kStateIsOnlyForDSP = 0x10; | |||
/** | |||
State is for UI side only.@n | |||
If the DSP and UI are separate and the UI is not available, this property won't be saved. | |||
*/ | |||
static const uint32_t kStateIsOnlyForUI = 0x20; | |||
/** @} */ | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Base Plugin structs */ | |||
@@ -108,8 +189,61 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; | |||
@{ | |||
*/ | |||
/** | |||
Parameter designation.@n | |||
Allows a parameter to be specially designated for a task, like bypass. | |||
Each designation is unique, there must be only one parameter that uses it.@n | |||
The use of designated parameters is completely optional. | |||
@note Designated parameters have strict ranges. | |||
@see ParameterRanges::adjustForDesignation() | |||
*/ | |||
enum ParameterDesignation { | |||
/** | |||
Null or unset designation. | |||
*/ | |||
kParameterDesignationNull = 0, | |||
/** | |||
Bypass designation.@n | |||
When on (> 0.5f), it means the plugin must run in a bypassed state. | |||
*/ | |||
kParameterDesignationBypass = 1 | |||
}; | |||
/** | |||
Predefined Port Groups Ids. | |||
This enumeration provides a few commonly used groups for convenient use in plugins. | |||
For preventing conflicts with user code, negative values are used here. | |||
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. | |||
@see PortGroup | |||
*/ | |||
enum PredefinedPortGroupsIds { | |||
/** | |||
Null or unset port group. | |||
*/ | |||
kPortGroupNone = (uint32_t)-1, | |||
/** | |||
A single channel audio group. | |||
*/ | |||
kPortGroupMono = (uint32_t)-2, | |||
/** | |||
A 2-channel discrete stereo audio group, | |||
where the 1st audio port is the left channel and the 2nd port is the right channel. | |||
*/ | |||
kPortGroupStereo = (uint32_t)-3 | |||
}; | |||
/** | |||
Audio Port. | |||
Can be used as CV port by specifying kAudioPortIsCV in hints,@n | |||
but this is only supported in LV2 and JACK standalone formats. | |||
*/ | |||
struct AudioPort { | |||
/** | |||
@@ -134,35 +268,23 @@ struct AudioPort { | |||
String symbol; | |||
/** | |||
Default constructor for a regular audio port. | |||
*/ | |||
AudioPort() noexcept | |||
: hints(0x0), | |||
name(), | |||
symbol() {} | |||
}; | |||
/** | |||
Parameter designation.@n | |||
Allows a parameter to be specially designated for a task, like bypass. | |||
Each designation is unique, there must be only one parameter that uses it.@n | |||
The use of designated parameters is completely optional. | |||
The group id that this audio/cv port belongs to. | |||
No group is assigned by default. | |||
@note Designated parameters have strict ranges. | |||
@see ParameterRanges::adjustForDesignation() | |||
*/ | |||
enum ParameterDesignation { | |||
/** | |||
Null or unset designation. | |||
You can use a group from PredefinedPortGroups or roll your own.@n | |||
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. | |||
@see PortGroup, Plugin::initPortGroup | |||
*/ | |||
kParameterDesignationNull = 0, | |||
uint32_t groupId; | |||
/** | |||
Bypass designation.@n | |||
When on (> 0.5f), it means the plugin must run in a bypassed state. | |||
Default constructor for a regular audio port. | |||
*/ | |||
kParameterDesignationBypass = 1 | |||
AudioPort() noexcept | |||
: hints(0x0), | |||
name(), | |||
symbol(), | |||
groupId(kPortGroupNone) {} | |||
}; | |||
/** | |||
@@ -189,7 +311,7 @@ struct ParameterRanges { | |||
float max; | |||
/** | |||
Default constructor, using 0.0 as minimum, 1.0 as maximum and 0.0 as default. | |||
Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum. | |||
*/ | |||
ParameterRanges() noexcept | |||
: def(0.0f), | |||
@@ -226,7 +348,7 @@ struct ParameterRanges { | |||
/** | |||
Get a fixed value within range. | |||
*/ | |||
const float& getFixedValue(const float& value) const noexcept | |||
float getFixedValue(const float& value) const noexcept | |||
{ | |||
if (value <= min) | |||
return min; | |||
@@ -333,9 +455,9 @@ struct ParameterEnumerationValues { | |||
/** | |||
Array of @ParameterEnumerationValue items.@n | |||
This pointer must be null or have been allocated on the heap with `new`. | |||
This pointer must be null or have been allocated on the heap with `new ParameterEnumerationValue[count]`. | |||
*/ | |||
const ParameterEnumerationValue* values; | |||
ParameterEnumerationValue* values; | |||
/** | |||
Default constructor, for zero enumeration values. | |||
@@ -349,7 +471,7 @@ struct ParameterEnumerationValues { | |||
Constructor using custom values.@n | |||
The pointer to @values must have been allocated on the heap with `new`. | |||
*/ | |||
ParameterEnumerationValues(uint32_t c, bool r, const ParameterEnumerationValue* v) noexcept | |||
ParameterEnumerationValues(uint32_t c, bool r, ParameterEnumerationValue* v) noexcept | |||
: count(c), | |||
restrictedMode(r), | |||
values(v) {} | |||
@@ -365,6 +487,8 @@ struct ParameterEnumerationValues { | |||
values = nullptr; | |||
} | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues) | |||
}; | |||
/** | |||
@@ -384,6 +508,13 @@ struct Parameter { | |||
*/ | |||
String name; | |||
/** | |||
The short name of this parameter.@n | |||
Used when displaying the parameter name in a very limited space. | |||
@note This value is optional, the full name is used when the short one is missing. | |||
*/ | |||
String shortName; | |||
/** | |||
The symbol of this parameter.@n | |||
A parameter symbol is a short restricted name used as a machine and human readable identifier.@n | |||
@@ -399,6 +530,12 @@ struct Parameter { | |||
*/ | |||
String unit; | |||
/** | |||
An extensive description/comment about the parameter. | |||
@note This value is optional and only used for LV2. | |||
*/ | |||
String description; | |||
/** | |||
Ranges of this parameter.@n | |||
The ranges describe the default, minimum and maximum values. | |||
@@ -424,18 +561,30 @@ struct Parameter { | |||
*/ | |||
uint8_t midiCC; | |||
/** | |||
The group id that this parameter belongs to. | |||
No group is assigned by default. | |||
You can use a group from PredefinedPortGroups or roll your own.@n | |||
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. | |||
@see PortGroup, Plugin::initPortGroup | |||
*/ | |||
uint32_t groupId; | |||
/** | |||
Default constructor for a null parameter. | |||
*/ | |||
Parameter() noexcept | |||
: hints(0x0), | |||
name(), | |||
shortName(), | |||
symbol(), | |||
unit(), | |||
ranges(), | |||
enumValues(), | |||
designation(kParameterDesignationNull), | |||
midiCC(0) {} | |||
midiCC(0), | |||
groupId(kPortGroupNone) {} | |||
/** | |||
Constructor using custom values. | |||
@@ -443,12 +592,14 @@ struct Parameter { | |||
Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept | |||
: hints(h), | |||
name(n), | |||
shortName(), | |||
symbol(s), | |||
unit(u), | |||
ranges(def, min, max), | |||
enumValues(), | |||
designation(kParameterDesignationNull), | |||
midiCC(0) {} | |||
midiCC(0), | |||
groupId(kPortGroupNone) {} | |||
/** | |||
Initialize a parameter for a specific designation. | |||
@@ -462,11 +613,13 @@ struct Parameter { | |||
case kParameterDesignationNull: | |||
break; | |||
case kParameterDesignationBypass: | |||
hints = kParameterIsAutomable|kParameterIsBoolean|kParameterIsInteger; | |||
name = "Bypass"; | |||
symbol = "dpf_bypass"; | |||
unit = ""; | |||
midiCC = 0; | |||
hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger; | |||
name = "Bypass"; | |||
shortName = "Bypass"; | |||
symbol = "dpf_bypass"; | |||
unit = ""; | |||
midiCC = 0; | |||
groupId = kPortGroupNone; | |||
ranges.def = 0.0f; | |||
ranges.min = 0.0f; | |||
ranges.max = 1.0f; | |||
@@ -475,6 +628,83 @@ struct Parameter { | |||
} | |||
}; | |||
/** | |||
Port Group.@n | |||
Allows to group together audio/cv ports or parameters. | |||
Each unique group MUST have an unique symbol and a name. | |||
A group can be applied to both inputs and outputs (at the same time). | |||
The same group cannot be used in audio ports and parameters. | |||
An audio port group logically combines ports which should be considered part of the same stream.@n | |||
For example, two audio ports in a group may form a stereo stream. | |||
A parameter group provides meta-data to the host to indicate that some parameters belong together. | |||
The use of port groups is completely optional. | |||
@see Plugin::initPortGroup, AudioPort::group, Parameter::group | |||
*/ | |||
struct PortGroup { | |||
/** | |||
The name of this port group.@n | |||
A port group name can contain any character, but hosts might have a hard time with non-ascii ones.@n | |||
The name doesn't have to be unique within a plugin instance, but it's recommended. | |||
*/ | |||
String name; | |||
/** | |||
The symbol of this port group.@n | |||
A port group symbol is a short restricted name used as a machine and human readable identifier.@n | |||
The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. | |||
@note Port group symbols MUST be unique within a plugin instance. | |||
*/ | |||
String symbol; | |||
}; | |||
/** | |||
State. | |||
In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n | |||
By default states are completely internal to the plugin and not visible by the host.@n | |||
Flags can be set to allow hosts to see and/or change them. | |||
TODO API under construction | |||
*/ | |||
struct State { | |||
/** | |||
Hints describing this state. | |||
@note Changing these hints can break compatibility with previously saved data. | |||
@see StateHints | |||
*/ | |||
uint32_t hints; | |||
/** | |||
The key or "symbol" of this state.@n | |||
A state key is a short restricted name used as a machine and human readable identifier. | |||
@note State keys MUST be unique within a plugin instance. | |||
TODO define rules for allowed characters, must be usable as URI non-encoded parameters | |||
*/ | |||
String key; | |||
/** | |||
The default value of this state.@n | |||
Can be left empty if considered a valid initial state. | |||
*/ | |||
String defaultValue; | |||
/** | |||
String representation of this state. | |||
*/ | |||
String label; | |||
/** | |||
An extensive description/comment about this state. | |||
@note This value is optional and only used for LV2. | |||
*/ | |||
String description; | |||
}; | |||
/** | |||
MIDI event. | |||
*/ | |||
@@ -497,6 +727,9 @@ struct MidiEvent { | |||
/** | |||
MIDI data.@n | |||
If size > kDataSize, dataExt is used (otherwise null). | |||
When dataExt is used, the event holder is responsible for | |||
keeping the pointer valid during the entirety of the run function. | |||
*/ | |||
uint8_t data[kDataSize]; | |||
const uint8_t* dataExt; | |||
@@ -507,7 +740,7 @@ struct MidiEvent { | |||
The @a playing and @a frame values are always valid.@n | |||
BBT values are only valid when @a bbt.valid is true. | |||
This struct is inspired by the JACK Transport API. | |||
This struct is inspired by the [JACK Transport API](https://jackaudio.org/api/structjack__position__t.html). | |||
*/ | |||
struct TimePosition { | |||
/** | |||
@@ -546,10 +779,11 @@ struct TimePosition { | |||
/** | |||
Current tick within beat.@n | |||
Should always be > 0 and <= @a ticksPerBeat.@n | |||
Should always be >= 0 and < @a ticksPerBeat.@n | |||
The first tick is tick '0'. | |||
@note Fraction part of tick is only available on some plugin formats. | |||
*/ | |||
int32_t tick; | |||
double tick; | |||
/** | |||
Number of ticks that have elapsed between frame 0 and the first beat of the current measure. | |||
@@ -567,7 +801,7 @@ struct TimePosition { | |||
float beatType; | |||
/** | |||
Number of ticks within a bar.@n | |||
Number of ticks within a beat.@n | |||
Usually a moderately large integer with many denominators, such as 1920.0. | |||
*/ | |||
double ticksPerBeat; | |||
@@ -590,6 +824,22 @@ struct TimePosition { | |||
beatType(0.0f), | |||
ticksPerBeat(0.0), | |||
beatsPerMinute(0.0) {} | |||
/** | |||
Reinitialize this position using the default null initialization. | |||
*/ | |||
void clear() noexcept | |||
{ | |||
valid = false; | |||
bar = 0; | |||
beat = 0; | |||
tick = 0; | |||
barStartTick = 0.0; | |||
beatsPerBar = 0.0f; | |||
beatType = 0.0f; | |||
ticksPerBeat = 0.0; | |||
beatsPerMinute = 0.0; | |||
} | |||
} bbt; | |||
/** | |||
@@ -599,6 +849,16 @@ struct TimePosition { | |||
: playing(false), | |||
frame(0), | |||
bbt() {} | |||
/** | |||
Reinitialize this position using the default null initialization. | |||
*/ | |||
void clear() noexcept | |||
{ | |||
playing = false; | |||
frame = 0; | |||
bbt.clear(); | |||
} | |||
}; | |||
/** @} */ | |||
@@ -670,6 +930,22 @@ public: | |||
*/ | |||
double getSampleRate() const noexcept; | |||
/** | |||
Get the bundle path where the plugin resides. | |||
Can return null if the plugin is not available in a bundle (if it is a single binary). | |||
@see getBinaryFilename | |||
@see getResourcePath | |||
*/ | |||
const char* getBundlePath() const noexcept; | |||
/** | |||
Check if this plugin instance is a "dummy" one used for plugin meta-data/information export.@n | |||
When true no processing will be done, the plugin is created only to extract information.@n | |||
In DPF, LADSPA/DSSI, VST2 and VST3 formats create one global instance per plugin binary | |||
while LV2 creates one when generating turtle meta-data. | |||
*/ | |||
bool isDummyInstance() const noexcept; | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
/** | |||
Get the current host transport time position.@n | |||
@@ -698,6 +974,37 @@ public: | |||
bool writeMidiEvent(const MidiEvent& midiEvent) noexcept; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
/** | |||
Check if parameter value change requests will work with the current plugin host. | |||
@note This function is only available if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST is enabled. | |||
@see requestParameterValueChange(uint32_t, float) | |||
*/ | |||
bool canRequestParameterValueChanges() const noexcept; | |||
/** | |||
Request a parameter value change from the host. | |||
If successful, this function will automatically trigger a parameter update on the UI side as well. | |||
This function can fail, for example if the host is busy with the parameter for read-only automation. | |||
Some hosts simply do not have this functionality, which can be verified with canRequestParameterValueChanges(). | |||
@note This function is only available if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST is enabled. | |||
*/ | |||
bool requestParameterValueChange(uint32_t index, float value) noexcept; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
/** | |||
Set state value and notify the host about the change.@n | |||
This function will call `setState()` and also trigger an update on the UI side as necessary.@n | |||
It must not be called during run.@n | |||
The state must be host readable. | |||
@note this function does nothing on DSSI plugin format, as DSSI only supports UI->DSP messages. | |||
TODO API under construction | |||
*/ | |||
bool updateStateValue(const char* key, const char* value) noexcept; | |||
#endif | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Information */ | |||
@@ -763,7 +1070,14 @@ protected: | |||
Initialize the parameter @a index.@n | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
virtual void initParameter(uint32_t index, Parameter& parameter) = 0; | |||
virtual void initParameter(uint32_t index, Parameter& parameter); | |||
/** | |||
Initialize the port group @a groupId.@n | |||
This function will be called once, | |||
shortly after the plugin is created and all audio ports and parameters have been enumerated. | |||
*/ | |||
virtual void initPortGroup(uint32_t groupId, PortGroup& portGroup); | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
/** | |||
@@ -776,11 +1090,17 @@ protected: | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
/** | |||
Set the state key and default value of @a index.@n | |||
Initialize the state @a index.@n | |||
This function will be called once, shortly after the plugin is created.@n | |||
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. | |||
*/ | |||
virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; | |||
virtual void initState(uint32_t index, State& state); | |||
DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)") | |||
virtual void initState(uint32_t, String&, String&) {} | |||
DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)") | |||
virtual bool isStateFile(uint32_t) { return false; } | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
@@ -790,15 +1110,15 @@ protected: | |||
Get the current value of a parameter.@n | |||
The host may call this function from any context, including realtime processing. | |||
*/ | |||
virtual float getParameterValue(uint32_t index) const = 0; | |||
virtual float getParameterValue(uint32_t index) const; | |||
/** | |||
Change a parameter value.@n | |||
The host may call this function from any context, including realtime processing.@n | |||
When a parameter is marked as automable, you must ensure no non-realtime operations are performed. | |||
When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. | |||
@note This function will only be called for parameter inputs. | |||
*/ | |||
virtual void setParameterValue(uint32_t index, float value) = 0; | |||
virtual void setParameterValue(uint32_t index, float value); | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
/** | |||
@@ -806,7 +1126,7 @@ protected: | |||
The host may call this function from any context, including realtime processing.@n | |||
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_PROGRAMS is enabled. | |||
*/ | |||
virtual void loadProgram(uint32_t index) = 0; | |||
virtual void loadProgram(uint32_t index); | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
@@ -816,7 +1136,7 @@ protected: | |||
Must be implemented by your plugin class if DISTRHO_PLUGIN_WANT_FULL_STATE is enabled. | |||
@note The use of this function breaks compatibility with the DSSI format. | |||
*/ | |||
virtual String getState(const char* key) const = 0; | |||
virtual String getState(const char* key) const; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
@@ -824,7 +1144,7 @@ protected: | |||
Change an internal state @a key to @a value.@n | |||
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. | |||
*/ | |||
virtual void setState(const char* key, const char* value) = 0; | |||
virtual void setState(const char* key, const char* value); | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
@@ -845,14 +1165,14 @@ protected: | |||
Run/process function for plugins with MIDI input. | |||
@note Some parameters might be null if there are no audio inputs/outputs or MIDI events. | |||
*/ | |||
virtual void run(const float* const* inputs, float** outputs, uint32_t frames, | |||
virtual void run(const float** inputs, float** outputs, uint32_t frames, | |||
const MidiEvent* midiEvents, uint32_t midiEventCount) = 0; | |||
#else | |||
/** | |||
Run/process function for plugins without MIDI input. | |||
@note Some parameters might be null if there are no audio inputs or outputs. | |||
*/ | |||
virtual void run(const float* const* inputs, float** outputs, uint32_t frames) = 0; | |||
virtual void run(const float** inputs, float** outputs, uint32_t frames) = 0; | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
@@ -895,7 +1215,10 @@ private: | |||
*/ | |||
/** | |||
TODO. | |||
Create an instance of the Plugin class.@n | |||
This is the entry point for DPF plugins.@n | |||
DPF will call this to either create an instance of your plugin for the host | |||
or to fetch some initial information for internal caching. | |||
*/ | |||
extern Plugin* createPlugin(); | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,14 +19,30 @@ | |||
#if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
# include "src/DistrhoPluginCarla.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
# include "src/DistrhoPluginJack.cpp" | |||
# include "src/DistrhoPluginJACK.cpp" | |||
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) | |||
# include "src/DistrhoPluginLADSPA+DSSI.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
# include "src/DistrhoPluginLV2.cpp" | |||
# include "src/DistrhoPluginLV2export.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST) | |||
# include "src/DistrhoPluginVST.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
# include "src/DistrhoPluginVST2.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
# include "src/DistrhoPluginVST3.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) | |||
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); | |||
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } | |||
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) | |||
START_NAMESPACE_DISTRHO | |||
Plugin* createStaticPlugin() { return createPlugin(); } | |||
END_NAMESPACE_DISTRHO | |||
#else | |||
# error unsupported format | |||
#endif | |||
#if defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
# define DISTRHO_IS_STANDALONE 1 | |||
#else | |||
# define DISTRHO_IS_STANDALONE 0 | |||
#endif | |||
#include "src/DistrhoUtils.cpp" |
@@ -0,0 +1,215 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DISTRHO_PLUGIN_UTILS_HPP_INCLUDED | |||
#define DISTRHO_PLUGIN_UTILS_HPP_INCLUDED | |||
#include "DistrhoPlugin.hpp" | |||
START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Plugin related utilities */ | |||
/** | |||
@defgroup PluginRelatedUtilities Plugin related utilities | |||
@{ | |||
*/ | |||
/** | |||
Get the absolute filename of the plugin DSP/UI binary.@n | |||
Under certain systems or plugin formats the binary will be inside the plugin bundle.@n | |||
Also, in some formats or setups, the DSP and UI binaries are in different files. | |||
*/ | |||
const char* getBinaryFilename(); | |||
/** | |||
Get a string representation of the current plugin format we are building against.@n | |||
This can be "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3".@n | |||
This string is purely informational and must not be used to tweak plugin behaviour. | |||
@note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. | |||
*/ | |||
const char* getPluginFormatName() noexcept; | |||
/** | |||
Get the path to where resources are stored within the plugin bundle.@n | |||
Requires a valid plugin bundle path. | |||
Returns a path inside the bundle where the plugin is meant to store its resources in.@n | |||
This path varies between systems and plugin formats, like so: | |||
- LV2: <bundle>/resources (can be stored anywhere inside the bundle really, DPF just uses this one) | |||
- VST2 macOS: <bundle>/Contents/Resources | |||
- VST2 non-macOS: <bundle>/resources (see note) | |||
The other non-mentioned formats do not support bundles.@n | |||
@note For VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory | |||
rather than only shipping with the binary (e.g. <myplugin.vst>/myplugin.dll) | |||
*/ | |||
const char* getResourcePath(const char* bundlePath) noexcept; | |||
/** @} */ | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Plugin helper classes */ | |||
/** | |||
@defgroup PluginHelperClasses Plugin helper classes | |||
@{ | |||
*/ | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
/** | |||
Handy class to help keep audio buffer in sync with incoming MIDI events. | |||
To use it, create a local variable (on the stack) and call nextEvent() until it returns false. | |||
@code | |||
for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();) | |||
{ | |||
float* const outL = amsh.outputs[0]; | |||
float* const outR = amsh.outputs[1]; | |||
for (uint32_t i=0; i<amsh.midiEventCount; ++i) | |||
{ | |||
const MidiEvent& ev(amsh.midiEvents[i]); | |||
// ... do something with the midi event | |||
} | |||
renderSynth(outL, outR, amsh.frames); | |||
} | |||
@endcode | |||
Some important notes when using this class: | |||
1. MidiEvent::frame retains its original value, but it is useless, do not use it. | |||
2. The class variable names are the same as the default ones in the run function. | |||
Keep that in mind and try to avoid typos. :) | |||
*/ | |||
struct AudioMidiSyncHelper | |||
{ | |||
/** Parameters from the run function, adjusted for event sync */ | |||
float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
uint32_t frames; | |||
const MidiEvent* midiEvents; | |||
uint32_t midiEventCount; | |||
/** | |||
Constructor, using values from the run function. | |||
*/ | |||
AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc) | |||
: outputs(), | |||
frames(0), | |||
midiEvents(m), | |||
midiEventCount(0), | |||
remainingFrames(f), | |||
remainingMidiEventCount(mc), | |||
totalFramesUsed(0) | |||
{ | |||
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
outputs[i] = o[i]; | |||
} | |||
/** | |||
Process a batch of events untill no more are available. | |||
You must not read any more values from this class after this function returns false. | |||
*/ | |||
bool nextEvent() | |||
{ | |||
// nothing else to do | |||
if (remainingFrames == 0) | |||
return false; | |||
// initial setup, need to find first MIDI event | |||
if (totalFramesUsed == 0) | |||
{ | |||
// no MIDI events at all in this process cycle | |||
if (remainingMidiEventCount == 0) | |||
{ | |||
frames = remainingFrames; | |||
remainingFrames = 0; | |||
totalFramesUsed += frames; | |||
return true; | |||
} | |||
// render audio until first midi event, if needed | |||
if (const uint32_t firstEventFrame = midiEvents[0].frame) | |||
{ | |||
DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame < remainingFrames, | |||
firstEventFrame, remainingFrames, false); | |||
frames = firstEventFrame; | |||
remainingFrames -= firstEventFrame; | |||
totalFramesUsed += firstEventFrame; | |||
return true; | |||
} | |||
} | |||
else | |||
{ | |||
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
outputs[i] += frames; | |||
} | |||
// no more MIDI events available | |||
if (remainingMidiEventCount == 0) | |||
{ | |||
frames = remainingFrames; | |||
midiEvents = nullptr; | |||
midiEventCount = 0; | |||
remainingFrames = 0; | |||
totalFramesUsed += frames; | |||
return true; | |||
} | |||
// if there were midi events before, increment pointer | |||
if (midiEventCount != 0) | |||
midiEvents += midiEventCount; | |||
const uint32_t firstEventFrame = midiEvents[0].frame; | |||
DISTRHO_SAFE_ASSERT_UINT2_RETURN(firstEventFrame >= totalFramesUsed, | |||
firstEventFrame, totalFramesUsed, false); | |||
midiEventCount = 1; | |||
while (midiEventCount < remainingMidiEventCount) | |||
{ | |||
if (midiEvents[midiEventCount].frame == firstEventFrame) | |||
++midiEventCount; | |||
else | |||
break; | |||
} | |||
frames = firstEventFrame - totalFramesUsed; | |||
remainingFrames -= frames; | |||
remainingMidiEventCount -= midiEventCount; | |||
totalFramesUsed += frames; | |||
return true; | |||
} | |||
private: | |||
/** @internal */ | |||
uint32_t remainingFrames; | |||
uint32_t remainingMidiEventCount; | |||
uint32_t totalFramesUsed; | |||
}; | |||
#endif | |||
/** @} */ | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_PLUGIN_UTILS_HPP_INCLUDED |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,17 +20,42 @@ | |||
#include "extra/LeakDetector.hpp" | |||
#include "src/DistrhoPluginChecks.h" | |||
#ifndef HAVE_DGL | |||
#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" | |||
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::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 | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# include "extra/FileBrowserDialog.hpp" | |||
#endif | |||
START_NAMESPACE_DGL | |||
class PluginWindow; | |||
END_NAMESPACE_DGL | |||
START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
@@ -53,8 +78,13 @@ public: | |||
/** | |||
UI class constructor. | |||
The UI should be initialized to a default state that matches the plugin side. | |||
When @a automaticallyScale is set to true, DPF will automatically scale up the UI | |||
to fit the host/desktop scale factor.@n | |||
It assumes aspect ratio is meant to be kept. | |||
Manually call setGeometryConstraints instead if keeping UI aspect ratio is not required. | |||
*/ | |||
UI(uint width = 0, uint height = 0); | |||
UI(uint width = 0, uint height = 0, bool automaticallyScaleAndSetAsMinimumSize = false); | |||
/** | |||
Destructor. | |||
@@ -64,21 +94,67 @@ public: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Host state */ | |||
/** | |||
Check if this UI window is resizable (by the user or window manager). | |||
There are situations where an UI supports resizing but the plugin host does not, so this could return false. | |||
You might want to add a resize handle for such cases, so the user is still allowed to resize the window. | |||
(programatically resizing a window is always possible, but the same is not true for the window manager) | |||
*/ | |||
bool isResizable() const noexcept; | |||
/** | |||
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; | |||
/** | |||
Get the current sample rate used in plugin processing. | |||
@see sampleRateChanged(double) | |||
*/ | |||
double getSampleRate() const noexcept; | |||
/** | |||
Get the bundle path where the UI resides.@n | |||
Can return null if the UI is not available in a bundle (if it is a single binary). | |||
@see getBinaryFilename | |||
*/ | |||
const char* getBundlePath() const noexcept; | |||
/** | |||
editParameter. | |||
@TODO Document this. | |||
Touch/pressed-down event. | |||
Lets the host know the user is tweaking a parameter. | |||
Required in some hosts to record automation. | |||
*/ | |||
void editParameter(uint32_t index, bool started); | |||
/** | |||
setParameterValue. | |||
@TODO Document this. | |||
Change a parameter value in the Plugin. | |||
*/ | |||
void setParameterValue(uint32_t index, float value); | |||
@@ -88,17 +164,43 @@ public: | |||
@TODO Document this. | |||
*/ | |||
void setState(const char* key, const char* value); | |||
/** | |||
Request a new file from the host, matching the properties of a state key.@n | |||
This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n | |||
Response will be sent asynchronously to stateChanged, with the matching key and the new file as the value.@n | |||
It is not possible to know if the action was cancelled by the user. | |||
@return Success if a file-browser was opened, otherwise false. | |||
@note You cannot request more than one file at a time. | |||
*/ | |||
bool requestStateFile(const char* key); | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
/** | |||
sendNote. | |||
@TODO Document this. | |||
@note Work in progress. Implemented for DSSI and LV2 formats. | |||
Send a single MIDI note from the UI to the plugin DSP side.@n | |||
A note with zero velocity will be sent as note-off (MIDI 0x80), otherwise note-on (MIDI 0x90). | |||
*/ | |||
void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | |||
#endif | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** | |||
Open a file browser dialog with this window as transient parent.@n | |||
A few options can be specified to setup the dialog. | |||
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. | |||
@note This is exactly the same API as provided by the Window class, | |||
but redeclared here so that non-embed/DGL based UIs can still use file browser related functions. | |||
*/ | |||
bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */ | |||
@@ -121,6 +223,13 @@ public: | |||
*/ | |||
static const char* getNextBundlePath() noexcept; | |||
/** | |||
Get the scale factor that will be used for the next UI. | |||
@note: This function is only valid during createUI(), | |||
it will return 1.0 when called from anywhere else. | |||
*/ | |||
static double getNextScaleFactor() noexcept; | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
/** | |||
Get the Window Id that will be used for the next created window. | |||
@@ -166,36 +275,74 @@ protected: | |||
*/ | |||
virtual void sampleRateChanged(double newSampleRate); | |||
#ifdef HAVE_DGL | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* UI Callbacks (optional) */ | |||
/** | |||
uiIdle. | |||
@TODO Document this. | |||
UI idle function, called to give idle time to the plugin UI directly from the host. | |||
This is called right after OS event handling and Window idle events (within the same cycle). | |||
There are no guarantees in terms of timing. | |||
@see addIdleCallback(IdleCallback*, uint). | |||
*/ | |||
virtual void uiIdle() {} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** | |||
File browser selected function. | |||
@see Window::fileBrowserSelected(const char*) | |||
Window scale factor function, called when the scale factor changes. | |||
This function is for plugin UIs to be able to override Window::onScaleFactorChanged(double). | |||
The default implementation does nothing. | |||
WARNING function needs a proper name | |||
*/ | |||
virtual void uiFileBrowserSelected(const char* filename); | |||
#endif | |||
virtual void uiScaleFactorChanged(double scaleFactor); | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/** | |||
Windows focus function, called when the window gains or loses the keyboard focus. | |||
This function is for plugin UIs to be able to override Window::onFocus(bool, CrossingMode). | |||
The default implementation does nothing. | |||
*/ | |||
virtual void uiFocus(bool focus, DGL_NAMESPACE::CrossingMode mode); | |||
/** | |||
OpenGL window reshape function, called when parent window is resized. | |||
You can reimplement this function for a custom OpenGL state. | |||
@see Window::onReshape(uint,uint) | |||
Window reshape function, called when the window is resized. | |||
This function is for plugin UIs to be able to override Window::onReshape(uint, uint). | |||
The plugin UI size will be set right after this function. | |||
The default implementation sets up the drawing context where necessary. | |||
You should almost never need to override this function. | |||
The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code. | |||
*/ | |||
virtual void uiReshape(uint width, uint height); | |||
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
/** | |||
Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). | |||
This function is for plugin UIs to be able to override Window::onFileSelected(const char*). | |||
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. | |||
If you need to use files as plugin state, please setup and use states with kStateIsFilenamePath instead. | |||
*/ | |||
virtual void uiFileBrowserSelected(const char* filename); | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* UI Resize Handling, internal */ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/** | |||
External Window resize function, called when the window is resized. | |||
This is overriden here so the host knows when the UI is resized by you. | |||
@see ExternalWindow::sizeChanged(uint,uint) | |||
*/ | |||
void sizeChanged(uint width, uint height) override; | |||
#else | |||
/** | |||
OpenGL widget resize function, called when the widget is resized. | |||
Widget resize function, called when the widget is resized. | |||
This is overriden here so the host knows when the UI is resized by you. | |||
@see Widget::onResize(const ResizeEvent&) | |||
*/ | |||
@@ -206,16 +353,12 @@ protected: | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
PrivateData* const uiData; | |||
friend class DGL_NAMESPACE::PluginWindow; | |||
friend class UIExporter; | |||
friend class UIExporterWindow; | |||
#ifdef HAVE_DGL | |||
// 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<int>&) const noexcept {} | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/** @internal */ | |||
void requestSizeChange(uint width, uint height) override; | |||
#endif | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -24,8 +24,21 @@ | |||
# include "src/DistrhoUIDSSI.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
# include "src/DistrhoUILV2.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST) | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
// nothing | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
# include "src/DistrhoUIVST3.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC) | |||
// nothing | |||
#else | |||
# error unsupported format | |||
#endif | |||
#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !DISTRHO_PLUGIN_TARGET_JACK && !DISTRHO_PLUGIN_TARGET_VST2 && !DISTRHO_PLUGIN_TARGET_VST3 | |||
# ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
# define DISTRHO_IS_STANDALONE 1 | |||
# else | |||
# define DISTRHO_IS_STANDALONE 0 | |||
# endif | |||
# include "src/DistrhoUtils.cpp" | |||
#endif |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -25,10 +25,6 @@ | |||
#include <cstring> | |||
#include <cmath> | |||
#undef max | |||
#undef min | |||
#include <limits> | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
@@ -37,7 +33,12 @@ | |||
# include <stdint.h> | |||
#endif | |||
#if defined(DISTRHO_OS_MAC) && ! defined(CARLA_OS_MAC) && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) | |||
#if defined(DISTRHO_OS_WINDOWS) && defined(_MSC_VER) | |||
#include <basetsd.h> | |||
typedef SSIZE_T ssize_t; | |||
#endif | |||
#if ! defined(CARLA_MATH_UTILS_HPP_INCLUDED) && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) | |||
namespace std { | |||
inline float fmin(float __x, float __y) | |||
{ return __builtin_fminf(__x, __y); } | |||
@@ -54,39 +55,57 @@ inline float round(float __x) | |||
# define M_PI 3.14159265358979323846 | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// misc functions | |||
#define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO | |||
#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) | |||
/* | |||
* Return a 64-bit number from 4 8-bit numbers. | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* misc functions */ | |||
/** | |||
@defgroup MiscellaneousFunctions Miscellaneous functions | |||
@{ | |||
*/ | |||
static inline | |||
/** | |||
Return a 32-bit number from 4 8-bit numbers.@n | |||
The return type is a int64_t for better compatibility with plugin formats that use such numbers. | |||
*/ | |||
static inline constexpr | |||
int64_t d_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) noexcept | |||
{ | |||
return (a << 24) | (b << 16) | (c << 8) | (d << 0); | |||
} | |||
/* | |||
* Return an hexadecimal representation of a MAJ.MIN.MICRO version number. | |||
/** | |||
Return an hexadecimal representation of a MAJ.MIN.MICRO version number. | |||
*/ | |||
static inline | |||
static inline constexpr | |||
uint32_t d_version(const uint8_t major, const uint8_t minor, const uint8_t micro) noexcept | |||
{ | |||
return uint32_t(major << 16) | uint32_t(minor << 8) | uint32_t(micro << 0); | |||
return uint32_t(major << 16) | uint32_t(minor << 8) | (micro << 0); | |||
} | |||
/* | |||
* Dummy function. | |||
/** | |||
Dummy, no-op function. | |||
*/ | |||
static inline | |||
void d_pass() noexcept {} | |||
// ----------------------------------------------------------------------- | |||
// string print functions | |||
/** @} */ | |||
/* | |||
* Print a string to stdout with newline (gray color). | |||
* Does nothing if DEBUG is not defined. | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* string print functions */ | |||
/** | |||
@defgroup StringPrintFunctions String print functions | |||
@{ | |||
*/ | |||
/** | |||
Print a string to stdout with newline (gray color). | |||
Does nothing if DEBUG is not defined. | |||
*/ | |||
#ifndef DEBUG | |||
# define d_debug(...) | |||
@@ -95,64 +114,64 @@ static inline | |||
void d_debug(const char* const fmt, ...) noexcept | |||
{ | |||
try { | |||
::va_list args; | |||
::va_start(args, fmt); | |||
va_list args; | |||
va_start(args, fmt); | |||
std::fprintf(stdout, "\x1b[30;1m"); | |||
std::vfprintf(stdout, fmt, args); | |||
std::fprintf(stdout, "\x1b[0m\n"); | |||
::va_end(args); | |||
va_end(args); | |||
} catch (...) {} | |||
} | |||
#endif | |||
/* | |||
* Print a string to stdout with newline. | |||
/** | |||
Print a string to stdout with newline. | |||
*/ | |||
static inline | |||
void d_stdout(const char* const fmt, ...) noexcept | |||
{ | |||
try { | |||
::va_list args; | |||
::va_start(args, fmt); | |||
va_list args; | |||
va_start(args, fmt); | |||
std::vfprintf(stdout, fmt, args); | |||
std::fprintf(stdout, "\n"); | |||
::va_end(args); | |||
va_end(args); | |||
} catch (...) {} | |||
} | |||
/* | |||
* Print a string to stderr with newline. | |||
/** | |||
Print a string to stderr with newline. | |||
*/ | |||
static inline | |||
void d_stderr(const char* const fmt, ...) noexcept | |||
{ | |||
try { | |||
::va_list args; | |||
::va_start(args, fmt); | |||
va_list args; | |||
va_start(args, fmt); | |||
std::vfprintf(stderr, fmt, args); | |||
std::fprintf(stderr, "\n"); | |||
::va_end(args); | |||
va_end(args); | |||
} catch (...) {} | |||
} | |||
/* | |||
* Print a string to stderr with newline (red color). | |||
/** | |||
Print a string to stderr with newline (red color). | |||
*/ | |||
static inline | |||
void d_stderr2(const char* const fmt, ...) noexcept | |||
{ | |||
try { | |||
::va_list args; | |||
::va_start(args, fmt); | |||
va_list args; | |||
va_start(args, fmt); | |||
std::fprintf(stderr, "\x1b[31m"); | |||
std::vfprintf(stderr, fmt, args); | |||
std::fprintf(stderr, "\x1b[0m\n"); | |||
::va_end(args); | |||
va_end(args); | |||
} catch (...) {} | |||
} | |||
/* | |||
* Print a safe assertion error message. | |||
/** | |||
Print a safe assertion error message. | |||
*/ | |||
static inline | |||
void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept | |||
@@ -160,8 +179,58 @@ void d_safe_assert(const char* const assertion, const char* const file, const in | |||
d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line); | |||
} | |||
/* | |||
* Print a safe exception error message. | |||
/** | |||
Print a safe assertion error message, with 1 extra signed integer value. | |||
*/ | |||
static inline | |||
void d_safe_assert_int(const char* const assertion, const char* const file, | |||
const int line, const int value) noexcept | |||
{ | |||
d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %i", assertion, file, line, value); | |||
} | |||
/** | |||
Print a safe assertion error message, with 1 extra unsigned integer value. | |||
*/ | |||
static inline | |||
void d_safe_assert_uint(const char* const assertion, const char* const file, | |||
const int line, const uint value) noexcept | |||
{ | |||
d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %u", assertion, file, line, value); | |||
} | |||
/** | |||
Print a safe assertion error message, with 2 extra signed integer values. | |||
*/ | |||
static inline | |||
void d_safe_assert_int2(const char* const assertion, const char* const file, | |||
const int line, const int v1, const int v2) noexcept | |||
{ | |||
d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %i, v2 %i", assertion, file, line, v1, v2); | |||
} | |||
/** | |||
Print a safe assertion error message, with 2 extra unsigned integer values. | |||
*/ | |||
static inline | |||
void d_safe_assert_uint2(const char* const assertion, const char* const file, | |||
const int line, const uint v1, const uint v2) noexcept | |||
{ | |||
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. | |||
*/ | |||
static inline | |||
void d_safe_exception(const char* const exception, const char* const file, const int line) noexcept | |||
@@ -169,12 +238,20 @@ void d_safe_exception(const char* const exception, const char* const file, const | |||
d_stderr2("exception caught: \"%s\" in file %s, line %i", exception, file, line); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// math functions | |||
/** @} */ | |||
/* | |||
* Safely compare two floating point numbers. | |||
* Returns true if they match. | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* math functions */ | |||
/** | |||
@defgroup MathFunctions Math related functions | |||
@{ | |||
*/ | |||
/** | |||
Safely compare two floating point numbers. | |||
Returns true if they match. | |||
*/ | |||
template<typename T> | |||
static inline | |||
@@ -183,9 +260,9 @@ bool d_isEqual(const T& v1, const T& v2) | |||
return std::abs(v1-v2) < std::numeric_limits<T>::epsilon(); | |||
} | |||
/* | |||
* Safely compare two floating point numbers. | |||
* Returns true if they don't match. | |||
/** | |||
Safely compare two floating point numbers. | |||
Returns true if they don't match. | |||
*/ | |||
template<typename T> | |||
static inline | |||
@@ -194,8 +271,8 @@ bool d_isNotEqual(const T& v1, const T& v2) | |||
return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon(); | |||
} | |||
/* | |||
* Safely check if a floating point number is zero. | |||
/** | |||
Safely check if a floating point number is zero. | |||
*/ | |||
template<typename T> | |||
static inline | |||
@@ -204,8 +281,8 @@ bool d_isZero(const T& value) | |||
return std::abs(value) < std::numeric_limits<T>::epsilon(); | |||
} | |||
/* | |||
* Safely check if a floating point number is not zero. | |||
/** | |||
Safely check if a floating point number is not zero. | |||
*/ | |||
template<typename T> | |||
static inline | |||
@@ -214,8 +291,8 @@ bool d_isNotZero(const T& value) | |||
return std::abs(value) >= std::numeric_limits<T>::epsilon(); | |||
} | |||
/* | |||
* Get next power of 2. | |||
/** | |||
Get next power of 2. | |||
*/ | |||
static inline | |||
uint32_t d_nextPowerOf2(uint32_t size) noexcept | |||
@@ -232,6 +309,8 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept | |||
return ++size; | |||
} | |||
/** @} */ | |||
// ----------------------------------------------------------------------- | |||
#ifndef DONT_SET_USING_DISTRHO_NAMESPACE | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,9 +19,7 @@ | |||
#include "String.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# error Unsupported platform! | |||
#else | |||
#ifndef DISTRHO_OS_WINDOWS | |||
# include <cerrno> | |||
# include <signal.h> | |||
# include <sys/wait.h> | |||
@@ -33,136 +31,544 @@ START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// ExternalWindow class | |||
/** | |||
External Window class. | |||
This is a standalone TopLevelWidget/Window-compatible class, but without any real event handling. | |||
Being compatible with TopLevelWidget/Window, it allows to be used as DPF UI target. | |||
It can be used to embed non-DPF things or to run a tool in a new process as the "UI". | |||
The uiIdle() function will be called at regular intervals to keep UI running. | |||
There are helper methods in place to launch external tools and keep track of its running state. | |||
External windows can be setup to run in 3 different modes: | |||
* Embed: | |||
Embed into the host UI, even-loop driven by the host. | |||
This is basically working as a regular plugin UI, as you typically expect them to. | |||
The plugin side does not get control over showing, hiding or closing the window (as usual for plugins). | |||
No restrictions on supported plugin format, everything should work. | |||
Requires DISTRHO_PLUGIN_HAS_EMBED_UI to be set to 1. | |||
* Semi-external: | |||
The UI is not embed into the host, but the even-loop is still driven by it. | |||
In this mode the host does not have control over the UI except for showing, hiding and setting transient parent. | |||
It is possible to close the window from the plugin, the host will be notified of such case. | |||
Host regularly calls isQuitting() to check if the UI got closed by the user or plugin side. | |||
This mode is only possible in LV2 plugin formats, using lv2ui:showInterface extension. | |||
* Standalone: | |||
The UI is not embed into the host or uses its event-loop, basically running as standalone. | |||
The host only has control over showing and hiding the window, nothing else. | |||
The UI is still free to close itself at any point. | |||
DPF will keep calling isRunning() to check if it should keep the event-loop running. | |||
Only possible in JACK and DSSI targets, as the UIs are literally standalone applications there. | |||
Please note that for non-embed windows, you cannot show the window yourself. | |||
The plugin window is only allowed to hide or close itself, a "show" action needs to come from the host. | |||
A few callbacks are provided so that implementations do not need to care about checking for state changes. | |||
They are not called on construction, but will be everytime something changes either by the host or the window itself. | |||
*/ | |||
class ExternalWindow | |||
{ | |||
public: | |||
ExternalWindow(const uint w = 1, const uint h = 1, const char* const t = "") | |||
: width(w), | |||
height(h), | |||
title(t), | |||
pid(0) {} | |||
struct PrivateData; | |||
public: | |||
/** | |||
Constructor. | |||
*/ | |||
explicit ExternalWindow() | |||
: pData() {} | |||
/** | |||
Constructor for DPF internal use. | |||
*/ | |||
explicit ExternalWindow(const PrivateData& data) | |||
: pData(data) {} | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~ExternalWindow() | |||
{ | |||
terminateAndWaitForProcess(); | |||
DISTRHO_SAFE_ASSERT(!pData.visible); | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow specific calls - Host side calls that you can reimplement for fine-grained funtionality */ | |||
/** | |||
Check if main-loop is running. | |||
This is used under standalone mode to check whether to keep things running. | |||
Returning false from this function will stop the event-loop and close the window. | |||
*/ | |||
virtual bool isRunning() const | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
if (ext.inUse) | |||
return ext.isRunning(); | |||
#endif | |||
return isVisible(); | |||
} | |||
/** | |||
Check if we are about to close. | |||
This is used when the event-loop is provided by the host to check if it should close the window. | |||
It is also used in standalone mode right after isRunning() returns false to verify if window needs to be closed. | |||
*/ | |||
virtual bool isQuitting() const | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
return ext.inUse ? ext.isQuitting : pData.isQuitting; | |||
#else | |||
return pData.isQuitting; | |||
#endif | |||
} | |||
/** | |||
Get the "native" window handle. | |||
This can be reimplemented in order to pass the native window to hosts that can use such informaton. | |||
Returned value type 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`. | |||
@note Only available to override if DISTRHO_PLUGIN_HAS_EMBED_UI is set to 1. | |||
*/ | |||
virtual uintptr_t getNativeWindowHandle() const noexcept | |||
{ | |||
return 0; | |||
} | |||
/** | |||
Grab the keyboard input focus. | |||
Typically you would setup OS-native methods to bring the window to front and give it focus. | |||
Default implementation does nothing. | |||
*/ | |||
virtual void focus() {} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - Information, can be called by either host or plugin */ | |||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
/** | |||
Whether this Window is embed into another (usually not DGL-controlled) Window. | |||
*/ | |||
bool isEmbed() const noexcept | |||
{ | |||
return pData.parentWindowHandle != 0; | |||
} | |||
#endif | |||
/** | |||
Check if this window is visible. | |||
@see setVisible(bool) | |||
*/ | |||
bool isVisible() const noexcept | |||
{ | |||
return pData.visible; | |||
} | |||
/** | |||
Whether this Window is running as standalone, that is, without being coupled to a host event-loop. | |||
When in standalone mode, isRunning() is called to check if the event-loop should keep running. | |||
*/ | |||
bool isStandalone() const noexcept | |||
{ | |||
return pData.isStandalone; | |||
} | |||
/** | |||
Get width of this window. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
uint getWidth() const noexcept | |||
{ | |||
return width; | |||
return pData.width; | |||
} | |||
/** | |||
Get height of this window. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
uint getHeight() const noexcept | |||
{ | |||
return height; | |||
return pData.height; | |||
} | |||
/** | |||
Get the scale factor requested for this window. | |||
This is purely informational, and up to developers to choose what to do with it. | |||
*/ | |||
double getScaleFactor() const noexcept | |||
{ | |||
return pData.scaleFactor; | |||
} | |||
/** | |||
Get the title of the window previously set with setTitle(). | |||
This is typically displayed in the title bar or in window switchers. | |||
*/ | |||
const char* getTitle() const noexcept | |||
{ | |||
return title; | |||
return pData.title; | |||
} | |||
void setTitle(const char* const t) noexcept | |||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
/** | |||
Get the "native" window handle that this window should embed itself into. | |||
Returned value type 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 getParentWindowHandle() const noexcept | |||
{ | |||
title = t; | |||
return pData.parentWindowHandle; | |||
} | |||
#endif | |||
bool isRunning() noexcept | |||
/** | |||
Get the transient window that we should attach ourselves to. | |||
TODO what id? also NSView* on macOS, or NSWindow? | |||
*/ | |||
uintptr_t getTransientWindowId() const noexcept | |||
{ | |||
if (pid <= 0) | |||
return false; | |||
return pData.transientWinId; | |||
} | |||
const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - actions called by either host or plugin */ | |||
if (p == pid || (p == -1 && errno == ECHILD)) | |||
{ | |||
printf("NOTICE: Child process exited while idle\n"); | |||
pid = 0; | |||
return false; | |||
} | |||
/** | |||
Hide window. | |||
This is the same as calling setVisible(false). | |||
Embed windows should never call this! | |||
@see isVisible(), setVisible(bool) | |||
*/ | |||
void hide() | |||
{ | |||
setVisible(false); | |||
} | |||
/** | |||
Hide the UI and gracefully terminate. | |||
Embed windows should never call this! | |||
*/ | |||
virtual void close() | |||
{ | |||
pData.isQuitting = true; | |||
hide(); | |||
#ifndef DISTRHO_OS_WINDOWS | |||
if (ext.inUse) | |||
terminateAndWaitForExternalProcess(); | |||
#endif | |||
} | |||
/** | |||
Set width of this window. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setWidth(uint width) | |||
{ | |||
setSize(width, getHeight()); | |||
} | |||
return true; | |||
/** | |||
Set height of this window. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setHeight(uint height) | |||
{ | |||
setSize(getWidth(), height); | |||
} | |||
/** | |||
Set size of this window using @a width and @a height values. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setSize(uint width, uint height) | |||
{ | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(width > 1, width,); | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(height > 1, height,); | |||
if (pData.width == width && pData.height == height) | |||
return; | |||
pData.width = width; | |||
pData.height = height; | |||
sizeChanged(width, height); | |||
} | |||
/** | |||
Set the title of the window, typically displayed in the title bar or in window switchers. | |||
Can trigger a titleChanged callback. | |||
Only relevant to hosts when the UI is not embedded. | |||
*/ | |||
void setTitle(const char* title) | |||
{ | |||
if (pData.title == title) | |||
return; | |||
pData.title = title; | |||
titleChanged(title); | |||
} | |||
/** | |||
Set geometry constraints for the Window when resized by the user. | |||
*/ | |||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumWidth > 0, minimumWidth,); | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumHeight > 0, minimumHeight,); | |||
pData.minWidth = minimumWidth; | |||
pData.minHeight = minimumHeight; | |||
pData.keepAspectRatio = keepAspectRatio; | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - actions called by the host */ | |||
/** | |||
Show window. | |||
This is the same as calling setVisible(true). | |||
@see isVisible(), setVisible(bool) | |||
*/ | |||
void show() | |||
{ | |||
setVisible(true); | |||
} | |||
/** | |||
Set window visible (or not) according to @a visible. | |||
@see isVisible(), hide(), show() | |||
*/ | |||
void setVisible(bool visible) | |||
{ | |||
if (pData.visible == visible) | |||
return; | |||
pData.visible = visible; | |||
visibilityChanged(visible); | |||
} | |||
/** | |||
Called by the host to set the transient parent window that we should attach ourselves to. | |||
TODO what id? also NSView* on macOS, or NSWindow? | |||
*/ | |||
void setTransientWindowId(uintptr_t winId) | |||
{ | |||
if (pData.transientWinId == winId) | |||
return; | |||
pData.transientWinId = winId; | |||
transientParentWindowChanged(winId); | |||
} | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow special calls for running externals tools */ | |||
bool startExternalProcess(const char* args[]) | |||
{ | |||
terminateAndWaitForProcess(); | |||
#ifndef DISTRHO_OS_WINDOWS | |||
ext.inUse = true; | |||
pid = vfork(); | |||
return ext.start(args); | |||
#else | |||
(void)args; | |||
return false; // TODO | |||
#endif | |||
} | |||
switch (pid) | |||
{ | |||
case 0: | |||
execvp(args[0], (char**)args); | |||
_exit(1); | |||
return false; | |||
void terminateAndWaitForExternalProcess() | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
ext.isQuitting = true; | |||
ext.terminateAndWait(); | |||
#else | |||
// TODO | |||
#endif | |||
} | |||
case -1: | |||
printf("Could not start external ui\n"); | |||
return false; | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow specific callbacks */ | |||
default: | |||
return true; | |||
} | |||
/** | |||
A callback for when the window size changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void sizeChanged(uint /* width */, uint /* height */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
private: | |||
uint width; | |||
uint height; | |||
String title; | |||
pid_t pid; | |||
/** | |||
A callback for when the window title changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void titleChanged(const char* /* title */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
friend class UIExporter; | |||
/** | |||
A callback for when the window visibility changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void visibilityChanged(bool /* visible */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
void terminateAndWaitForProcess() | |||
/** | |||
A callback for when the transient parent window changes. | |||
*/ | |||
virtual void transientParentWindowChanged(uintptr_t /* winId */) | |||
{ | |||
if (pid <= 0) | |||
return; | |||
// unused, meant for custom implementations | |||
} | |||
private: | |||
friend class PluginWindow; | |||
friend class UI; | |||
printf("Waiting for previous process to stop,,,\n"); | |||
#ifndef DISTRHO_OS_WINDOWS | |||
struct ExternalProcess { | |||
bool inUse; | |||
bool isQuitting; | |||
mutable pid_t pid; | |||
bool sendTerm = true; | |||
ExternalProcess() | |||
: inUse(false), | |||
isQuitting(false), | |||
pid(0) {} | |||
for (pid_t p;;) | |||
bool isRunning() const noexcept | |||
{ | |||
p = ::waitpid(pid, nullptr, WNOHANG); | |||
if (pid <= 0) | |||
return false; | |||
const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||
switch (p) | |||
if (p == pid || (p == -1 && errno == ECHILD)) | |||
{ | |||
d_stdout("NOTICE: Child process exited while idle"); | |||
pid = 0; | |||
return false; | |||
} | |||
return true; | |||
} | |||
bool start(const char* args[]) | |||
{ | |||
terminateAndWait(); | |||
pid = vfork(); | |||
switch (pid) | |||
{ | |||
case 0: | |||
if (sendTerm) | |||
{ | |||
sendTerm = false; | |||
::kill(pid, SIGTERM); | |||
} | |||
break; | |||
execvp(args[0], (char**)args); | |||
_exit(1); | |||
return false; | |||
case -1: | |||
if (errno == ECHILD) | |||
{ | |||
printf("Done! (no such process)\n"); | |||
pid = 0; | |||
return; | |||
} | |||
break; | |||
d_stderr("Could not start external ui"); | |||
return false; | |||
default: | |||
if (p == pid) | |||
return true; | |||
} | |||
} | |||
void terminateAndWait() | |||
{ | |||
if (pid <= 0) | |||
return; | |||
d_stdout("Waiting for external process to stop,,,"); | |||
bool sendTerm = true; | |||
for (pid_t p;;) | |||
{ | |||
p = ::waitpid(pid, nullptr, WNOHANG); | |||
switch (p) | |||
{ | |||
printf("Done! (clean wait)\n"); | |||
pid = 0; | |||
return; | |||
case 0: | |||
if (sendTerm) | |||
{ | |||
sendTerm = false; | |||
::kill(pid, SIGTERM); | |||
} | |||
break; | |||
case -1: | |||
if (errno == ECHILD) | |||
{ | |||
d_stdout("Done! (no such process)"); | |||
pid = 0; | |||
return; | |||
} | |||
break; | |||
default: | |||
if (p == pid) | |||
{ | |||
d_stdout("Done! (clean wait)"); | |||
pid = 0; | |||
return; | |||
} | |||
break; | |||
} | |||
break; | |||
} | |||
// 5 msec | |||
usleep(5*1000); | |||
// 5 msec | |||
usleep(5*1000); | |||
} | |||
} | |||
} | |||
} ext; | |||
#endif | |||
DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow) | |||
struct PrivateData { | |||
uintptr_t parentWindowHandle; | |||
uintptr_t transientWinId; | |||
uint width; | |||
uint height; | |||
double scaleFactor; | |||
String title; | |||
uint minWidth; | |||
uint minHeight; | |||
bool keepAspectRatio; | |||
bool isQuitting; | |||
bool isStandalone; | |||
bool visible; | |||
PrivateData() | |||
: parentWindowHandle(0), | |||
transientWinId(0), | |||
width(1), | |||
height(1), | |||
scaleFactor(1.0), | |||
title(), | |||
minWidth(0), | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
isQuitting(false), | |||
isStandalone(false), | |||
visible(false) {} | |||
} pData; | |||
DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -23,7 +23,25 @@ START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// The following code was based from juce-core LeakDetector class | |||
// Copyright (C) 2013 Raw Material Software Ltd. | |||
/** | |||
Copyright (C) 2013 Raw Material Software Ltd. | |||
Permission is granted to use this software under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |||
FITNESS. IN NO EVENT SHALL ISC 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. | |||
*/ | |||
/** A good old-fashioned C macro concatenation helper. | |||
This combines two items (which may themselves be macros) into a single string, | |||
@@ -54,13 +72,13 @@ START_NAMESPACE_DISTRHO | |||
DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName> 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 | |||
//============================================================================== | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -20,6 +20,9 @@ | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#endif | |||
@@ -39,7 +42,7 @@ public: | |||
/* | |||
* Constructor. | |||
*/ | |||
Mutex(bool inheritPriority = true) noexcept | |||
Mutex(const bool inheritPriority = true) noexcept | |||
: fMutex() | |||
{ | |||
pthread_mutexattr_t attr; | |||
@@ -61,9 +64,9 @@ public: | |||
/* | |||
* Lock the mutex. | |||
*/ | |||
void lock() const noexcept | |||
bool lock() const noexcept | |||
{ | |||
pthread_mutex_lock(&fMutex); | |||
return (pthread_mutex_lock(&fMutex) == 0); | |||
} | |||
/* | |||
@@ -86,8 +89,7 @@ public: | |||
private: | |||
mutable pthread_mutex_t fMutex; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(Mutex) | |||
DISTRHO_DECLARE_NON_COPYABLE(Mutex) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -133,12 +135,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,8 +177,7 @@ private: | |||
mutable pthread_mutex_t fMutex; | |||
#endif | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex) | |||
DISTRHO_DECLARE_NON_COPYABLE(RecursiveMutex) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -256,7 +258,7 @@ private: | |||
volatile bool fTriggered; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(Signal) | |||
DISTRHO_DECLARE_NON_COPYABLE(Signal) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -281,7 +283,7 @@ private: | |||
const Mutex& fMutex; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker) | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeLocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -295,6 +297,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) | |||
@@ -316,7 +322,7 @@ private: | |||
const bool fLocked; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker) | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeTryLocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -341,7 +347,7 @@ private: | |||
const Mutex& fMutex; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker) | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeUnlocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -25,7 +25,25 @@ START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// The following code was based from juce-core ScopedPointer class | |||
// Copyright (C) 2013 Raw Material Software Ltd. | |||
/** | |||
Copyright (C) 2013 Raw Material Software Ltd. | |||
Permission is granted to use this software under the terms of the ISC license | |||
http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |||
FITNESS. IN NO EVENT SHALL ISC 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. | |||
*/ | |||
//============================================================================== | |||
/** | |||
@@ -145,6 +163,9 @@ public: | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
ObjectType* get() const noexcept { return object; } | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
ObjectType& getObject() const noexcept { return *object; } | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
ObjectType& operator*() const noexcept { return *object; } | |||
@@ -20,6 +20,9 @@ | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#else | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,6 +18,7 @@ | |||
#define DISTRHO_STRING_HPP_INCLUDED | |||
#include "../DistrhoUtils.hpp" | |||
#include "../extra/ScopedSafeLocale.hpp" | |||
#include <algorithm> | |||
@@ -37,14 +38,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; | |||
@@ -56,20 +59,21 @@ public: | |||
/* | |||
* Simple char string. | |||
*/ | |||
explicit String(char* const strBuf, const bool copyData = true) noexcept | |||
explicit String(char* const strBuf, const bool reallocData = true) noexcept | |||
: fBuffer(_null()), | |||
fBufferLen(0) | |||
fBufferLen(0), | |||
fBufferAlloc(false) | |||
{ | |||
if (copyData || strBuf == nullptr) | |||
if (reallocData || strBuf == nullptr) | |||
{ | |||
_dup(strBuf); | |||
} | |||
else | |||
{ | |||
fBuffer = strBuf; | |||
fBufferLen = std::strlen(strBuf); | |||
fBuffer = strBuf; | |||
fBufferLen = std::strlen(strBuf); | |||
fBufferAlloc = true; | |||
} | |||
} | |||
/* | |||
@@ -77,7 +81,8 @@ public: | |||
*/ | |||
explicit String(const char* const strBuf) noexcept | |||
: fBuffer(_null()), | |||
fBufferLen(0) | |||
fBufferLen(0), | |||
fBufferAlloc(false) | |||
{ | |||
_dup(strBuf); | |||
} | |||
@@ -87,7 +92,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 +107,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 +122,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 +137,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 +152,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 +167,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 +182,16 @@ public: | |||
*/ | |||
explicit String(const float value) noexcept | |||
: fBuffer(_null()), | |||
fBufferLen(0) | |||
fBufferLen(0), | |||
fBufferAlloc(false) | |||
{ | |||
char strBuf[0xff+1]; | |||
std::snprintf(strBuf, 0xff, "%f", value); | |||
{ | |||
const ScopedSafeLocale ssl; | |||
std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value)); | |||
} | |||
strBuf[0xff] = '\0'; | |||
_dup(strBuf); | |||
@@ -185,10 +202,16 @@ public: | |||
*/ | |||
explicit String(const double value) noexcept | |||
: fBuffer(_null()), | |||
fBufferLen(0) | |||
fBufferLen(0), | |||
fBufferAlloc(false) | |||
{ | |||
char strBuf[0xff+1]; | |||
std::snprintf(strBuf, 0xff, "%g", value); | |||
{ | |||
const ScopedSafeLocale ssl; | |||
std::snprintf(strBuf, 0xff, "%.24g", value); | |||
} | |||
strBuf[0xff] = '\0'; | |||
_dup(strBuf); | |||
@@ -202,7 +225,8 @@ public: | |||
*/ | |||
String(const String& str) noexcept | |||
: fBuffer(_null()), | |||
fBufferLen(0) | |||
fBufferLen(0), | |||
fBufferAlloc(false) | |||
{ | |||
_dup(str.fBuffer); | |||
} | |||
@@ -217,13 +241,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 +276,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<fBufferLen; ++i) | |||
{ | |||
if (fBuffer[i] == c) | |||
return true; | |||
} | |||
return false; | |||
} | |||
/* | |||
* Check if the string contains another string, optionally ignoring case. | |||
*/ | |||
@@ -388,7 +425,7 @@ public: | |||
if (ret < 0) | |||
{ | |||
// should never happen! | |||
d_safe_assert("ret >= 0", __FILE__, __LINE__); | |||
d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret)); | |||
if (found != nullptr) | |||
*found = false; | |||
@@ -479,7 +516,7 @@ public: | |||
*/ | |||
String& replace(const char before, const char after) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this); | |||
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); | |||
for (std::size_t i=0; i < fBufferLen; ++i) | |||
{ | |||
@@ -490,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'. | |||
*/ | |||
@@ -498,9 +558,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 +587,7 @@ public: | |||
} | |||
/* | |||
* Convert to all ascii characters to lowercase. | |||
* Convert all ascii characters to lowercase. | |||
*/ | |||
String& toLower() noexcept | |||
{ | |||
@@ -545,7 +603,7 @@ public: | |||
} | |||
/* | |||
* Convert to all ascii characters to uppercase. | |||
* Convert all ascii characters to uppercase. | |||
*/ | |||
String& toUpper() noexcept | |||
{ | |||
@@ -560,6 +618,36 @@ public: | |||
return *this; | |||
} | |||
/* | |||
* Create a new string where all non-basic characters are converted to '_'. | |||
* @see toBasic() | |||
*/ | |||
String asBasic() const noexcept | |||
{ | |||
String s(*this); | |||
return s.toBasic(); | |||
} | |||
/* | |||
* Create a new string where all ascii characters are converted lowercase. | |||
* @see toLower() | |||
*/ | |||
String asLower() const noexcept | |||
{ | |||
String s(*this); | |||
return s.toLower(); | |||
} | |||
/* | |||
* Create a new string where all ascii characters are converted to uppercase. | |||
* @see toUpper() | |||
*/ | |||
String asUpper() const noexcept | |||
{ | |||
String s(*this); | |||
return s.toUpper(); | |||
} | |||
/* | |||
* Direct access to the string buffer (read-only). | |||
*/ | |||
@@ -568,6 +656,20 @@ public: | |||
return fBuffer; | |||
} | |||
/* | |||
* 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* ret = fBufferLen > 0 ? fBuffer : nullptr; | |||
fBuffer = _null(); | |||
fBufferLen = 0; | |||
fBufferAlloc = false; | |||
return ret; | |||
} | |||
// ------------------------------------------------------------------- | |||
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
// Copyright (C) 2004-2008 René Nyffenegger | |||
@@ -579,14 +681,18 @@ public: | |||
"abcdefghijklmnopqrstuvwxyz" | |||
"0123456789+/"; | |||
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U); | |||
#ifndef _MSC_VER | |||
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |||
#else | |||
constexpr std::size_t kTmpBufSize = 65536U; | |||
#endif | |||
const uchar* bytesToEncode((const uchar*)data); | |||
uint i=0, j=0; | |||
uint charArray3[3], charArray4[4]; | |||
char strBuf[kTmpBufSize+1]; | |||
char strBuf[kTmpBufSize + 1]; | |||
strBuf[kTmpBufSize] = '\0'; | |||
std::size_t strBufIndex = 0; | |||
@@ -711,16 +817,26 @@ public: | |||
String& operator+=(const char* const strBuf) noexcept | |||
{ | |||
if (strBuf == nullptr) | |||
if (strBuf == nullptr || strBuf[0] == '\0') | |||
return *this; | |||
const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1; | |||
char newBuf[newBufSize]; | |||
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; | |||
} | |||
// 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; | |||
} | |||
@@ -732,15 +848,20 @@ 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); | |||
return String(newBuf, false); | |||
} | |||
String operator+(const String& str) noexcept | |||
@@ -748,11 +869,18 @@ public: | |||
return operator+(str.fBuffer); | |||
} | |||
// needed for std::map compatibility | |||
bool operator<(const String& str) const noexcept | |||
{ | |||
return std::strcmp(fBuffer, str.fBuffer) < 0; | |||
} | |||
// ------------------------------------------------------------------- | |||
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. | |||
@@ -780,7 +908,7 @@ private: | |||
if (std::strcmp(fBuffer, strBuf) == 0) | |||
return; | |||
if (fBuffer != _null()) | |||
if (fBufferAlloc) | |||
std::free(fBuffer); | |||
fBufferLen = (size > 0) ? size : std::strlen(strBuf); | |||
@@ -788,28 +916,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<uint>(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; | |||
} | |||
} | |||
@@ -821,27 +952,41 @@ 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()); | |||
return String(newBuf); | |||
std::memcpy(newBuf, strBefore.buffer(), strBeforeLen); | |||
std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1); | |||
return String(newBuf, false); | |||
} | |||
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]; | |||
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::strcpy(newBuf, strBufBefore); | |||
std::strcat(newBuf, strBufAfter); | |||
std::memcpy(newBuf, strBufBefore, strBufBeforeLen); | |||
std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1); | |||
return String(newBuf); | |||
return String(newBuf, false); | |||
} | |||
// ----------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -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(); | |||
@@ -281,7 +324,7 @@ private: | |||
return nullptr; | |||
} | |||
DISTRHO_DECLARE_NON_COPY_CLASS(Thread) | |||
DISTRHO_DECLARE_NON_COPYABLE(Thread) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -27,10 +27,12 @@ | |||
/* Check OS */ | |||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) | |||
# define DISTRHO_API | |||
# define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) | |||
# define DISTRHO_OS_WINDOWS 1 | |||
# define DISTRHO_DLL_EXTENSION "dll" | |||
#else | |||
# define DISTRHO_API | |||
# define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) | |||
# if defined(__APPLE__) | |||
# define DISTRHO_OS_MAC 1 | |||
@@ -39,6 +41,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 | |||
@@ -51,26 +57,82 @@ | |||
# if HAVE_CPP11_SUPPORT | |||
# define DISTRHO_PROPER_CPP11_SUPPORT | |||
# endif | |||
#elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept) | |||
#elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept) || (defined(_MSC_VER) && _MSVC_LANG >= 201103L) | |||
# define DISTRHO_PROPER_CPP11_SUPPORT | |||
# if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407 && ! defined(__clang__)) || (defined(__clang__) && ! __has_extension(cxx_override_control)) | |||
# define override // gcc4.7+ only | |||
# define final // gcc4.7+ only | |||
# define override /* gcc4.7+ only */ | |||
# define final /* gcc4.7+ only */ | |||
# endif | |||
#endif | |||
#ifndef DISTRHO_PROPER_CPP11_SUPPORT | |||
# define constexpr | |||
# define noexcept throw() | |||
# define override | |||
# define final | |||
# define nullptr NULL | |||
#endif | |||
/* Define unlikely */ | |||
#ifdef __GNUC__ | |||
# define unlikely(x) __builtin_expect(x,0) | |||
#else | |||
# define unlikely(x) x | |||
#endif | |||
/* Define DISTRHO_DEPRECATED */ | |||
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480 | |||
# define DISTRHO_DEPRECATED __attribute__((deprecated)) | |||
#elif defined(_MSC_VER) | |||
# define DISTRHO_DEPRECATED [[deprecated]] /* Note: __declspec(deprecated) it not applicable to enum members */ | |||
#else | |||
# define DISTRHO_DEPRECATED | |||
#endif | |||
/* Define DISTRHO_DEPRECATED_BY */ | |||
#if defined(__clang__) && defined(DISTRHO_PROPER_CPP11_SUPPORT) | |||
# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("", other))) | |||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480 | |||
# 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; } | |||
#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_SAFE_ASSERT(cond) if (unlikely(!(cond))) d_safe_assert (#cond, __FILE__, __LINE__); | |||
#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (unlikely(!(cond))) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast<int>(value)); | |||
#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); | |||
#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (unlikely(!(cond))) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast<uint>(value)); | |||
#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); | |||
#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); break; } | |||
#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } | |||
#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } | |||
#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (unlikely(!(cond))) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); | |||
#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; } | |||
#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; } | |||
#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; } | |||
#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (unlikely(!(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 (unlikely(!(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 (unlikely(!(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 (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); break; } | |||
#define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); continue; } | |||
#define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); return ret; } | |||
#define DISTRHO_SAFE_ASSERT_INT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); break; } | |||
#define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); continue; } | |||
#define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2)); return ret; } | |||
#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); break; } | |||
#define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); continue; } | |||
#define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast<uint>(value)); return ret; } | |||
#define DISTRHO_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); break; } | |||
#define DISTRHO_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); continue; } | |||
#define DISTRHO_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2)); return ret; } | |||
/* Define DISTRHO_SAFE_EXCEPTION */ | |||
#define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } | |||
@@ -78,34 +140,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) | |||
#endif | |||
/* Define DISTRHO_PREVENT_HEAP_ALLOCATION */ | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
# define DISTRHO_PREVENT_HEAP_ALLOCATION \ | |||
@@ -119,6 +170,17 @@ private: \ | |||
static void operator delete(void*); | |||
#endif | |||
/* Define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION */ | |||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \ | |||
private: \ | |||
static void* operator new(std::size_t) = delete; | |||
#else | |||
# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \ | |||
private: \ | |||
static void* operator new(std::size_t); | |||
#endif | |||
/* Define namespace */ | |||
#ifndef DISTRHO_NAMESPACE | |||
# define DISTRHO_NAMESPACE DISTRHO | |||
@@ -127,10 +189,37 @@ 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 | |||
/* MSVC warnings */ | |||
#ifdef _MSC_VER | |||
# define strdup _strdup | |||
# pragma warning(disable:4244) /* possible loss of data */ | |||
#endif | |||
/* Useful macros */ | |||
#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0]) | |||
/* Useful typedefs */ | |||
typedef unsigned char uchar; | |||
typedef unsigned short int ushort; | |||
typedef unsigned int uint; | |||
typedef unsigned long int ulong; | |||
typedef unsigned long long int ulonglong; | |||
/* 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 |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -21,16 +21,20 @@ START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Static data, see DistrhoPluginInternal.hpp */ | |||
uint32_t d_lastBufferSize = 0; | |||
double d_lastSampleRate = 0.0; | |||
uint32_t d_nextBufferSize = 0; | |||
double d_nextSampleRate = 0.0; | |||
const char* d_nextBundlePath = nullptr; | |||
bool d_nextPluginIsDummy = false; | |||
bool d_nextCanRequestParameterValueChanges = false; | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Static fallback data, see DistrhoPluginInternal.hpp */ | |||
const String PluginExporter::sFallbackString; | |||
const AudioPort PluginExporter::sFallbackAudioPort; | |||
/* */ AudioPortWithBusId PluginExporter::sFallbackAudioPort; | |||
const ParameterRanges PluginExporter::sFallbackRanges; | |||
const ParameterEnumerationValues PluginExporter::sFallbackEnumValues; | |||
const PortGroupWithId PluginExporter::sFallbackPortGroup; | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Plugin */ | |||
@@ -39,7 +43,13 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou | |||
: pData(new PrivateData()) | |||
{ | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
#endif | |||
#ifdef DPF_ABORT_ON_ERROR | |||
# define DPF_ABORT abort(); | |||
#else | |||
# define DPF_ABORT | |||
#endif | |||
if (parameterCount > 0) | |||
@@ -48,26 +58,29 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou | |||
pData->parameters = new Parameter[parameterCount]; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
if (programCount > 0) | |||
{ | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
pData->programCount = programCount; | |||
pData->programNames = new String[programCount]; | |||
} | |||
#else | |||
DISTRHO_SAFE_ASSERT(programCount == 0); | |||
d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); | |||
DPF_ABORT | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
if (stateCount > 0) | |||
{ | |||
pData->stateCount = stateCount; | |||
pData->stateKeys = new String[stateCount]; | |||
pData->stateDefValues = new String[stateCount]; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
pData->stateCount = stateCount; | |||
pData->states = new State[stateCount]; | |||
#else | |||
DISTRHO_SAFE_ASSERT(stateCount == 0); | |||
d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); | |||
DPF_ABORT | |||
#endif | |||
} | |||
#undef DPF_ABORT | |||
} | |||
Plugin::~Plugin() | |||
@@ -88,6 +101,16 @@ double Plugin::getSampleRate() const noexcept | |||
return pData->sampleRate; | |||
} | |||
const char* Plugin::getBundlePath() const noexcept | |||
{ | |||
return pData->bundlePath; | |||
} | |||
bool Plugin::isDummyInstance() const noexcept | |||
{ | |||
return pData->isDummy; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
const TimePosition& Plugin::getTimePosition() const noexcept | |||
{ | |||
@@ -109,6 +132,25 @@ bool Plugin::writeMidiEvent(const MidiEvent& midiEvent) noexcept | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
bool Plugin::canRequestParameterValueChanges() const noexcept | |||
{ | |||
return pData->canRequestParameterValueChanges; | |||
} | |||
bool Plugin::requestParameterValueChange(const uint32_t index, const float value) noexcept | |||
{ | |||
return pData->requestParameterValueChangeCallback(index, value); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
bool Plugin::updateStateValue(const char* const key, const char* const value) noexcept | |||
{ | |||
return pData->updateStateValueCallback(key, value); | |||
} | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Init */ | |||
@@ -130,11 +172,69 @@ void Plugin::initAudioPort(bool input, uint32_t index, AudioPort& port) | |||
} | |||
} | |||
void Plugin::initParameter(uint32_t, Parameter&) {} | |||
void Plugin::initPortGroup(const uint32_t groupId, PortGroup& portGroup) | |||
{ | |||
fillInPredefinedPortGroupData(groupId, portGroup); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void Plugin::initProgramName(uint32_t, String&) {} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void Plugin::initState(const uint32_t index, State& state) | |||
{ | |||
uint hints = 0x0; | |||
String stateKey, defaultStateValue; | |||
#if defined(__clang__) | |||
#pragma clang diagnostic push | |||
#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
#endif | |||
initState(index, stateKey, defaultStateValue); | |||
if (isStateFile(index)) | |||
hints = kStateIsFilenamePath; | |||
#if defined(__clang__) | |||
#pragma clang diagnostic pop | |||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | |||
#pragma GCC diagnostic pop | |||
#endif | |||
state.hints = hints; | |||
state.key = stateKey; | |||
state.label = stateKey; | |||
state.defaultValue = defaultStateValue; | |||
} | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Init */ | |||
float Plugin::getParameterValue(uint32_t) const { return 0.0f; } | |||
void Plugin::setParameterValue(uint32_t, float) {} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void Plugin::loadProgram(uint32_t) {} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
String Plugin::getState(const char*) const { return String(); } | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void Plugin::setState(const char*, const char*) {} | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Callbacks (optional) */ | |||
void Plugin::bufferSizeChanged(uint32_t) {} | |||
void Plugin::sampleRateChanged(double) {} | |||
void Plugin::sampleRateChanged(double) {} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -22,6 +22,10 @@ | |||
#include "CarlaNative.hpp" | |||
// TODO | |||
#undef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
#define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0 | |||
// ----------------------------------------------------------------------- | |||
START_NAMESPACE_DISTRHO | |||
@@ -29,6 +33,9 @@ START_NAMESPACE_DISTRHO | |||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
static const writeMidiFunc writeMidiCallback = nullptr; | |||
#endif | |||
#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
// ----------------------------------------------------------------------- | |||
@@ -98,13 +105,9 @@ public: | |||
// --------------------------------------------- | |||
protected: | |||
void handleEditParameter(const uint32_t rindex, const bool touch) | |||
void handleEditParameter(const uint32_t, const bool) | |||
{ | |||
fHost->dispatcher(fHost->handle, | |||
NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER, | |||
static_cast<int32_t>(rindex), | |||
touch ? 1 : 0, | |||
nullptr, 0.0f); | |||
// TODO | |||
} | |||
void handleSetParameterValue(const uint32_t rindex, const float value) | |||
@@ -188,7 +191,7 @@ class PluginCarla : public NativePluginClass | |||
public: | |||
PluginCarla(const NativeHostDescriptor* const host) | |||
: NativePluginClass(host), | |||
fPlugin(this, writeMidiCallback), | |||
fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), | |||
fScalePointsCache(nullptr) | |||
{ | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
@@ -235,8 +238,8 @@ protected: | |||
int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED; | |||
const uint32_t paramHints = fPlugin.getParameterHints(index); | |||
if (paramHints & kParameterIsAutomable) | |||
nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMATABLE; | |||
if (paramHints & kParameterIsAutomatable) | |||
nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE; | |||
if (paramHints & kParameterIsBoolean) | |||
nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN; | |||
if (paramHints & kParameterIsInteger) | |||
@@ -364,8 +367,7 @@ protected: | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames, | |||
const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override | |||
void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override | |||
{ | |||
MidiEvent realMidiEvents[midiEventCount]; | |||
@@ -386,13 +388,12 @@ protected: | |||
realMidiEvent.dataExt = nullptr; | |||
} | |||
fPlugin.run(inBuffer, outBuffer, frames, realMidiEvents, midiEventCount); | |||
fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount); | |||
} | |||
#else | |||
void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames, | |||
const NativeMidiEvent* const, const uint32_t) override | |||
void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override | |||
{ | |||
fPlugin.run(inBuffer, outBuffer, frames); | |||
fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames); | |||
} | |||
#endif | |||
@@ -518,6 +519,19 @@ private: | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
bool requestParameterValueChange(const uint32_t index, const float value) | |||
{ | |||
// TODO implementation | |||
return false; | |||
} | |||
static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value) | |||
{ | |||
return thisPtr->requestParameterValueChange(index, value); | |||
} | |||
#endif | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginCarla) | |||
// ------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -69,6 +69,10 @@ | |||
# define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
# define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0 | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_WANT_PROGRAMS | |||
# define DISTRHO_PLUGIN_WANT_PROGRAMS 0 | |||
#endif | |||
@@ -79,12 +83,17 @@ | |||
#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE | |||
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0 | |||
# define DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_WANT_TIMEPOS | |||
# define DISTRHO_PLUGIN_WANT_TIMEPOS 0 | |||
#endif | |||
#ifndef DISTRHO_UI_USER_RESIZABLE | |||
# define DISTRHO_UI_USER_RESIZABLE 0 | |||
#endif | |||
#ifndef DISTRHO_UI_USE_NANOVG | |||
# define DISTRHO_UI_USE_NANOVG 0 | |||
#endif | |||
@@ -93,7 +102,7 @@ | |||
// Define DISTRHO_PLUGIN_HAS_EMBED_UI if needed | |||
#ifndef DISTRHO_PLUGIN_HAS_EMBED_UI | |||
# ifdef HAVE_DGL | |||
# if (defined(DGL_CAIRO) && defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && defined(HAVE_OPENGL)) | |||
# define DISTRHO_PLUGIN_HAS_EMBED_UI 1 | |||
# else | |||
# define DISTRHO_PLUGIN_HAS_EMBED_UI 0 | |||
@@ -104,7 +113,7 @@ | |||
// Define DISTRHO_UI_URI if needed | |||
#ifndef DISTRHO_UI_URI | |||
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI" | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
@@ -123,10 +132,23 @@ | |||
# error Synths need MIDI input to work! | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Enable state if plugin wants state files (deprecated) | |||
#ifdef DISTRHO_PLUGIN_WANT_STATEFILES | |||
# warning DISTRHO_PLUGIN_WANT_STATEFILES is deprecated | |||
# undef DISTRHO_PLUGIN_WANT_STATEFILES | |||
# if ! DISTRHO_PLUGIN_WANT_STATE | |||
# undef DISTRHO_PLUGIN_WANT_STATE | |||
# define DISTRHO_PLUGIN_WANT_STATE 1 | |||
# endif | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Enable full state if plugin exports presets | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET) | |||
# warning Plugins with programs and state should implement full state API too | |||
# undef DISTRHO_PLUGIN_WANT_FULL_STATE | |||
# define DISTRHO_PLUGIN_WANT_FULL_STATE 1 | |||
#endif | |||
@@ -134,11 +156,23 @@ | |||
// ----------------------------------------------------------------------- | |||
// Disable UI if DGL or External UI is not available | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI && ! defined(HAVE_DGL) | |||
#if (defined(DGL_CAIRO) && ! defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && ! defined(HAVE_OPENGL)) | |||
# undef DISTRHO_PLUGIN_HAS_EMBED_UI | |||
# define DISTRHO_PLUGIN_HAS_EMBED_UI 0 | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# undef DISTRHO_PLUGIN_HAS_UI | |||
# 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 |
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,6 +19,12 @@ | |||
#include "../DistrhoPlugin.hpp" | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
# include "DistrhoPluginVST3.hpp" | |||
#endif | |||
#include <set> | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
@@ -29,28 +35,76 @@ static const uint32_t kMaxMidiEvents = 512; | |||
// ----------------------------------------------------------------------- | |||
// Static data, see DistrhoPlugin.cpp | |||
extern uint32_t d_lastBufferSize; | |||
extern double d_lastSampleRate; | |||
extern uint32_t d_nextBufferSize; | |||
extern double d_nextSampleRate; | |||
extern const char* d_nextBundlePath; | |||
extern bool d_nextPluginIsDummy; | |||
extern bool d_nextCanRequestParameterValueChanges; | |||
// ----------------------------------------------------------------------- | |||
// DSP callbacks | |||
typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); | |||
typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value); | |||
typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value); | |||
// ----------------------------------------------------------------------- | |||
// Helpers | |||
struct AudioPortWithBusId : AudioPort { | |||
uint32_t busId; | |||
AudioPortWithBusId() | |||
: AudioPort(), | |||
busId(0) {} | |||
}; | |||
struct PortGroupWithId : PortGroup { | |||
uint32_t groupId; | |||
PortGroupWithId() | |||
: PortGroup(), | |||
groupId(kPortGroupNone) {} | |||
}; | |||
static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup) | |||
{ | |||
switch (groupId) | |||
{ | |||
case kPortGroupNone: | |||
portGroup.name.clear(); | |||
portGroup.symbol.clear(); | |||
break; | |||
case kPortGroupMono: | |||
portGroup.name = "Mono"; | |||
portGroup.symbol = "dpf_mono"; | |||
break; | |||
case kPortGroupStereo: | |||
portGroup.name = "Stereo"; | |||
portGroup.symbol = "dpf_stereo"; | |||
break; | |||
} | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Plugin private data | |||
struct Plugin::PrivateData { | |||
const bool canRequestParameterValueChanges; | |||
const bool isDummy; | |||
bool isProcessing; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
AudioPort* audioPorts; | |||
AudioPortWithBusId* audioPorts; | |||
#endif | |||
uint32_t parameterCount; | |||
uint32_t parameterOffset; | |||
Parameter* parameters; | |||
uint32_t portGroupCount; | |||
PortGroupWithId* portGroups; | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
uint32_t programCount; | |||
String* programNames; | |||
@@ -58,8 +112,7 @@ struct Plugin::PrivateData { | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
uint32_t stateCount; | |||
String* stateKeys; | |||
String* stateDefValues; | |||
State* states; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
@@ -73,34 +126,43 @@ struct Plugin::PrivateData { | |||
// Callbacks | |||
void* callbacksPtr; | |||
writeMidiFunc writeMidiCallbackFunc; | |||
requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc; | |||
updateStateValueFunc updateStateValueCallbackFunc; | |||
uint32_t bufferSize; | |||
double sampleRate; | |||
char* bundlePath; | |||
PrivateData() noexcept | |||
: isProcessing(false), | |||
: canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), | |||
isDummy(d_nextPluginIsDummy), | |||
isProcessing(false), | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
audioPorts(nullptr), | |||
#endif | |||
parameterCount(0), | |||
parameterOffset(0), | |||
parameters(nullptr), | |||
portGroupCount(0), | |||
portGroups(nullptr), | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
programCount(0), | |||
programNames(nullptr), | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
stateCount(0), | |||
stateKeys(nullptr), | |||
stateDefValues(nullptr), | |||
states(nullptr), | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
latency(0), | |||
#endif | |||
callbacksPtr(nullptr), | |||
writeMidiCallbackFunc(nullptr), | |||
bufferSize(d_lastBufferSize), | |||
sampleRate(d_lastSampleRate) | |||
requestParameterValueChangeCallbackFunc(nullptr), | |||
updateStateValueCallbackFunc(nullptr), | |||
bufferSize(d_nextBufferSize), | |||
sampleRate(d_nextSampleRate), | |||
bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) | |||
{ | |||
DISTRHO_SAFE_ASSERT(bufferSize != 0); | |||
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | |||
@@ -113,12 +175,16 @@ 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_STATE || DISTRHO_PLUGIN_WANT_TIMEPOS) | |||
parameterOffset += 1; | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
# endif | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# endif | |||
# endif | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
parameterOffset += kVst3InternalParameterCount; | |||
#endif | |||
} | |||
@@ -138,6 +204,12 @@ struct Plugin::PrivateData { | |||
parameters = nullptr; | |||
} | |||
if (portGroups != nullptr) | |||
{ | |||
delete[] portGroups; | |||
portGroups = nullptr; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
if (programNames != nullptr) | |||
{ | |||
@@ -147,18 +219,18 @@ struct Plugin::PrivateData { | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
if (stateKeys != nullptr) | |||
if (states != nullptr) | |||
{ | |||
delete[] stateKeys; | |||
stateKeys = nullptr; | |||
delete[] states; | |||
states = nullptr; | |||
} | |||
#endif | |||
if (stateDefValues != nullptr) | |||
if (bundlePath != nullptr) | |||
{ | |||
delete[] stateDefValues; | |||
stateDefValues = nullptr; | |||
std::free(bundlePath); | |||
bundlePath = nullptr; | |||
} | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
@@ -170,6 +242,27 @@ struct Plugin::PrivateData { | |||
return false; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
bool requestParameterValueChangeCallback(const uint32_t index, const float value) | |||
{ | |||
if (requestParameterValueChangeCallbackFunc != nullptr) | |||
return requestParameterValueChangeCallbackFunc(callbacksPtr, index, value); | |||
return false; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
bool updateStateValueCallback(const char* const key, const char* const value) | |||
{ | |||
d_stdout("updateStateValueCallback %p", updateStateValueCallbackFunc); | |||
if (updateStateValueCallbackFunc != nullptr) | |||
return updateStateValueCallbackFunc(callbacksPtr, key, value); | |||
return false; | |||
} | |||
#endif | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -178,7 +271,10 @@ struct Plugin::PrivateData { | |||
class PluginExporter | |||
{ | |||
public: | |||
PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall) | |||
PluginExporter(void* const callbacksPtr, | |||
const writeMidiFunc writeMidiCall, | |||
const requestParameterValueChangeFunc requestParameterValueChangeCall, | |||
const updateStateValueFunc updateStateValueCall) | |||
: fPlugin(createPlugin()), | |||
fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | |||
fIsActive(false) | |||
@@ -186,6 +282,79 @@ public: | |||
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
#if defined(DPF_RUNTIME_TESTING) && defined(__GNUC__) && !defined(__clang__) | |||
/* Run-time testing build. | |||
* Verify that virtual functions are overriden if parameters, programs or states are in use. | |||
* This does not work on all compilers, but we use it purely as informational check anyway. */ | |||
if (fData->parameterCount != 0) | |||
{ | |||
if ((void*)(fPlugin->*(&Plugin::initParameter)) == (void*)&Plugin::initParameter) | |||
{ | |||
d_stderr2("DPF warning: Plugins with parameters must implement `initParameter`"); | |||
abort(); | |||
} | |||
if ((void*)(fPlugin->*(&Plugin::getParameterValue)) == (void*)&Plugin::getParameterValue) | |||
{ | |||
d_stderr2("DPF warning: Plugins with parameters must implement `getParameterValue`"); | |||
abort(); | |||
} | |||
if ((void*)(fPlugin->*(&Plugin::setParameterValue)) == (void*)&Plugin::setParameterValue) | |||
{ | |||
d_stderr2("DPF warning: Plugins with parameters must implement `setParameterValue`"); | |||
abort(); | |||
} | |||
} | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
if (fData->programCount != 0) | |||
{ | |||
if ((void*)(fPlugin->*(&Plugin::initProgramName)) == (void*)&Plugin::initProgramName) | |||
{ | |||
d_stderr2("DPF warning: Plugins with programs must implement `initProgramName`"); | |||
abort(); | |||
} | |||
if ((void*)(fPlugin->*(&Plugin::loadProgram)) == (void*)&Plugin::loadProgram) | |||
{ | |||
d_stderr2("DPF warning: Plugins with programs must implement `loadProgram`"); | |||
abort(); | |||
} | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
if (fData->stateCount != 0) | |||
{ | |||
if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState) | |||
{ | |||
d_stderr2("DPF warning: Plugins with state must implement `initState`"); | |||
abort(); | |||
} | |||
if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState) | |||
{ | |||
d_stderr2("DPF warning: Plugins with state must implement `setState`"); | |||
abort(); | |||
} | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
if (fData->stateCount != 0) | |||
{ | |||
if ((void*)(fPlugin->*(&Plugin::getState)) == (void*)&Plugin::getState) | |||
{ | |||
d_stderr2("DPF warning: Plugins with full state must implement `getState`"); | |||
abort(); | |||
} | |||
} | |||
else | |||
{ | |||
d_stderr2("DPF warning: Plugins with full state must have at least 1 state"); | |||
abort(); | |||
} | |||
# endif | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
{ | |||
uint32_t j=0; | |||
@@ -198,11 +367,42 @@ public: | |||
fPlugin->initAudioPort(false, i, fData->audioPorts[j]); | |||
# endif | |||
} | |||
#endif | |||
#endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) | |||
fPlugin->initParameter(i, fData->parameters[i]); | |||
{ | |||
std::set<uint32_t> portGroupIndices; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
portGroupIndices.insert(fData->audioPorts[i].groupId); | |||
#endif | |||
for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) | |||
portGroupIndices.insert(fData->parameters[i].groupId); | |||
portGroupIndices.erase(kPortGroupNone); | |||
if (const uint32_t portGroupSize = static_cast<uint32_t>(portGroupIndices.size())) | |||
{ | |||
fData->portGroups = new PortGroupWithId[portGroupSize]; | |||
fData->portGroupCount = portGroupSize; | |||
uint32_t index = 0; | |||
for (std::set<uint32_t>::iterator it = portGroupIndices.begin(); it != portGroupIndices.end(); ++it, ++index) | |||
{ | |||
PortGroupWithId& portGroup(fData->portGroups[index]); | |||
portGroup.groupId = *it; | |||
if (portGroup.groupId < portGroupSize) | |||
fPlugin->initPortGroup(portGroup.groupId, portGroup); | |||
else | |||
fillInPredefinedPortGroupData(portGroup.groupId, portGroup); | |||
} | |||
} | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
for (uint32_t i=0, count=fData->programCount; i < count; ++i) | |||
fPlugin->initProgramName(i, fData->programNames[i]); | |||
@@ -210,11 +410,13 @@ public: | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
for (uint32_t i=0, count=fData->stateCount; i < count; ++i) | |||
fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); | |||
fPlugin->initState(i, fData->states[i]); | |||
#endif | |||
fData->callbacksPtr = callbacksPtr; | |||
fData->callbacksPtr = callbacksPtr; | |||
fData->writeMidiCallbackFunc = writeMidiCall; | |||
fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall; | |||
fData->updateStateValueCallbackFunc = updateStateValueCall; | |||
} | |||
~PluginExporter() | |||
@@ -297,7 +499,7 @@ public: | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
const AudioPort& getAudioPort(const bool input, const uint32_t index) const noexcept | |||
AudioPortWithBusId& getAudioPort(const bool input, const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort); | |||
@@ -316,6 +518,11 @@ public: | |||
return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)]; | |||
} | |||
uint32_t getAudioPortHints(const bool input, const uint32_t index) const noexcept | |||
{ | |||
return getAudioPort(input, index).hints; | |||
} | |||
#endif | |||
uint32_t getParameterCount() const noexcept | |||
@@ -356,6 +563,11 @@ public: | |||
return (getParameterHints(index) & kParameterIsOutput) != 0x0; | |||
} | |||
bool isParameterTrigger(const uint32_t index) const noexcept | |||
{ | |||
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger; | |||
} | |||
bool isParameterOutputOrTrigger(const uint32_t index) const noexcept | |||
{ | |||
const uint32_t hints = getParameterHints(index); | |||
@@ -375,6 +587,13 @@ public: | |||
return fData->parameters[index].name; | |||
} | |||
const String& getParameterShortName(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
return fData->parameters[index].shortName; | |||
} | |||
const String& getParameterSymbol(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
@@ -389,6 +608,13 @@ public: | |||
return fData->parameters[index].unit; | |||
} | |||
const String& getParameterDescription(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString); | |||
return fData->parameters[index].description; | |||
} | |||
const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues); | |||
@@ -410,6 +636,21 @@ public: | |||
return fData->parameters[index].midiCC; | |||
} | |||
uint32_t getParameterGroupId(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, kPortGroupNone); | |||
return fData->parameters[index].groupId; | |||
} | |||
float getParameterDefault(const uint32_t index) const | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f); | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f); | |||
return fData->parameters[index].ranges.def; | |||
} | |||
float getParameterValue(const uint32_t index) const | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f); | |||
@@ -426,6 +667,42 @@ public: | |||
fPlugin->setParameterValue(index, value); | |||
} | |||
uint32_t getPortGroupCount() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||
return fData->portGroupCount; | |||
} | |||
const PortGroupWithId& getPortGroupById(const uint32_t groupId) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && fData->portGroupCount != 0, sFallbackPortGroup); | |||
for (uint32_t i=0; i < fData->portGroupCount; ++i) | |||
{ | |||
const PortGroupWithId& portGroup(fData->portGroups[i]); | |||
if (portGroup.groupId == groupId) | |||
return portGroup; | |||
} | |||
return sFallbackPortGroup; | |||
} | |||
const PortGroupWithId& getPortGroupByIndex(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->portGroupCount, sFallbackPortGroup); | |||
return fData->portGroups[index]; | |||
} | |||
const String& getPortGroupSymbolForId(const uint32_t groupId) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString); | |||
return getPortGroupById(groupId).symbol; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
uint32_t getProgramCount() const noexcept | |||
{ | |||
@@ -458,22 +735,43 @@ public: | |||
return fData->stateCount; | |||
} | |||
uint32_t getStateHints(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0); | |||
return fData->states[index].hints; | |||
} | |||
const String& getStateKey(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||
return fData->stateKeys[index]; | |||
return fData->states[index].key; | |||
} | |||
const String& getStateDefaultValue(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||
return fData->stateDefValues[index]; | |||
return fData->states[index].defaultValue; | |||
} | |||
const String& getStateLabel(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||
return fData->states[index].label; | |||
} | |||
const String& getStateDescription(const uint32_t index) const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); | |||
return fData->states[index].description; | |||
} | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
String getState(const char* key) const | |||
String getStateValue(const char* const key) const | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString); | |||
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', sFallbackString); | |||
@@ -498,7 +796,7 @@ public: | |||
for (uint32_t i=0; i < fData->stateCount; ++i) | |||
{ | |||
if (fData->stateKeys[i] == key) | |||
if (fData->states[i].key == key) | |||
return true; | |||
} | |||
@@ -552,7 +850,7 @@ public: | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
void run(const float* const* const inputs, float** const outputs, const uint32_t frames, | |||
void run(const float** const inputs, float** const outputs, const uint32_t frames, | |||
const MidiEvent* const midiEvents, const uint32_t midiEventCount) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
@@ -569,7 +867,7 @@ public: | |||
fData->isProcessing = false; | |||
} | |||
#else | |||
void run(const float* const* const inputs, float** const outputs, const uint32_t frames) | |||
void run(const float** const inputs, float** const outputs, const uint32_t frames) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); | |||
@@ -650,12 +948,15 @@ private: | |||
// Static fallback data, see DistrhoPlugin.cpp | |||
static const String sFallbackString; | |||
static const AudioPort sFallbackAudioPort; | |||
static /* */ AudioPortWithBusId sFallbackAudioPort; | |||
static const ParameterRanges sFallbackRanges; | |||
static const ParameterEnumerationValues sFallbackEnumValues; | |||
static const PortGroupWithId sFallbackPortGroup; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) | |||
#ifndef DISTRHO_PLUGIN_TARGET_VST3 /* there is no way around this for VST3 */ | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
#endif | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -14,78 +14,280 @@ | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "DistrhoUIInternal.hpp" | |||
#include "src/DistrhoPluginChecks.h" | |||
#include "src/DistrhoDefines.h" | |||
#ifdef HAVE_DGL | |||
# include "src/WidgetPrivateData.hpp" | |||
#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_OS_MAC) | |||
# define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION | |||
# define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) | |||
# define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin | |||
# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_add_recent) | |||
# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_buttons) | |||
# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_filter_callback) | |||
# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_close) | |||
# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_configure) | |||
# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_filename) | |||
# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_free_recent) | |||
# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_handle_events) | |||
# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_load_recent) | |||
# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_at) | |||
# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_count) | |||
# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_file) | |||
# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_save_recent) | |||
# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_show) | |||
# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_status) | |||
# include "../extra/FileBrowserDialog.cpp" | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# if defined(DISTRHO_OS_WINDOWS) | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
# elif defined(HAVE_X11) | |||
# include <X11/Xresource.h> | |||
# endif | |||
#else | |||
# include "src/TopLevelWidgetPrivateData.hpp" | |||
# include "src/WindowPrivateData.hpp" | |||
#endif | |||
#include "DistrhoUIPrivateData.hpp" | |||
START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Static data, see DistrhoUIInternal.hpp */ | |||
double d_lastUiSampleRate = 0.0; | |||
void* d_lastUiDspPtr = nullptr; | |||
#ifdef HAVE_DGL | |||
Window* d_lastUiWindow = nullptr; | |||
const char* g_nextBundlePath = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uintptr_t g_nextWindowId = 0; | |||
double g_nextScaleFactor = 1.0; | |||
#endif | |||
uintptr_t g_nextWindowId = 0; | |||
const char* g_nextBundlePath = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI */ | |||
* get global scale factor */ | |||
#ifdef HAVE_DGL | |||
UI::UI(uint width, uint height) | |||
: UIWidget(*d_lastUiWindow), | |||
pData(new PrivateData()) | |||
#ifdef DISTRHO_OS_MAC | |||
double getDesktopScaleFactor(uintptr_t parentWindowHandle); | |||
#else | |||
static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
{ | |||
((UIWidget*)this)->pData->needsFullViewport = false; | |||
// allow custom scale for testing | |||
if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | |||
return std::max(1.0, std::atof(scale)); | |||
#if defined(DISTRHO_OS_WINDOWS) | |||
if (const HMODULE Shcore = LoadLibraryA("Shcore.dll")) | |||
{ | |||
typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*); | |||
typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wcast-function-type" | |||
# endif | |||
const PFN_GetProcessDpiAwareness GetProcessDpiAwareness | |||
= (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness"); | |||
const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor | |||
= (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor"); | |||
# if defined(__GNUC__) && (__GNUC__ >= 9) | |||
# pragma GCC diagnostic pop | |||
# endif | |||
DWORD dpiAware = 0; | |||
if (GetProcessDpiAwareness && GetScaleFactorForMonitor | |||
&& GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0) | |||
{ | |||
const HMONITOR hMon = parentWindowHandle != 0 | |||
? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY) | |||
: MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY); | |||
DWORD scaleFactor = 0; | |||
if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0) | |||
{ | |||
FreeLibrary(Shcore); | |||
return static_cast<double>(scaleFactor) / 100.0; | |||
} | |||
} | |||
FreeLibrary(Shcore); | |||
} | |||
#elif defined(HAVE_X11) | |||
::Display* const display = XOpenDisplay(nullptr); | |||
DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0); | |||
XrmInitialize(); | |||
if (char* const rms = XResourceManagerString(display)) | |||
{ | |||
if (const XrmDatabase sdb = XrmGetStringDatabase(rms)) | |||
{ | |||
char* type = nullptr; | |||
XrmValue ret; | |||
if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret) | |||
&& ret.addr != nullptr | |||
&& type != nullptr | |||
&& std::strncmp("String", type, 6) == 0) | |||
{ | |||
if (const double dpi = std::atof(ret.addr)) | |||
{ | |||
XCloseDisplay(display); | |||
return dpi / 96; | |||
} | |||
} | |||
} | |||
} | |||
if (width > 0 && height > 0) | |||
setSize(width, height); | |||
XCloseDisplay(display); | |||
#endif | |||
return 1.0; | |||
// might be unused | |||
(void)parentWindowHandle; | |||
} | |||
#endif // !DISTRHO_OS_MAC | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI::PrivateData special handling */ | |||
UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
ExternalWindow::PrivateData | |||
#else | |||
UI::UI(uint width, uint height) | |||
: UIWidget(width, height), | |||
pData(new PrivateData()) {} | |||
PluginWindow& | |||
#endif | |||
UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height) | |||
{ | |||
UI::PrivateData* const pData = s_nextPrivateData; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
pData->window = new PluginWindow(ui, pData->app); | |||
ExternalWindow::PrivateData ewData; | |||
ewData.parentWindowHandle = pData->winId; | |||
ewData.width = width; | |||
ewData.height = height; | |||
ewData.scaleFactor = pData->scaleFactor != 0.0 ? pData->scaleFactor : getDesktopScaleFactor(pData->winId); | |||
ewData.title = DISTRHO_PLUGIN_NAME; | |||
ewData.isStandalone = DISTRHO_UI_IS_STANDALONE; | |||
return ewData; | |||
#else | |||
pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor); | |||
// If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks | |||
if (pData->callbacksPtr == nullptr) | |||
pData->window->setIgnoreIdleCallbacks(); | |||
return pData->window.getObject(); | |||
#endif | |||
} | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI */ | |||
UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) | |||
: UIWidget(UI::PrivateData::createNextWindow(this, width, height)), | |||
uiData(UI::PrivateData::s_nextPrivateData) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
if (width != 0 && height != 0) | |||
{ | |||
Widget::setSize(width, height); | |||
if (automaticallyScaleAndSetAsMinimumSize) | |||
setGeometryConstraints(width, height, true, true, true); | |||
} | |||
#else | |||
// unused | |||
(void)automaticallyScaleAndSetAsMinimumSize; | |||
#endif | |||
} | |||
UI::~UI() | |||
{ | |||
delete pData; | |||
} | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Host state */ | |||
bool UI::isResizable() const noexcept | |||
{ | |||
#if DISTRHO_UI_USER_RESIZABLE | |||
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
return true; | |||
# else | |||
return uiData->window->isResizable(); | |||
# endif | |||
#else | |||
return false; | |||
#endif | |||
} | |||
uint UI::getBackgroundColor() const noexcept | |||
{ | |||
return uiData->bgColor; | |||
} | |||
uint UI::getForegroundColor() const noexcept | |||
{ | |||
return uiData->fgColor; | |||
} | |||
double UI::getSampleRate() const noexcept | |||
{ | |||
return pData->sampleRate; | |||
return uiData->sampleRate; | |||
} | |||
const char* UI::getBundlePath() const noexcept | |||
{ | |||
return uiData->bundlePath; | |||
} | |||
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_STATE | |||
bool UI::requestStateFile(const char* 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 | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
bool UI::openFileBrowser(const FileBrowserOptions& options) | |||
{ | |||
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// TODO | |||
return false; | |||
(void)options; | |||
# else | |||
return getWindow().openFileBrowser(options); | |||
# endif | |||
} | |||
#endif | |||
@@ -95,60 +297,102 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | |||
void* UI::getPluginInstancePointer() const noexcept | |||
{ | |||
return pData->dspPtr; | |||
return uiData->dspPtr; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* External UI helpers */ | |||
* External UI helpers (static calls) */ | |||
const char* UI::getNextBundlePath() noexcept | |||
{ | |||
return g_nextBundlePath; | |||
} | |||
double UI::getNextScaleFactor() noexcept | |||
{ | |||
return g_nextScaleFactor; | |||
} | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
uintptr_t UI::getNextWindowId() noexcept | |||
{ | |||
return g_nextWindowId; | |||
} | |||
# endif | |||
#endif | |||
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* DSP/Plugin Callbacks (optional) */ | |||
void UI::sampleRateChanged(double) {} | |||
void UI::sampleRateChanged(double) | |||
{ | |||
} | |||
#ifdef HAVE_DGL | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI Callbacks (optional) */ | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
void UI::uiFileBrowserSelected(const char*) | |||
void UI::uiScaleFactorChanged(double) | |||
{ | |||
} | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode) | |||
{ | |||
} | |||
#endif | |||
void UI::uiReshape(uint width, uint height) | |||
void UI::uiReshape(uint, uint) | |||
{ | |||
glEnable(GL_BLEND); | |||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
glMatrixMode(GL_PROJECTION); | |||
glLoadIdentity(); | |||
glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); | |||
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
glMatrixMode(GL_MODELVIEW); | |||
glLoadIdentity(); | |||
// NOTE this must be the same as Window::onReshape | |||
pData->fallbackOnResize(); | |||
} | |||
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
void UI::uiFileBrowserSelected(const char*) | |||
{ | |||
} | |||
#endif | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI Resize Handling, internal */ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void UI::sizeChanged(const uint width, const uint height) | |||
{ | |||
UIWidget::sizeChanged(width, height); | |||
uiData->setSizeCallback(width, height); | |||
} | |||
#else | |||
void UI::onResize(const ResizeEvent& ev) | |||
{ | |||
pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight()); | |||
UIWidget::onResize(ev); | |||
#ifndef DISTRHO_PLUGIN_TARGET_VST3 | |||
if (uiData->initializing) | |||
return; | |||
const uint width = ev.size.getWidth(); | |||
const uint height = ev.size.getHeight(); | |||
uiData->setSizeCallback(width, height); | |||
#endif | |||
} | |||
// NOTE: only used for VST3 | |||
void UI::requestSizeChange(const uint width, const uint height) | |||
{ | |||
# ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
if (uiData->initializing) | |||
uiData->window->setSizeForVST3(width, height); | |||
else | |||
uiData->setSizeCallback(width, height); | |||
# else | |||
// unused | |||
(void)width; | |||
(void)height; | |||
# endif | |||
} | |||
#endif | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -17,462 +17,403 @@ | |||
#ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED | |||
#define DISTRHO_UI_INTERNAL_HPP_INCLUDED | |||
#include "../DistrhoUI.hpp" | |||
#ifdef HAVE_DGL | |||
# include "../../dgl/Application.hpp" | |||
# include "../../dgl/Window.hpp" | |||
using DGL_NAMESPACE::Application; | |||
using DGL_NAMESPACE::IdleCallback; | |||
using DGL_NAMESPACE::Window; | |||
#endif | |||
#include "DistrhoUIPrivateData.hpp" | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// Static data, see DistrhoUI.cpp | |||
extern double d_lastUiSampleRate; | |||
extern void* d_lastUiDspPtr; | |||
#ifdef HAVE_DGL | |||
extern Window* d_lastUiWindow; | |||
#endif | |||
extern uintptr_t g_nextWindowId; | |||
extern const char* g_nextBundlePath; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
extern uintptr_t g_nextWindowId; | |||
extern double g_nextScaleFactor; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// UI callbacks | |||
// UI exporter class | |||
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); | |||
class UIExporter | |||
{ | |||
// ------------------------------------------------------------------- | |||
// UI Widget and its private data | |||
// ----------------------------------------------------------------------- | |||
// UI private data | |||
struct UI::PrivateData { | |||
// DSP | |||
double sampleRate; | |||
uint32_t parameterOffset; | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
void* dspPtr; | |||
#endif | |||
UI* ui; | |||
UI::PrivateData* uiData; | |||
// Callbacks | |||
void* callbacksPtr; | |||
editParamFunc editParamCallbackFunc; | |||
setParamFunc setParamCallbackFunc; | |||
setStateFunc setStateCallbackFunc; | |||
sendNoteFunc sendNoteCallbackFunc; | |||
setSizeFunc setSizeCallbackFunc; | |||
PrivateData() noexcept | |||
: sampleRate(d_lastUiSampleRate), | |||
parameterOffset(0), | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
dspPtr(d_lastUiDspPtr), | |||
#endif | |||
callbacksPtr(nullptr), | |||
editParamCallbackFunc(nullptr), | |||
setParamCallbackFunc(nullptr), | |||
setStateCallbackFunc(nullptr), | |||
sendNoteCallbackFunc(nullptr), | |||
setSizeCallbackFunc(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 | |||
public: | |||
UIExporter(void* const callbacksPtr, | |||
const uintptr_t winId, | |||
const double sampleRate, | |||
const editParamFunc editParamCall, | |||
const setParamFunc setParamCall, | |||
const setStateFunc setStateCall, | |||
const sendNoteFunc sendNoteCall, | |||
const setSizeFunc setSizeCall, | |||
const fileRequestFunc fileRequestCall, | |||
const char* const bundlePath = nullptr, | |||
void* const dspPtr = nullptr, | |||
const double scaleFactor = 0.0, | |||
const uint32_t bgColor = 0, | |||
const uint32_t fgColor = 0xffffffff) | |||
: ui(nullptr), | |||
uiData(new UI::PrivateData()) | |||
{ | |||
uiData->sampleRate = sampleRate; | |||
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | |||
uiData->dspPtr = dspPtr; | |||
uiData->bgColor = bgColor; | |||
uiData->fgColor = fgColor; | |||
uiData->scaleFactor = scaleFactor; | |||
uiData->winId = winId; | |||
uiData->callbacksPtr = callbacksPtr; | |||
uiData->editParamCallbackFunc = editParamCall; | |||
uiData->setParamCallbackFunc = setParamCall; | |||
uiData->setStateCallbackFunc = setStateCall; | |||
uiData->sendNoteCallbackFunc = sendNoteCall; | |||
uiData->setSizeCallbackFunc = setSizeCall; | |||
uiData->fileRequestCallbackFunc = fileRequestCall; | |||
g_nextBundlePath = bundlePath; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
g_nextWindowId = winId; | |||
g_nextScaleFactor = scaleFactor; | |||
#endif | |||
} | |||
UI::PrivateData::s_nextPrivateData = uiData; | |||
void editParamCallback(const uint32_t rindex, const bool started) | |||
{ | |||
if (editParamCallbackFunc != nullptr) | |||
editParamCallbackFunc(callbacksPtr, rindex, started); | |||
} | |||
UI* const uiPtr = createUI(); | |||
void setParamCallback(const uint32_t rindex, const float value) | |||
{ | |||
if (setParamCallbackFunc != nullptr) | |||
setParamCallbackFunc(callbacksPtr, rindex, value); | |||
} | |||
g_nextBundlePath = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
g_nextWindowId = 0; | |||
g_nextScaleFactor = 0.0; | |||
#else | |||
// enter context called in the PluginWindow constructor, see DistrhoUIPrivateData.hpp | |||
uiData->window->leaveContext(); | |||
#endif | |||
UI::PrivateData::s_nextPrivateData = nullptr; | |||
void setStateCallback(const char* const key, const char* const value) | |||
{ | |||
if (setStateCallbackFunc != nullptr) | |||
setStateCallbackFunc(callbacksPtr, key, value); | |||
} | |||
DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,); | |||
ui = uiPtr; | |||
uiData->initializing = false; | |||
void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
{ | |||
if (sendNoteCallbackFunc != nullptr) | |||
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// unused | |||
(void)bundlePath; | |||
#endif | |||
} | |||
void setSizeCallback(const uint width, const uint height) | |||
~UIExporter() | |||
{ | |||
if (setSizeCallbackFunc != nullptr) | |||
setSizeCallbackFunc(callbacksPtr, width, height); | |||
quit(); | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uiData->window->enterContextForDeletion(); | |||
#endif | |||
delete ui; | |||
delete uiData; | |||
} | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Plugin Window, needed to take care of resize properly | |||
// ------------------------------------------------------------------- | |||
#ifdef HAVE_DGL | |||
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; | |||
} | |||
class UIExporterWindow : public Window | |||
{ | |||
public: | |||
UIExporterWindow(Application& app, const intptr_t winId, void* const dspPtr) | |||
: Window(app, winId), | |||
fUI(createUiWrapper(dspPtr, this)), | |||
fIsReady(false) | |||
uint getWidth() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
// set window size | |||
setResizable(false); | |||
setSize(fUI->getWidth(), fUI->getHeight()); | |||
return uiData->window->getWidth(); | |||
} | |||
~UIExporterWindow() | |||
uint getHeight() const noexcept | |||
{ | |||
delete fUI; | |||
return uiData->window->getHeight(); | |||
} | |||
UI* getUI() const noexcept | |||
double getScaleFactor() const noexcept | |||
{ | |||
return fUI; | |||
return uiData->window->getScaleFactor(); | |||
} | |||
bool isReady() const noexcept | |||
bool getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept | |||
{ | |||
return fIsReady; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uiData->window->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); | |||
#else | |||
const DGL_NAMESPACE::Size<uint> size(uiData->window->getGeometryConstraints(keepAspectRatio)); | |||
minimumWidth = size.getWidth(); | |||
minimumHeight = size.getHeight(); | |||
#endif | |||
return true; | |||
} | |||
protected: | |||
// custom window reshape | |||
void onReshape(uint width, uint height) override | |||
bool isResizable() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->uiReshape(width, height); | |||
fIsReady = true; | |||
return uiData->window->isResizable(); | |||
} | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
// custom file-browser selected | |||
void fileBrowserSelected(const char* filename) override | |||
bool isVisible() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->uiFileBrowserSelected(filename); | |||
return uiData->window->isVisible(); | |||
} | |||
#endif | |||
private: | |||
UI* const fUI; | |||
bool fIsReady; | |||
}; | |||
#else | |||
static inline | |||
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath) | |||
{ | |||
d_lastUiDspPtr = dspPtr; | |||
g_nextWindowId = winId; | |||
g_nextBundlePath = bundlePath; | |||
UI* const ret = createUI(); | |||
d_lastUiDspPtr = nullptr; | |||
g_nextWindowId = 0; | |||
g_nextBundlePath = nullptr; | |||
return ret; | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// UI exporter class | |||
class UIExporter | |||
{ | |||
public: | |||
UIExporter(void* const callbacksPtr, | |||
const intptr_t winId, | |||
const editParamFunc editParamCall, | |||
const setParamFunc setParamCall, | |||
const setStateFunc setStateCall, | |||
const sendNoteFunc sendNoteCall, | |||
const setSizeFunc setSizeCall, | |||
void* const dspPtr = nullptr, | |||
const char* const bundlePath = nullptr) | |||
#ifdef HAVE_DGL | |||
: glApp(), | |||
glWindow(glApp, winId, dspPtr), | |||
fChangingSize(false), | |||
fUI(glWindow.getUI()), | |||
#else | |||
: fUI(createUiWrapper(dspPtr, winId, bundlePath)), | |||
#endif | |||
fData((fUI != nullptr) ? fUI->pData : nullptr) | |||
uintptr_t getNativeWindowHandle() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
fData->callbacksPtr = callbacksPtr; | |||
fData->editParamCallbackFunc = editParamCall; | |||
fData->setParamCallbackFunc = setParamCall; | |||
fData->setStateCallbackFunc = setStateCall; | |||
fData->sendNoteCallbackFunc = sendNoteCall; | |||
fData->setSizeCallbackFunc = setSizeCall; | |||
#ifdef HAVE_DGL | |||
// unused | |||
return; (void)bundlePath; | |||
#endif | |||
return uiData->window->getNativeWindowHandle(); | |||
} | |||
// ------------------------------------------------------------------- | |||
uint getWidth() const noexcept | |||
uint getBackgroundColor() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getWidth(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | |||
return fUI->getWidth(); | |||
#endif | |||
} | |||
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0); | |||
uint getHeight() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getHeight(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1); | |||
return fUI->getHeight(); | |||
#endif | |||
return uiData->bgColor; | |||
} | |||
bool isVisible() const noexcept | |||
uint getForegroundColor() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.isVisible(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||
return fUI->isRunning(); | |||
#endif | |||
} | |||
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0xffffffff); | |||
// ------------------------------------------------------------------- | |||
intptr_t getWindowId() const noexcept | |||
{ | |||
#ifdef HAVE_DGL | |||
return glWindow.getWindowId(); | |||
#else | |||
return 0; | |||
#endif | |||
return uiData->fgColor; | |||
} | |||
// ------------------------------------------------------------------- | |||
uint32_t getParameterOffset() const noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); | |||
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0); | |||
return fData->parameterOffset; | |||
return uiData->parameterOffset; | |||
} | |||
// ------------------------------------------------------------------- | |||
void parameterChanged(const uint32_t index, const float value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
fUI->parameterChanged(index, value); | |||
ui->parameterChanged(index, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void programLoaded(const uint32_t index) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
fUI->programLoaded(index); | |||
ui->programLoaded(index); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void stateChanged(const char* const key, const char* const value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||
DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,); | |||
fUI->stateChanged(key, value); | |||
ui->stateChanged(key, value); | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
#ifdef HAVE_DGL | |||
void exec(IdleCallback* const cb) | |||
#if DISTRHO_UI_IS_STANDALONE | |||
void exec(DGL_NAMESPACE::IdleCallback* const cb) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
glWindow.addIdleCallback(cb); | |||
glWindow.setVisible(true); | |||
glApp.exec(); | |||
uiData->window->show(); | |||
uiData->window->focus(); | |||
uiData->app.addIdleCallback(cb); | |||
uiData->app.exec(); | |||
} | |||
void exec_idle() | |||
{ | |||
if (glWindow.isReady()) | |||
fUI->uiIdle(); | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, ); | |||
ui->uiIdle(); | |||
} | |||
#endif | |||
bool idle() | |||
void showAndFocus() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); | |||
uiData->window->show(); | |||
uiData->window->focus(); | |||
} | |||
#endif | |||
#ifdef HAVE_DGL | |||
glApp.idle(); | |||
bool plugin_idle() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false); | |||
if (glWindow.isReady()) | |||
fUI->uiIdle(); | |||
uiData->app.idle(); | |||
ui->uiIdle(); | |||
return ! uiData->app.isQuitting(); | |||
} | |||
return ! glApp.isQuiting(); | |||
#else | |||
return fUI->isRunning(); | |||
#endif | |||
void focus() | |||
{ | |||
uiData->window->focus(); | |||
} | |||
void quit() | |||
{ | |||
#ifdef HAVE_DGL | |||
glWindow.close(); | |||
glApp.quit(); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->terminateAndWaitForProcess(); | |||
#endif | |||
uiData->window->close(); | |||
uiData->app.quit(); | |||
} | |||
// ------------------------------------------------------------------- | |||
void setWindowTitle(const char* const uiTitle) | |||
#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) | |||
void idleForVST3() | |||
{ | |||
#ifdef HAVE_DGL | |||
glWindow.setTitle(uiTitle); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
fUI->setTitle(uiTitle); | |||
#endif | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
uiData->app.triggerIdleCallbacks(); | |||
ui->uiIdle(); | |||
} | |||
#ifdef HAVE_DGL | |||
void setWindowSize(const uint width, const uint height, const bool updateUI = false) | |||
# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,); | |||
uiData->window->addIdleCallback(cb, timerFrequencyInMs); | |||
} | |||
fChangingSize = true; | |||
void removeIdleCallbackForVST3(IdleCallback* const cb) | |||
{ | |||
uiData->window->removeIdleCallback(cb); | |||
} | |||
# endif | |||
#endif | |||
if (updateUI) | |||
fUI->setSize(width, height); | |||
// ------------------------------------------------------------------- | |||
glWindow.setSize(width, height); | |||
void setWindowOffset(const int x, const int y) | |||
{ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// TODO | |||
(void)x; (void)y; | |||
#else | |||
uiData->window->setOffset(x, y); | |||
#endif | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
void setWindowSizeForVST3(const uint width, const uint height) | |||
{ | |||
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
ui->setSize(width, height); | |||
# else | |||
uiData->window->setSizeForVST3(width, height); | |||
# endif | |||
} | |||
#endif | |||
fChangingSize = false; | |||
void setWindowTitle(const char* const uiTitle) | |||
{ | |||
uiData->window->setTitle(uiTitle); | |||
} | |||
void setWindowTransientWinId(const uintptr_t winId) | |||
{ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
ui->setTransientWindowId(winId); | |||
#elif 0 /* TODO */ | |||
glWindow.setTransientWinId(winId); | |||
#else | |||
(void)winId; | |||
#endif | |||
} | |||
bool setWindowVisible(const bool yesNo) | |||
{ | |||
glWindow.setVisible(yesNo); | |||
uiData->window->setVisible(yesNo); | |||
return ! glApp.isQuiting(); | |||
return ! uiData->app.isQuitting(); | |||
} | |||
bool handlePluginKeyboard(const bool press, const uint key) | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
bool handlePluginKeyboardVST2(const bool press, const uint key, const uint16_t mods) | |||
{ | |||
return glWindow.handlePluginKeyboard(press, key); | |||
DGL_NAMESPACE::Widget::KeyboardEvent ev; | |||
ev.mod = mods; | |||
ev.press = press; | |||
ev.key = key; | |||
const bool ret = ui->onKeyboard(ev); | |||
if (! press) | |||
return ret; | |||
DGL_NAMESPACE::Widget::CharacterInputEvent cev; | |||
cev.mod = mods; | |||
cev.character = key; | |||
// if shift modifier is on, convert a-z -> A-Z for character input | |||
if (key >= 'a' && key <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) | |||
cev.character -= 'a' - 'A'; | |||
ui->onCharacterInput(cev); | |||
return ret; | |||
} | |||
bool handlePluginSpecial(const bool press, const Key key) | |||
bool handlePluginKeyboardVST3(const bool press, const uint keychar, const uint keycode, const uint16_t mods) | |||
{ | |||
return glWindow.handlePluginKeyboard(press, key); | |||
DGL_NAMESPACE::Widget::KeyboardEvent ev; | |||
ev.mod = mods; | |||
ev.press = press; | |||
ev.key = keychar; | |||
ev.keycode = keycode; | |||
const bool ret = ui->onKeyboard(ev); | |||
if (! press) | |||
return ret; | |||
DGL_NAMESPACE::Widget::CharacterInputEvent cev; | |||
cev.mod = mods; | |||
cev.keycode = keycode; | |||
cev.character = keychar; | |||
// if shift modifier is on, convert a-z -> A-Z for character input | |||
if (keychar >= 'a' && keychar <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) | |||
cev.character -= 'a' - 'A'; | |||
ui->onCharacterInput(cev); | |||
return ret; | |||
} | |||
#else | |||
void setWindowSize(const uint, const uint, const bool) {} | |||
void setWindowTransientWinId(const uintptr_t) {} | |||
bool setWindowVisible(const bool) { return true; } | |||
#endif | |||
// ------------------------------------------------------------------- | |||
void setSampleRate(const double sampleRate, const bool doCallback = false) | |||
void notifyScaleFactorChanged(const double scaleFactor) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT(sampleRate > 0.0); | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (d_isEqual(fData->sampleRate, sampleRate)) | |||
return; | |||
ui->uiScaleFactorChanged(scaleFactor); | |||
} | |||
fData->sampleRate = sampleRate; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void notifyFocusChanged(const bool focus) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (doCallback) | |||
fUI->sampleRateChanged(sampleRate); | |||
ui->uiFocus(focus, DGL_NAMESPACE::kCrossingNormal); | |||
} | |||
#endif | |||
private: | |||
#ifdef HAVE_DGL | |||
// ------------------------------------------------------------------- | |||
// DGL Application and Window for this widget | |||
Application glApp; | |||
UIExporterWindow glWindow; | |||
void setSampleRate(const double sampleRate, const bool doCallback = false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,); | |||
DISTRHO_SAFE_ASSERT(sampleRate > 0.0); | |||
// prevent recursion | |||
bool fChangingSize; | |||
#endif | |||
if (d_isEqual(uiData->sampleRate, sampleRate)) | |||
return; | |||
// ------------------------------------------------------------------- | |||
// Widget and DistrhoUI data | |||
uiData->sampleRate = sampleRate; | |||
UI* const fUI; | |||
UI::PrivateData* const fData; | |||
if (doCallback) | |||
ui->sampleRateChanged(sampleRate); | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter) | |||
}; | |||
@@ -0,0 +1,500 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED | |||
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED | |||
#include "../DistrhoUI.hpp" | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
# include "DistrhoPluginVST3.hpp" | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../extra/Sleep.hpp" | |||
#else | |||
# include "../../dgl/src/ApplicationPrivateData.hpp" | |||
# include "../../dgl/src/WindowPrivateData.hpp" | |||
# include "../../dgl/src/pugl.hpp" | |||
#endif | |||
#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||
# define DISTRHO_UI_IS_STANDALONE 1 | |||
#else | |||
# define DISTRHO_UI_IS_STANDALONE 0 | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
# define DISTRHO_UI_IS_VST3 1 | |||
#else | |||
# define DISTRHO_UI_IS_VST3 0 | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST2 | |||
# undef DISTRHO_UI_USER_RESIZABLE | |||
# define DISTRHO_UI_USER_RESIZABLE 0 | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
START_NAMESPACE_DISTRHO | |||
#else | |||
START_NAMESPACE_DGL | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Plugin Application, will set class name based on plugin details | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
struct PluginApplication | |||
{ | |||
DGL_NAMESPACE::IdleCallback* idleCallback; | |||
UI* ui; | |||
explicit PluginApplication() | |||
: idleCallback(nullptr), | |||
ui(nullptr) {} | |||
void addIdleCallback(DGL_NAMESPACE::IdleCallback* const cb) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(idleCallback == nullptr,); | |||
idleCallback = cb; | |||
} | |||
bool isQuitting() const noexcept | |||
{ | |||
return ui->isQuitting(); | |||
} | |||
bool isStandalone() const noexcept | |||
{ | |||
return DISTRHO_UI_IS_STANDALONE; | |||
} | |||
void exec() | |||
{ | |||
while (ui->isRunning()) | |||
{ | |||
d_msleep(30); | |||
idleCallback->idleCallback(); | |||
} | |||
if (! ui->isQuitting()) | |||
ui->close(); | |||
} | |||
// these are not needed | |||
void idle() {} | |||
void quit() {} | |||
void triggerIdleCallbacks() {} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | |||
}; | |||
#else | |||
class PluginApplication : public Application | |||
{ | |||
public: | |||
explicit 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); | |||
} | |||
void triggerIdleCallbacks() | |||
{ | |||
pData->triggerIdleCallbacks(); | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | |||
}; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Plugin Window, will pass some Window events to UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
class PluginWindow | |||
{ | |||
UI* const ui; | |||
public: | |||
explicit PluginWindow(UI* const uiPtr, PluginApplication& app) | |||
: ui(uiPtr) | |||
{ | |||
app.ui = ui; | |||
} | |||
// fetch cached data | |||
uint getWidth() const noexcept { return ui->pData.width; } | |||
uint getHeight() const noexcept { return ui->pData.height; } | |||
double getScaleFactor() const noexcept { return ui->pData.scaleFactor; } | |||
// direct mappings | |||
void close() { ui->close(); } | |||
void focus() { ui->focus(); } | |||
void show() { ui->show(); } | |||
bool isResizable() const noexcept { return ui->isResizable(); } | |||
bool isVisible() const noexcept { return ui->isVisible(); } | |||
void setTitle(const char* const title) { ui->setTitle(title); } | |||
void setVisible(const bool visible) { ui->setVisible(visible); } | |||
uintptr_t getNativeWindowHandle() const noexcept { return ui->getNativeWindowHandle(); } | |||
void getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept | |||
{ | |||
minimumWidth = ui->pData.minWidth; | |||
minimumHeight = ui->pData.minHeight; | |||
keepAspectRatio = ui->pData.keepAspectRatio; | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
}; | |||
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
class PluginWindow : public Window | |||
{ | |||
DISTRHO_NAMESPACE::UI* const ui; | |||
bool initializing; | |||
bool receivedReshapeDuringInit; | |||
public: | |||
explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr, | |||
PluginApplication& app, | |||
const uintptr_t parentWindowHandle, | |||
const uint width, | |||
const uint height, | |||
const double scaleFactor) | |||
: Window(app, parentWindowHandle, width, height, scaleFactor, | |||
DISTRHO_UI_USER_RESIZABLE, DISTRHO_UI_IS_VST3, false), | |||
ui(uiPtr), | |||
initializing(true), | |||
receivedReshapeDuringInit(false) | |||
{ | |||
if (pData->view == nullptr) | |||
return; | |||
// this is called just before creating UI, ensuring proper context to it | |||
if (pData->initPost()) | |||
puglBackendEnter(pData->view); | |||
} | |||
~PluginWindow() | |||
{ | |||
if (pData->view != nullptr) | |||
puglBackendLeave(pData->view); | |||
} | |||
// called after creating UI, restoring proper context | |||
void leaveContext() | |||
{ | |||
if (pData->view == nullptr) | |||
return; | |||
if (receivedReshapeDuringInit) | |||
ui->uiReshape(getWidth(), getHeight()); | |||
initializing = false; | |||
puglBackendLeave(pData->view); | |||
} | |||
// used for temporary windows (VST2/3 get size without active/visible view) | |||
void setIgnoreIdleCallbacks(const bool ignore = true) | |||
{ | |||
pData->ignoreIdleCallbacks = ignore; | |||
} | |||
// called right before deleting UI, ensuring correct context | |||
void enterContextForDeletion() | |||
{ | |||
if (pData->view != nullptr) | |||
puglBackendEnter(pData->view); | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
void setSizeForVST3(const uint width, const uint height) | |||
{ | |||
puglSetWindowSize(pData->view, width, height); | |||
} | |||
#endif | |||
protected: | |||
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (initializing) | |||
return; | |||
ui->uiFocus(focus, mode); | |||
} | |||
void onReshape(const uint width, const uint height) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (initializing) | |||
{ | |||
receivedReshapeDuringInit = true; | |||
return; | |||
} | |||
ui->uiReshape(width, height); | |||
} | |||
void onScaleFactorChanged(const double scaleFactor) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (initializing) | |||
return; | |||
ui->uiScaleFactorChanged(scaleFactor); | |||
} | |||
# ifndef DGL_FILE_BROWSER_DISABLED | |||
void onFileSelected(const char* filename) override; | |||
# endif | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
}; | |||
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
END_NAMESPACE_DISTRHO | |||
#else | |||
END_NAMESPACE_DGL | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
START_NAMESPACE_DISTRHO | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
using DGL_NAMESPACE::PluginApplication; | |||
using DGL_NAMESPACE::PluginWindow; | |||
#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 { | |||
// DGL | |||
PluginApplication app; | |||
ScopedPointer<PluginWindow> window; | |||
// DSP | |||
double sampleRate; | |||
uint32_t parameterOffset; | |||
void* dspPtr; | |||
// UI | |||
uint bgColor; | |||
uint fgColor; | |||
double scaleFactor; | |||
uintptr_t winId; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
char* uiStateFileKeyRequest; | |||
#endif | |||
char* bundlePath; | |||
// Ignore initial resize events while initializing | |||
bool initializing; | |||
// Callbacks | |||
void* callbacksPtr; | |||
editParamFunc editParamCallbackFunc; | |||
setParamFunc setParamCallbackFunc; | |||
setStateFunc setStateCallbackFunc; | |||
sendNoteFunc sendNoteCallbackFunc; | |||
setSizeFunc setSizeCallbackFunc; | |||
fileRequestFunc fileRequestCallbackFunc; | |||
PrivateData() noexcept | |||
: app(), | |||
window(nullptr), | |||
sampleRate(0), | |||
parameterOffset(0), | |||
dspPtr(nullptr), | |||
bgColor(0), | |||
fgColor(0xffffffff), | |||
scaleFactor(1.0), | |||
winId(0), | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
uiStateFileKeyRequest(nullptr), | |||
#endif | |||
bundlePath(nullptr), | |||
initializing(true), | |||
callbacksPtr(nullptr), | |||
editParamCallbackFunc(nullptr), | |||
setParamCallbackFunc(nullptr), | |||
setStateCallbackFunc(nullptr), | |||
sendNoteCallbackFunc(nullptr), | |||
setSizeCallbackFunc(nullptr), | |||
fileRequestCallbackFunc(nullptr) | |||
{ | |||
#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_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# endif | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# endif | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
parameterOffset += kVst3InternalParameterCount; | |||
#endif | |||
} | |||
~PrivateData() noexcept | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
std::free(uiStateFileKeyRequest); | |||
#endif | |||
std::free(bundlePath); | |||
} | |||
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); | |||
} | |||
// implemented below, after PluginWindow | |||
bool fileRequestCallback(const char* const key); | |||
static UI::PrivateData* s_nextPrivateData; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
static ExternalWindow::PrivateData createNextWindow(UI* ui, uint width, uint height); | |||
#else | |||
static PluginWindow& createNextWindow(UI* ui, uint width, uint height); | |||
#endif | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// UI private data fileRequestCallback, which requires PluginWindow definitions | |||
inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
{ | |||
if (fileRequestCallbackFunc != nullptr) | |||
return fileRequestCallbackFunc(callbacksPtr, key); | |||
#if DISTRHO_PLUGIN_WANT_STATE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
std::free(uiStateFileKeyRequest); | |||
uiStateFileKeyRequest = strdup(key); | |||
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); | |||
char title[0xff]; | |||
snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key); | |||
title[sizeof(title)-1u] = '\0'; | |||
FileBrowserOptions opts; | |||
opts.title = title; | |||
return window->openFileBrowser(opts); | |||
#endif | |||
return false; | |||
} | |||
END_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// PluginWindow onFileSelected that require UI::PrivateData definitions | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
START_NAMESPACE_DGL | |||
inline void PluginWindow::onFileSelected(const char* const filename) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
if (initializing) | |||
return; | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
if (char* const key = ui->uiData->uiStateFileKeyRequest) | |||
{ | |||
ui->uiData->uiStateFileKeyRequest = nullptr; | |||
if (filename != nullptr) | |||
{ | |||
// notify DSP | |||
ui->setState(key, filename); | |||
// notify UI | |||
ui->stateChanged(key, filename); | |||
} | |||
std::free(key); | |||
return; | |||
} | |||
# endif | |||
ui->uiFileBrowserSelected(filename); | |||
} | |||
END_NAMESPACE_DGL | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED |
@@ -0,0 +1,140 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef DISTRHO_IS_STANDALONE | |||
# error Wrong build configuration | |||
#endif | |||
#include "../extra/String.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# include <windows.h> | |||
#else | |||
# ifndef STATIC_BUILD | |||
# include <dlfcn.h> | |||
# endif | |||
# include <limits.h> | |||
# include <stdlib.h> | |||
#endif | |||
#if defined(DISTRHO_OS_WINDOWS) && !DISTRHO_IS_STANDALONE | |||
static HINSTANCE hInstance = nullptr; | |||
DISTRHO_PLUGIN_EXPORT | |||
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) | |||
{ | |||
if (reason == DLL_PROCESS_ATTACH) | |||
hInstance = hInst; | |||
return 1; | |||
} | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
const char* getBinaryFilename() | |||
{ | |||
static String filename; | |||
if (filename.isNotEmpty()) | |||
return filename; | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# if DISTRHO_IS_STANDALONE | |||
constexpr const HINSTANCE hInstance = nullptr; | |||
# endif | |||
CHAR filenameBuf[MAX_PATH]; | |||
filenameBuf[0] = '\0'; | |||
GetModuleFileNameA(hInstance, filenameBuf, sizeof(filenameBuf)); | |||
filename = filenameBuf; | |||
#elif !defined(STATIC_BUILD) | |||
Dl_info info; | |||
dladdr((void*)getBinaryFilename, &info); | |||
char filenameBuf[PATH_MAX]; | |||
filename = realpath(info.dli_fname, filenameBuf); | |||
#endif | |||
return filename; | |||
} | |||
const char* getPluginFormatName() noexcept | |||
{ | |||
#if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
return "Carla"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
return "JACK/Standalone"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) | |||
return "LADSPA"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||
return "DSSI"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
return "LV2"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
return "VST2"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
return "VST3"; | |||
#else | |||
return "Unknown"; | |||
#endif | |||
} | |||
const char* getResourcePath(const char* const bundlePath) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(bundlePath != nullptr, nullptr); | |||
#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
static String resourcePath; | |||
if (resourcePath.isEmpty()) | |||
{ | |||
resourcePath = bundlePath; | |||
# ifdef DISTRHO_OS_MAC | |||
resourcePath += "/Contents/Resources"; | |||
# else | |||
resourcePath += DISTRHO_OS_SEP_STR "resources"; | |||
# endif | |||
} | |||
return resourcePath.buffer(); | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
static String resourcePath; | |||
if (resourcePath.isEmpty()) | |||
{ | |||
resourcePath = bundlePath; | |||
resourcePath += DISTRHO_OS_SEP_STR "resources"; | |||
} | |||
return resourcePath.buffer(); | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
static String resourcePath; | |||
if (resourcePath.isEmpty()) | |||
{ | |||
resourcePath = bundlePath; | |||
resourcePath += "/Contents/Resources"; | |||
} | |||
return resourcePath.buffer(); | |||
#endif | |||
return nullptr; | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO |
@@ -1 +0,0 @@ | |||
../../../includes/dssi |
@@ -1 +0,0 @@ | |||
../../../includes/ladspa |
@@ -1 +0,0 @@ | |||
../../../includes/lv2 |
@@ -1 +0,0 @@ | |||
../../../includes/vestige |
@@ -21,6 +21,7 @@ | |||
#include "ui_launcher_res.cpp" | |||
#include <cstring> | |||
#include <vector> | |||
#ifdef __WINE__ | |||
__cdecl static intptr_t cvst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) | |||
@@ -130,9 +131,9 @@ intptr_t VSTAudioMaster(AEffect* effect, int32_t opcode, int32_t index, intptr_t | |||
bool isUsingUILauncher() | |||
{ | |||
#ifdef CARLA_OS_LINUX | |||
return false; | |||
#else | |||
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) | |||
return true; | |||
#else | |||
return false; | |||
#endif | |||
} |
@@ -1,6 +1,6 @@ | |||
/* | |||
* Carla Native Plugins | |||
* Copyright (C) 2013-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2013-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU General Public License as | |||
@@ -32,7 +32,7 @@ struct VstObject { | |||
NativePlugin* plugin; | |||
}; | |||
CarlaUILauncher* createUILauncher(intptr_t winId, const NativePluginDescriptor* d, NativePluginHandle h); | |||
CarlaUILauncher* createUILauncher(uintptr_t winId, const NativePluginDescriptor* d, NativePluginHandle h); | |||
void idleUILauncher(CarlaUILauncher* ui); | |||
void destoryUILauncher(CarlaUILauncher* ui); | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* Carla Native Plugin UI launcher | |||
* Copyright (C) 2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2018-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU General Public License as | |||
@@ -15,8 +15,10 @@ | |||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
*/ | |||
#include "dgl/Application.hpp" | |||
#include "dgl/ImageWidgets.hpp" | |||
#include "dgl/OpenGL.hpp" | |||
#include "dgl/src/pugl.hpp" | |||
#include "dgl/src/WindowPrivateData.hpp" | |||
#include "CarlaNative.h" | |||
#include "ui_launcher_res.hpp" | |||
#include "CarlaDefines.h" | |||
@@ -25,24 +27,71 @@ | |||
START_NAMESPACE_DGL | |||
class CarlaButtonWidget : public Widget, | |||
private ImageButton::Callback | |||
class PluginApplication : public Application | |||
{ | |||
public: | |||
explicit PluginApplication() | |||
: Application(false) | |||
{ | |||
setClassName("CarlaPluginWrapper"); | |||
} | |||
}; | |||
class PluginWindow : public Window | |||
{ | |||
public: | |||
explicit PluginWindow(PluginApplication& app, const uintptr_t winId) | |||
: Window(app, winId, ui_launcher_res::carla_uiWidth, ui_launcher_res::carla_uiHeight, 0.0, false, false, false) | |||
{ | |||
// this is called just before creating UI, ensuring proper context to it | |||
if (pData->view != nullptr && pData->initPost()) | |||
puglBackendEnter(pData->view); | |||
} | |||
~PluginWindow() | |||
{ | |||
if (pData->view != nullptr) | |||
puglBackendLeave(pData->view); | |||
} | |||
// called right before deleting UI, ensuring correct context | |||
void enterContextForDeletion() | |||
{ | |||
if (pData->view != nullptr) | |||
puglBackendEnter(pData->view); | |||
} | |||
// called after creating UI, restoring proper context | |||
void leaveContextAfterCreation() | |||
{ | |||
if (pData->view != nullptr) | |||
puglBackendLeave(pData->view); | |||
} | |||
}; | |||
class CarlaButtonWidget : public TopLevelWidget, | |||
private OpenGLImageButton::Callback | |||
{ | |||
public: | |||
CarlaButtonWidget(Window& parent, const NativePluginDescriptor* const d, const NativePluginHandle h) | |||
: Widget(parent), | |||
explicit CarlaButtonWidget(PluginWindow& parent, const NativePluginDescriptor* const d, const NativePluginHandle h) | |||
: TopLevelWidget(parent), | |||
startButtonImage(ui_launcher_res::carla_uiData, | |||
ui_launcher_res::carla_uiWidth, | |||
ui_launcher_res::carla_uiHeight, | |||
GL_BGR), | |||
kImageFormatBGR), | |||
startButton(this, startButtonImage), | |||
descriptor(d), | |||
handle(h) | |||
handle(h), | |||
pluginWindow(parent) | |||
{ | |||
startButton.setCallback(this); | |||
setSize(startButtonImage.getSize()); | |||
parent.setSize(startButtonImage.getSize()); | |||
pluginWindow.leaveContextAfterCreation(); | |||
} | |||
~CarlaButtonWidget() override | |||
{ | |||
pluginWindow.enterContextForDeletion(); | |||
} | |||
protected: | |||
@@ -50,7 +99,7 @@ protected: | |||
{ | |||
} | |||
void imageButtonClicked(ImageButton* imageButton, int) override | |||
void imageButtonClicked(OpenGLImageButton* imageButton, int) override | |||
{ | |||
if (imageButton != &startButton) | |||
return; | |||
@@ -60,10 +109,11 @@ protected: | |||
} | |||
private: | |||
Image startButtonImage; | |||
ImageButton startButton; | |||
OpenGLImage startButtonImage; | |||
OpenGLImageButton startButton; | |||
const NativePluginDescriptor* const descriptor; | |||
const NativePluginHandle handle; | |||
PluginWindow& pluginWindow; | |||
CARLA_DECLARE_NON_COPY_CLASS(CarlaButtonWidget); | |||
}; | |||
@@ -72,18 +122,20 @@ END_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
USE_NAMESPACE_DGL | |||
struct CarlaUILauncher { | |||
DGL_NAMESPACE::Application app; | |||
DGL_NAMESPACE::Window window; | |||
PluginApplication app; | |||
PluginWindow window; | |||
CarlaButtonWidget widget; | |||
CarlaUILauncher(const intptr_t winId, const NativePluginDescriptor* const d, const NativePluginHandle h) | |||
CarlaUILauncher(const uintptr_t winId, const NativePluginDescriptor* const d, const NativePluginHandle h) | |||
: app(), | |||
window(app, winId), | |||
widget(window, d, h) {} | |||
}; | |||
CarlaUILauncher* createUILauncher(const intptr_t winId, | |||
CarlaUILauncher* createUILauncher(const uintptr_t winId, | |||
const NativePluginDescriptor* const d, | |||
const NativePluginHandle h) | |||
{ | |||