@@ -0,0 +1,189 @@ | |||
name: build | |||
on: | |||
push: | |||
branches: | |||
- '*' | |||
pull_request: | |||
branches: | |||
- '*' | |||
env: | |||
DEBIAN_FRONTEND: noninteractive | |||
jobs: | |||
linux-arm64: | |||
runs-on: ubuntu-18.04 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Set up dependencies | |||
run: | | |||
sudo dpkg --add-architecture arm64 && \ | |||
sudo sed -i "s/deb http/deb [arch=amd64] http/" /etc/apt/sources.list && \ | |||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports-arm64.list && \ | |||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list && \ | |||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list && \ | |||
sudo apt-get update -qq && \ | |||
sudo apt-get install -yq g++-aarch64-linux-gnu libasound2-dev:arm64 libcairo2-dev:arm64 libgl1-mesa-dev:arm64 liblo-dev:arm64 libpulse-dev:arm64 qemu-user-static | |||
- name: Build linux arm64 cross-compiled | |||
env: | |||
CC: aarch64-linux-gnu-gcc | |||
CXX: aarch64-linux-gnu-g++ | |||
LDFLAGS: -static-libgcc -static-libstdc++ | |||
run: | | |||
make -j $(nproc) | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-linux-arm64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
bin/* | |||
linux-armhf: | |||
runs-on: ubuntu-18.04 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Set up dependencies | |||
run: | | |||
sudo dpkg --add-architecture armhf && \ | |||
sudo sed -i "s/deb http/deb [arch=amd64] http/" /etc/apt/sources.list && \ | |||
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports-armhf.list && \ | |||
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list && \ | |||
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list && \ | |||
sudo apt-get update -qq && \ | |||
sudo apt-get install -yq g++-arm-linux-gnueabihf libasound2-dev:armhf libcairo2-dev:armhf libgl1-mesa-dev:armhf liblo-dev:armhf libpulse-dev:armhf qemu-user-static | |||
- name: Build linux armhf cross-compiled | |||
env: | |||
CC: arm-linux-gnueabihf-gcc | |||
CXX: arm-linux-gnueabihf-g++ | |||
LDFLAGS: -static-libgcc -static-libstdc++ | |||
run: | | |||
make -j $(nproc) | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-linux-armhf-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
bin/* | |||
linux-x64: | |||
runs-on: ubuntu-18.04 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Set up dependencies | |||
run: | | |||
sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev | |||
- name: Build linux x64 | |||
env: | |||
LDFLAGS: -static-libgcc -static-libstdc++ | |||
run: | | |||
make -j $(nproc) | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-linux-x64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
bin/* | |||
macos-universal: | |||
runs-on: macos-10.15 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Fix up Xcode | |||
run: | | |||
sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/* | |||
sudo xcode-select -s "/Applications/Xcode_12.3.app" | |||
- name: Build macOS universal | |||
env: | |||
CFLAGS: -arch x86_64 -arch arm64 -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_12 -mmacosx-version-min=10.12 -mtune=generic -msse -msse2 | |||
CXXFLAGS: -arch x86_64 -arch arm64 -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_12 -mmacosx-version-min=10.12 -mtune=generic -msse -msse2 | |||
LDFLAGS: -arch x86_64 -arch arm64 -mmacosx-version-min=10.12 | |||
run: | | |||
make NOOPT=true -j $(sysctl -n hw.logicalcpu) && \ | |||
./dpf/utils/package-osx-bundles.sh | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-macOS-universal-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
*-macOS.pkg | |||
bin/* | |||
!bin/*-ladspa.dylib | |||
!bin/*-dssi.dylib | |||
!bin/lv2 | |||
!bin/vst2 | |||
win32: | |||
runs-on: ubuntu-20.04 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Set up dependencies | |||
run: | | |||
sudo dpkg --add-architecture i386 && \ | |||
sudo apt-get update -qq && \ | |||
sudo apt-get install -yq binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 wine-stable:i386 | |||
- name: Build win32 cross-compiled | |||
env: | |||
CC: i686-w64-mingw32-gcc | |||
CXX: i686-w64-mingw32-g++ | |||
EXE_WRAPPER: wine | |||
PKG_CONFIG: "false" | |||
WINEDEBUG: "-all" | |||
run: | | |||
make -j $(nproc) | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-win32-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
bin/* | |||
!bin/*-ladspa.dll | |||
!bin/*-dssi.dll | |||
win64: | |||
runs-on: ubuntu-20.04 | |||
steps: | |||
- uses: actions/checkout@v2 | |||
with: | |||
submodules: recursive | |||
- name: Set up dependencies | |||
run: | | |||
sudo apt-get install -yq binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 wine-stable | |||
- name: Build win64 cross-compiled | |||
env: | |||
CC: x86_64-w64-mingw32-gcc | |||
CXX: x86_64-w64-mingw32-g++ | |||
EXE_WRAPPER: wine | |||
PKG_CONFIG: "false" | |||
WINEDEBUG: "-all" | |||
run: | | |||
make -j $(nproc) | |||
- name: Set sha8 | |||
id: slug | |||
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)" | |||
- uses: actions/upload-artifact@v2 | |||
with: | |||
name: ${{ github.event.repository.name }}-win64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }} | |||
path: | | |||
bin/* | |||
!bin/*-ladspa.dll | |||
!bin/*-dssi.dll |
@@ -1,4 +1,3 @@ | |||
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 | |||
@@ -198,7 +198,7 @@ endif | |||
ifeq ($(WINDOWS),true) | |||
# Always build statically on windows | |||
LINK_FLAGS += -static | |||
LINK_FLAGS += -static -static-libgcc -static-libstdc++ | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -247,6 +247,20 @@ endif | |||
HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | |||
ifeq ($(MACOS),true) | |||
HAVE_RTAUDIO = true | |||
else ifeq ($(WINDOWS),true) | |||
HAVE_RTAUDIO = true | |||
else ifneq ($(HAIKU),true) | |||
HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true) | |||
HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true) | |||
ifeq ($(HAVE_ALSA),true) | |||
HAVE_RTAUDIO = true | |||
else ifeq ($(HAVE_PULSEAUDIO),true) | |||
HAVE_RTAUDIO = true | |||
endif | |||
endif | |||
# backwards compat | |||
HAVE_JACK = true | |||
@@ -357,9 +371,19 @@ endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set optional libraries specific stuff | |||
ifeq ($(HAVE_ALSA),true) | |||
ALSA_FLAGS = $(shell $(PKG_CONFIG) --cflags alsa) | |||
ALSA_LIBS = $(shell $(PKG_CONFIG) --libs alsa) | |||
endif | |||
ifeq ($(HAVE_LIBLO),true) | |||
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo) | |||
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo) | |||
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo) | |||
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo) | |||
endif | |||
ifeq ($(HAVE_PULSEAUDIO),true) | |||
PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple) | |||
PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -7,14 +7,12 @@ | |||
# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | |||
ifeq ($(DPF_CUSTOM_PATH),) | |||
ifeq ($(DPF_PATH),) | |||
ifeq (,$(wildcard ../../Makefile.base.mk)) | |||
DPF_PATH=../../dpf | |||
else | |||
DPF_PATH=../.. | |||
endif | |||
else | |||
DPF_PATH = $(DPF_CUSTOM_PATH) | |||
endif | |||
include $(DPF_PATH)/Makefile.base.mk | |||
@@ -22,31 +20,49 @@ include $(DPF_PATH)/Makefile.base.mk | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Basic setup | |||
ifeq ($(DPF_CUSTOM_PATH),) | |||
ifeq ($(DPF_TARGET_DIR),) | |||
TARGET_DIR = ../../bin | |||
BUILD_DIR = ../../build/$(NAME) | |||
else | |||
ifeq ($(DPF_CUSTOM_TARGET_DIR),) | |||
$(error DPF_CUSTOM_TARGET_DIR is not set) | |||
else | |||
TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR) | |||
TARGET_DIR = $(DPF_TARGET_DIR) | |||
endif | |||
ifeq ($(DPF_CUSTOM_BUILD_DIR),) | |||
$(error DPF_CUSTOM_BUILD_DIR is not set) | |||
ifeq ($(DPF_BUILD_DIR),) | |||
BUILD_DIR = ../../build/$(NAME) | |||
else | |||
BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR) | |||
endif | |||
BUILD_DIR = $(DPF_BUILD_DIR) | |||
endif | |||
BUILD_C_FLAGS += -I. | |||
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | |||
ifeq ($(HAVE_ALSA),true) | |||
BASE_FLAGS += -DHAVE_ALSA | |||
endif | |||
ifeq ($(HAVE_LIBLO),true) | |||
BASE_FLAGS += -DHAVE_LIBLO | |||
endif | |||
ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) | |||
JACK_LIBS = -ldl | |||
ifeq ($(HAVE_PULSEAUDIO),true) | |||
BASE_FLAGS += -DHAVE_PULSEAUDIO | |||
endif | |||
ifeq ($(MACOS),true) | |||
JACK_LIBS += -framework CoreAudio -framework CoreFoundation | |||
else ifeq ($(WINDOWS),true) | |||
JACK_LIBS += -lksuser -lmfplat -lmfuuid -lole32 -lwinmm -lwmcodecdspuuid | |||
else ifneq ($(HAIKU),true) | |||
JACK_LIBS = -ldl | |||
ifeq ($(HAVE_ALSA),true) | |||
JACK_FLAGS += $(ALSA_FLAGS) | |||
JACK_LIBS += $(ALSA_LIBS) | |||
endif | |||
ifeq ($(HAVE_PULSEAUDIO),true) | |||
JACK_FLAGS += $(PULSEAUDIO_FLAGS) | |||
JACK_LIBS += $(PULSEAUDIO_LIBS) | |||
endif | |||
ifeq ($(HAVE_RTAUDIO),true) | |||
JACK_LIBS += -lpthread | |||
endif # !HAIKU | |||
endif | |||
# backwards compat | |||
@@ -257,7 +273,7 @@ $(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm | |||
$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | |||
-@mkdir -p $(BUILD_DIR) | |||
@echo "Compiling DistrhoPluginMain.cpp (JACK)" | |||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@ | |||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@ | |||
$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | |||
-@mkdir -p $(BUILD_DIR) | |||
@@ -187,6 +187,13 @@ function(dpf__build_jack NAME DGL_LIBRARY) | |||
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | |||
target_link_libraries("${NAME}-jack" PRIVATE "dl") | |||
endif() | |||
# for RtAudio native fallback | |||
if(APPLE) | |||
find_library(APPLE_COREAUDIO_FRAMEWORK "CoreAudio") | |||
find_library(APPLE_COREFOUNDATION_FRAMEWORK "CoreFoundation") | |||
target_link_libraries("${NAME}-jack" PRIVATE "${APPLE_COREAUDIO_FRAMEWORK}" "${APPLE_COREFOUNDATION_FRAMEWORK}") | |||
endif() | |||
endfunction() | |||
# dpf__build_ladspa | |||
@@ -326,6 +333,7 @@ function(dpf__add_dgl_cairo) | |||
"${DPF_ROOT_DIR}/dgl/src/Application.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/Color.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" | |||
@@ -386,6 +394,7 @@ function(dpf__add_dgl_opengl) | |||
"${DPF_ROOT_DIR}/dgl/src/Application.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/Color.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | |||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" | |||
@@ -30,6 +30,8 @@ START_NAMESPACE_DGL | |||
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. | |||
Unless stated otherwise, functions within this class are not thread-safe. | |||
*/ | |||
class Application | |||
{ | |||
@@ -61,6 +63,7 @@ public: | |||
/** | |||
Quit the application. | |||
This stops the event-loop and closes all Windows. | |||
This function is thread-safe. | |||
@note This function is meant for standalones only, *never* call this from plugins. | |||
*/ | |||
void quit(); | |||
@@ -68,14 +71,21 @@ public: | |||
/** | |||
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 isQuiting() const noexcept; | |||
/** | |||
Check if the application is standalone, otherwise running as a module or plugin. | |||
This function is thread-safe. | |||
*/ | |||
bool isStandalone() const noexcept; | |||
/** | |||
Add a callback function to be triggered on every idle cycle. | |||
You can add more than one, and remove them at anytime with removeIdleCallback(). | |||
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle). | |||
There are no guarantees in terms of timing. | |||
There are no guarantees in terms of timing, use Window::addIdleCallback for time-relative callbacks. | |||
*/ | |||
void addIdleCallback(IdleCallback* callback); | |||
@@ -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. | |||
@@ -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_EVENT_HANDLERS_HPP_INCLUDED | |||
#define DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
#include "Widget.hpp" | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
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); | |||
~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); | |||
~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; | |||
DISTRHO_LEAK_DETECTOR(KnobEventHandler) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
@@ -17,6 +17,7 @@ | |||
#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | |||
#include "EventHandlers.hpp" | |||
#include "StandaloneWindow.hpp" | |||
#include "SubWidget.hpp" | |||
@@ -47,7 +48,8 @@ private: | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
class ImageBaseButton : public SubWidget | |||
class ImageBaseButton : public SubWidget, | |||
public ButtonEventHandler | |||
{ | |||
public: | |||
class Callback | |||
@@ -80,14 +82,10 @@ private: | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
class ImageBaseKnob : public SubWidget | |||
class ImageBaseKnob : public SubWidget, | |||
public KnobEventHandler | |||
{ | |||
public: | |||
enum Orientation { | |||
Horizontal, | |||
Vertical | |||
}; | |||
class Callback | |||
{ | |||
public: | |||
@@ -102,25 +100,16 @@ public: | |||
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | |||
~ImageBaseKnob() override; | |||
float getValue() const noexcept; | |||
void setDefault(float def) noexcept; | |||
void setRange(float min, float max) noexcept; | |||
void setStep(float step) noexcept; | |||
void setValue(float value, bool sendCallback = false) noexcept; | |||
void setUsingLogScale(bool yesNo) noexcept; | |||
void setCallback(Callback* callback) noexcept; | |||
void setOrientation(Orientation orientation) noexcept; | |||
void setRotationAngle(int angle); | |||
void setImageLayerCount(uint count) noexcept; | |||
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; | |||
void onDisplay() override; | |||
bool onMouse(const MouseEvent&) override; | |||
bool onMotion(const MotionEvent&) override; | |||
bool onScroll(const ScrollEvent&) override; | |||
private: | |||
struct PrivateData; | |||
@@ -27,6 +27,7 @@ OBJS_common = \ | |||
../build/dgl/Application.cpp.o \ | |||
../build/dgl/ApplicationPrivateData.cpp.o \ | |||
../build/dgl/Color.cpp.o \ | |||
../build/dgl/EventHandlers.cpp.o \ | |||
../build/dgl/Geometry.cpp.o \ | |||
../build/dgl/ImageBase.cpp.o \ | |||
../build/dgl/ImageBaseWidgets.cpp.o \ | |||
@@ -39,7 +40,6 @@ OBJS_common = \ | |||
../build/dgl/WidgetPrivateData.cpp.o \ | |||
../build/dgl/Window.cpp.o \ | |||
../build/dgl/WindowPrivateData.cpp.o | |||
# ../build/dgl/WindowFileBrowser.cpp.o | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -111,6 +111,22 @@ public: | |||
*/ | |||
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. | |||
*/ | |||
@@ -66,7 +66,7 @@ public: | |||
uint flags; | |||
uint time; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | |||
/** Destuctor */ | |||
virtual ~BaseEvent() noexcept {} | |||
@@ -96,7 +96,7 @@ public: | |||
uint key; | |||
uint keycode; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
KeyboardEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
@@ -118,7 +118,7 @@ public: | |||
bool press; | |||
Key key; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
SpecialEvent() noexcept | |||
: BaseEvent(), | |||
press(false), | |||
@@ -145,7 +145,7 @@ public: | |||
uint character; | |||
char string[8]; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
CharacterInputEvent() noexcept | |||
: BaseEvent(), | |||
keycode(0), | |||
@@ -168,7 +168,7 @@ public: | |||
Point<double> pos; | |||
Point<double> absolutePos; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
MouseEvent() noexcept | |||
: BaseEvent(), | |||
button(0), | |||
@@ -188,7 +188,7 @@ public: | |||
Point<double> pos; | |||
Point<double> absolutePos; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
MotionEvent() noexcept | |||
: BaseEvent(), | |||
pos(0.0, 0.0), | |||
@@ -216,7 +216,7 @@ public: | |||
Point<double> delta; | |||
ScrollDirection direction; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
ScrollEvent() noexcept | |||
: BaseEvent(), | |||
pos(0.0, 0.0), | |||
@@ -235,7 +235,7 @@ public: | |||
Size<uint> size; | |||
Size<uint> oldSize; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
ResizeEvent() noexcept | |||
: size(0, 0), | |||
oldSize(0, 0) {} | |||
@@ -251,7 +251,7 @@ public: | |||
Point<int> pos; | |||
Point<int> oldPos; | |||
/** Constuctor */ | |||
/** Constructor */ | |||
PositionChangedEvent() noexcept | |||
: pos(0, 0), | |||
oldPos(0, 0) {} | |||
@@ -87,14 +87,14 @@ public: | |||
/** Whether to show list of places (bookmarks) */ | |||
ButtonState showPlaces; | |||
/** Constuctor for default values */ | |||
/** Constructor for default values */ | |||
Buttons() | |||
: listAllFiles(kButtonVisibleChecked), | |||
showHidden(kButtonVisibleUnchecked), | |||
showPlaces(kButtonVisibleUnchecked) {} | |||
} buttons; | |||
/** Constuctor for default values */ | |||
/** Constructor for default values */ | |||
FileBrowserOptions() | |||
: startDir(nullptr), | |||
title(nullptr), | |||
@@ -104,6 +104,43 @@ public: | |||
}; | |||
#endif // DGL_FILE_BROWSER_DISABLED | |||
/** | |||
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 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. | |||
We must always cleanly enter and leave the OpenGL context. | |||
In order to avoid messing up the global host context, this class is used around widget creation. | |||
*/ | |||
class ScopedGraphicsContext | |||
{ | |||
Window& window; | |||
public: | |||
explicit ScopedGraphicsContext(Window& window); | |||
~ScopedGraphicsContext(); | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopedGraphicsContext) | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
}; | |||
/** | |||
Constructor for a regular, standalone window. | |||
*/ | |||
@@ -362,9 +399,6 @@ public: | |||
DISTRHO_DEPRECATED_BY("runAsModal(bool)") | |||
inline void exec(bool blockWait = false) { runAsModal(blockWait); } | |||
// TESTING, DO NOT USE | |||
void leaveContext(); | |||
protected: | |||
/** | |||
A function called when the window is attempted to be closed. | |||
@@ -414,6 +448,7 @@ private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
friend class Application; | |||
friend class PluginWindow; | |||
friend class TopLevelWidget; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); | |||
@@ -423,15 +458,4 @@ private: | |||
END_NAMESPACE_DGL | |||
/* TODO | |||
* add eventcrossing/enter-leave event | |||
*/ | |||
#if 0 | |||
protected: | |||
bool handlePluginKeyboard(const bool press, const uint key); | |||
bool handlePluginSpecial(const bool press, const Key key); | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
#endif // DGL_WINDOW_HPP_INCLUDED |
@@ -53,6 +53,11 @@ bool Application::isQuiting() const noexcept | |||
return pData->isQuitting || pData->isQuittingInNextCycle; | |||
} | |||
bool Application::isStandalone() const noexcept | |||
{ | |||
return pData->isStandalone; | |||
} | |||
void Application::addIdleCallback(IdleCallback* const callback) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | |||
@@ -25,7 +25,7 @@ START_NAMESPACE_DGL | |||
typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator; | |||
static ThreadHandle getCurrentThreadHandle() noexcept | |||
static d_ThreadHandle getCurrentThreadHandle() noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
return GetCurrentThread(); | |||
@@ -34,7 +34,7 @@ static ThreadHandle getCurrentThreadHandle() noexcept | |||
#endif | |||
} | |||
static bool isThisMainThread(const ThreadHandle mainThreadHandle) noexcept | |||
static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ? | |||
@@ -127,7 +127,7 @@ void Application::PrivateData::quit() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); | |||
if (! isThisMainThread(mainThreadHandle)) | |||
if (! isThisTheMainThread(mainThreadHandle)) | |||
{ | |||
if (! isQuittingInNextCycle) | |||
{ | |||
@@ -24,10 +24,10 @@ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
typedef HANDLE ThreadHandle; | |||
typedef HANDLE d_ThreadHandle; | |||
#else | |||
# include <pthread.h> | |||
typedef pthread_t ThreadHandle; | |||
typedef pthread_t d_ThreadHandle; | |||
#endif | |||
typedef struct PuglWorldImpl PuglWorld; | |||
@@ -59,7 +59,7 @@ struct Application::PrivateData { | |||
uint visibleWindows; | |||
/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */ | |||
ThreadHandle mainThreadHandle; | |||
d_ThreadHandle mainThreadHandle; | |||
/** List of windows for this application. Only used during `close`. */ | |||
std::list<DGL_NAMESPACE::Window*> windows; | |||
@@ -24,7 +24,6 @@ | |||
#include "../Color.hpp" | |||
#include "../ImageBaseWidgets.hpp" | |||
#include "Common.hpp" | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
@@ -657,8 +656,7 @@ void ImageBaseKnob<CairoImage>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||
/ (pData->maximum - pData->minimum); | |||
const double normValue = getNormalizedValue(); | |||
cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | |||
@@ -114,6 +114,13 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept | |||
interpolate(color2, u); | |||
} | |||
Color Color::withAlpha(const float alpha) noexcept | |||
{ | |||
Color color(*this); | |||
color.alpha = alpha; | |||
return color; | |||
} | |||
Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | |||
{ | |||
float m1, m2; | |||
@@ -1,188 +0,0 @@ | |||
/* | |||
* 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_COMMON_HPP_INCLUDED | |||
#define DGL_COMMON_HPP_INCLUDED | |||
#include "../ImageBaseWidgets.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ButtonImpl { | |||
enum State { | |||
kStateNormal = 0, | |||
kStateHover, | |||
kStateDown | |||
}; | |||
int button; | |||
int state; | |||
ImageBaseButton<ImageType>* const self; | |||
typename ImageBaseButton<ImageType>::Callback* callback_img; | |||
explicit ButtonImpl(ImageBaseButton<ImageType>* 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(self, button2); | |||
return true; | |||
} | |||
// button was pressed, wait for release | |||
if (ev.press && self->contains(ev.pos)) | |||
{ | |||
button = static_cast<int>(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_COPYABLE(ButtonImpl) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseKnob<ImageType>::PrivateData { | |||
ImageType image; | |||
float minimum; | |||
float maximum; | |||
float step; | |||
float value; | |||
float valueDef; | |||
float valueTmp; | |||
bool usingDefault; | |||
bool usingLog; | |||
Orientation orientation; | |||
int rotationAngle; | |||
bool dragging; | |||
double lastX; | |||
double lastY; | |||
Callback* callback; | |||
bool alwaysRepaint; | |||
bool isImgVertical; | |||
uint imgLayerWidth; | |||
uint imgLayerHeight; | |||
uint imgLayerCount; | |||
bool isReady; | |||
union { | |||
uint glTextureId; | |||
void* cairoSurface; | |||
}; | |||
explicit PrivateData(const ImageType& img, const Orientation o); | |||
explicit PrivateData(PrivateData* const other); | |||
void assignFrom(PrivateData* const other); | |||
~PrivateData() | |||
{ | |||
cleanup(); | |||
} | |||
void init(); | |||
void cleanup(); | |||
inline float logscale(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; | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
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 |
@@ -16,7 +16,6 @@ | |||
#include "../ImageBaseWidgets.hpp" | |||
#include "../Color.hpp" | |||
#include "Common.hpp" | |||
START_NAMESPACE_DGL | |||
@@ -89,18 +88,25 @@ bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
struct ImageBaseButton<ImageType>::PrivateData { | |||
ButtonImpl<ImageType> impl; | |||
struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { | |||
ImageBaseButton<ImageType>::Callback* callback; | |||
ImageType imageNormal; | |||
ImageType imageHover; | |||
ImageType imageDown; | |||
PrivateData(ImageBaseButton<ImageType>* const s, const ImageType& normal, const ImageType& hover, const ImageType& down) | |||
: impl(s), | |||
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) | |||
}; | |||
@@ -109,28 +115,34 @@ struct ImageBaseButton<ImageType>::PrivateData { | |||
template <class ImageType> | |||
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) | |||
: SubWidget(parentWidget), | |||
pData(new PrivateData(this, image, image, image)) | |||
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), | |||
pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) | |||
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), | |||
pData(new PrivateData(this, imageNormal, imageHover, imageDown)) | |||
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()); | |||
} | |||
@@ -143,7 +155,7 @@ ImageBaseButton<ImageType>::~ImageBaseButton() | |||
template <class ImageType> | |||
void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | |||
{ | |||
pData->impl.callback_img = callback; | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
@@ -151,137 +163,162 @@ void ImageBaseButton<ImageType>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
switch (pData->impl.state) | |||
{ | |||
case ButtonImpl<ImageType>::kStateDown: | |||
const State state = ButtonEventHandler::getState(); | |||
if (state & kButtonStateActive) | |||
pData->imageDown.draw(context); | |||
break; | |||
case ButtonImpl<ImageType>::kStateHover: | |||
else if (state & kButtonStateHover) | |||
pData->imageHover.draw(context); | |||
break; | |||
default: | |||
else | |||
pData->imageNormal.draw(context); | |||
break; | |||
} | |||
} | |||
template <class ImageType> | |||
bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
return pData->impl.onMouse(ev); | |||
if (SubWidget::onMouse(ev)) | |||
return true; | |||
return ButtonEventHandler::mouseEvent(ev); | |||
} | |||
template <class ImageType> | |||
bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | |||
{ | |||
return pData->impl.onMotion(ev); | |||
if (SubWidget::onMotion(ev)) | |||
return true; | |||
return ButtonEventHandler::motionEvent(ev); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>::PrivateData::PrivateData(const ImageType& img, const Orientation o) | |||
: image(img), | |||
minimum(0.0f), | |||
maximum(1.0f), | |||
step(0.0f), | |||
value(0.5f), | |||
valueDef(value), | |||
valueTmp(value), | |||
usingDefault(false), | |||
usingLog(false), | |||
orientation(o), | |||
rotationAngle(0), | |||
dragging(false), | |||
lastX(0.0), | |||
lastY(0.0), | |||
callback(nullptr), | |||
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(); | |||
} | |||
struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback { | |||
ImageBaseKnob<ImageType>::Callback* callback; | |||
ImageType image; | |||
template <class ImageType> | |||
ImageBaseKnob<ImageType>::PrivateData::PrivateData(PrivateData* const other) | |||
: image(other->image), | |||
minimum(other->minimum), | |||
maximum(other->maximum), | |||
step(other->step), | |||
value(other->value), | |||
valueDef(other->valueDef), | |||
valueTmp(value), | |||
usingDefault(other->usingDefault), | |||
usingLog(other->usingLog), | |||
orientation(other->orientation), | |||
rotationAngle(other->rotationAngle), | |||
dragging(false), | |||
lastX(0.0), | |||
lastY(0.0), | |||
callback(other->callback), | |||
alwaysRepaint(other->alwaysRepaint), | |||
isImgVertical(other->isImgVertical), | |||
imgLayerWidth(other->imgLayerWidth), | |||
imgLayerHeight(other->imgLayerHeight), | |||
imgLayerCount(other->imgLayerCount), | |||
isReady(false) | |||
{ | |||
init(); | |||
} | |||
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(); | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::PrivateData::assignFrom(PrivateData* const other) | |||
{ | |||
cleanup(); | |||
image = other->image; | |||
minimum = other->minimum; | |||
maximum = other->maximum; | |||
step = other->step; | |||
value = other->value; | |||
valueDef = other->valueDef; | |||
valueTmp = value; | |||
usingDefault = other->usingDefault; | |||
usingLog = other->usingLog; | |||
orientation = other->orientation; | |||
rotationAngle = other->rotationAngle; | |||
dragging = false; | |||
lastX = 0.0; | |||
lastY = 0.0; | |||
callback = other->callback; | |||
alwaysRepaint = other->alwaysRepaint; | |||
isImgVertical = other->isImgVertical; | |||
imgLayerWidth = other->imgLayerWidth; | |||
imgLayerHeight = other->imgLayerHeight; | |||
imgLayerCount = other->imgLayerCount; | |||
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 | |||
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, | |||
const ImageType& image, | |||
const Orientation orientation) noexcept | |||
: SubWidget(parentWidget), | |||
pData(new PrivateData(image, orientation)) | |||
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; | |||
@@ -293,116 +330,12 @@ ImageBaseKnob<ImageType>::~ImageBaseKnob() | |||
delete pData; | |||
} | |||
template <class ImageType> | |||
float ImageBaseKnob<ImageType>::getValue() const noexcept | |||
{ | |||
return pData->value; | |||
} | |||
// NOTE: value is assumed to be scaled if using log | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setDefault(float value) noexcept | |||
{ | |||
pData->valueDef = value; | |||
pData->usingDefault = true; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setRange(float min, float max) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(max > min,); | |||
if (pData->value < min) | |||
{ | |||
pData->value = min; | |||
repaint(); | |||
if (pData->callback != nullptr) | |||
{ | |||
try { | |||
pData->callback->imageKnobValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setRange < min"); | |||
} | |||
} | |||
else if (pData->value > max) | |||
{ | |||
pData->value = max; | |||
repaint(); | |||
if (pData->callback != nullptr) | |||
{ | |||
try { | |||
pData->callback->imageKnobValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setRange > max"); | |||
} | |||
} | |||
pData->minimum = min; | |||
pData->maximum = max; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setStep(float step) noexcept | |||
{ | |||
pData->step = step; | |||
} | |||
// NOTE: value is assumed to be scaled if using log | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept | |||
{ | |||
if (d_isEqual(pData->value, value)) | |||
return; | |||
pData->value = value; | |||
if (d_isZero(pData->step)) | |||
pData->valueTmp = value; | |||
if (pData->rotationAngle == 0 || pData->alwaysRepaint) | |||
pData->isReady = false; | |||
repaint(); | |||
if (sendCallback && pData->callback != nullptr) | |||
{ | |||
try { | |||
pData->callback->imageKnobValueChanged(this, pData->value); | |||
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setValue"); | |||
} | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setUsingLogScale(bool yesNo) noexcept | |||
{ | |||
pData->usingLog = yesNo; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | |||
{ | |||
pData->callback = callback; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setOrientation(Orientation orientation) noexcept | |||
{ | |||
if (pData->orientation == orientation) | |||
return; | |||
pData->orientation = orientation; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setRotationAngle(int angle) | |||
{ | |||
if (pData->rotationAngle == angle) | |||
return; | |||
pData->rotationAngle = angle; | |||
pData->isReady = false; | |||
} | |||
template <class ImageType> | |||
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |||
{ | |||
@@ -419,38 +352,23 @@ void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||
void ImageBaseKnob<ImageType>::setRotationAngle(int angle) | |||
{ | |||
if (ev.button != 1) | |||
return false; | |||
if (ev.press) | |||
{ | |||
if (! contains(ev.pos)) | |||
return false; | |||
if ((ev.mod & kModifierShift) != 0 && pData->usingDefault) | |||
{ | |||
setValue(pData->valueDef, true); | |||
pData->valueTmp = pData->value; | |||
return true; | |||
} | |||
pData->dragging = true; | |||
pData->lastX = ev.pos.getX(); | |||
pData->lastY = ev.pos.getY(); | |||
if (pData->rotationAngle == angle) | |||
return; | |||
if (pData->callback != nullptr) | |||
pData->callback->imageKnobDragStarted(this); | |||
pData->rotationAngle = angle; | |||
pData->isReady = false; | |||
} | |||
return true; | |||
} | |||
else if (pData->dragging) | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept | |||
{ | |||
if (KnobEventHandler::setValue(value, sendCallback)) | |||
{ | |||
if (pData->callback != nullptr) | |||
pData->callback->imageKnobDragFinished(this); | |||
if (pData->rotationAngle == 0 || pData->alwaysRepaint) | |||
pData->isReady = false; | |||
pData->dragging = false; | |||
return true; | |||
} | |||
@@ -458,93 +376,27 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||
} | |||
template <class ImageType> | |||
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | |||
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (! pData->dragging) | |||
return false; | |||
bool doVal = false; | |||
float d, value = 0.0f; | |||
if (pData->orientation == ImageBaseKnob<ImageType>::Horizontal) | |||
{ | |||
if (const double movX = ev.pos.getX() - pData->lastX) | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movX)); | |||
doVal = true; | |||
} | |||
} | |||
else if (pData->orientation == ImageBaseKnob<ImageType>::Vertical) | |||
{ | |||
if (const double movY = pData->lastY - ev.pos.getY()) | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movY)); | |||
doVal = true; | |||
} | |||
} | |||
if (! doVal) | |||
return false; | |||
if (pData->usingLog) | |||
value = pData->logscale(value); | |||
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); | |||
pData->lastX = ev.pos.getX(); | |||
pData->lastY = ev.pos.getY(); | |||
if (SubWidget::onMouse(ev)) | |||
return true; | |||
return KnobEventHandler::mouseEvent(ev); | |||
} | |||
return true; | |||
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 (! 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 value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) | |||
+ ((pData->maximum - pData->minimum) / d * 10.f * dir); | |||
if (pData->usingLog) | |||
value = pData->logscale(value); | |||
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); | |||
return true; | |||
if (SubWidget::onScroll(ev)) | |||
return true; | |||
return KnobEventHandler::scrollEvent(ev); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -287,7 +287,10 @@ void NanoVG::beginFrame(Widget* const widget) | |||
return; | |||
if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) | |||
nvgBeginFrame(fContext, static_cast<int>(tlw->getWidth()), static_cast<int>(tlw->getHeight()), 1.0f); | |||
nvgBeginFrame(fContext, | |||
static_cast<int>(tlw->getWidth()), | |||
static_cast<int>(tlw->getHeight()), | |||
tlw->getScaleFactor()); | |||
} | |||
void NanoVG::cancelFrame() | |||
@@ -23,7 +23,6 @@ | |||
#include "../Color.hpp" | |||
#include "../ImageWidgets.hpp" | |||
#include "Common.hpp" | |||
#include "SubWidgetPrivateData.hpp" | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "WidgetPrivateData.hpp" | |||
@@ -317,6 +316,8 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, con | |||
setupCalled = true; | |||
} | |||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, textureId); | |||
@@ -486,8 +487,7 @@ template <> | |||
void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||
/ (pData->maximum - pData->minimum); | |||
const float normValue = getNormalizedValue(); | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||
@@ -32,9 +32,9 @@ SubWidget::~SubWidget() | |||
} | |||
template<typename T> | |||
bool SubWidget::contains(T x, T y) const noexcept | |||
bool SubWidget::contains(const T x, const T y) const noexcept | |||
{ | |||
return Rectangle<double>(0, 0, getWidth(), getHeight()).contains(x, y); | |||
return Rectangle<double>(0, 0, getWidth()-pData->margin.getX(), getHeight()-pData->margin.getY()).contains(x, y); | |||
} | |||
template<typename T> | |||
@@ -70,17 +70,17 @@ Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept | |||
getSize()); | |||
} | |||
void SubWidget::setAbsoluteX(int x) noexcept | |||
void SubWidget::setAbsoluteX(const int x) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(x, getAbsoluteY())); | |||
} | |||
void SubWidget::setAbsoluteY(int y) noexcept | |||
void SubWidget::setAbsoluteY(const int y) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(getAbsoluteX(), y)); | |||
} | |||
void SubWidget::setAbsolutePos(int x, int y) noexcept | |||
void SubWidget::setAbsolutePos(const int x, const int y) noexcept | |||
{ | |||
setAbsolutePos(Point<int>(x, y)); | |||
} | |||
@@ -100,6 +100,21 @@ void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept | |||
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; | |||
@@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) | |||
selfw((Widget*)s), | |||
parentWidget(pw), | |||
absolutePos(), | |||
margin(), | |||
needsFullViewportForDrawing(false), | |||
needsViewportScaling(false), | |||
skipDrawing(false), | |||
@@ -28,6 +28,7 @@ struct SubWidget::PrivateData { | |||
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 | |||
@@ -152,8 +152,8 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||
y - widget->getAbsoluteY()); | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onMouse(ev)) | |||
return true; | |||
@@ -191,8 +191,8 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||
y - widget->getAbsoluteY()); | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onMotion(ev)) | |||
return true; | |||
@@ -230,8 +230,8 @@ bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) | |||
if (! widget->isVisible()) | |||
continue; | |||
ev.pos = Point<double>(x - widget->getAbsoluteX(), | |||
y - widget->getAbsoluteY()); | |||
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(), | |||
y - widget->getAbsoluteY() + widget->getMargin().getY()); | |||
if (widget->onScroll(ev)) | |||
return true; | |||
@@ -20,6 +20,20 @@ | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// ScopedGraphicsContext | |||
Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win) | |||
: window(win) | |||
{ | |||
puglBackendEnter(window.pData->view); | |||
} | |||
Window::ScopedGraphicsContext::~ScopedGraphicsContext() | |||
{ | |||
puglBackendLeave(window.pData->view); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Window | |||
@@ -265,11 +279,6 @@ void Window::runAsModal(bool blockWait) | |||
pData->runAsModal(blockWait); | |||
} | |||
void Window::leaveContext() | |||
{ | |||
pData->leaveContext(); | |||
} | |||
void Window::setGeometryConstraints(const uint minimumWidth, | |||
const uint minimumHeight, | |||
const bool keepAspectRatio, | |||
@@ -79,6 +79,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(nullptr), | |||
topLevelWidgets(), | |||
isClosed(true), | |||
isVisible(false), | |||
@@ -102,6 +103,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(ppData->view), | |||
topLevelWidgets(), | |||
isClosed(true), | |||
isVisible(false), | |||
@@ -117,9 +119,10 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||
#endif | |||
modal(ppData) | |||
{ | |||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | |||
puglBackendLeave(transientParentView); | |||
puglSetTransientFor(view, puglGetNativeWindow(transientParentView)); | |||
puglSetTransientFor(view, puglGetNativeWindow(ppData->view)); | |||
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); | |||
} | |||
Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
@@ -129,6 +132,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(nullptr), | |||
topLevelWidgets(), | |||
isClosed(parentWindowHandle == 0), | |||
isVisible(parentWindowHandle != 0), | |||
@@ -158,6 +162,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
appData(a.pData), | |||
self(s), | |||
view(puglNewView(appData->world)), | |||
transientParentView(nullptr), | |||
topLevelWidgets(), | |||
isClosed(parentWindowHandle == 0), | |||
isVisible(parentWindowHandle != 0), | |||
@@ -237,14 +242,15 @@ void Window::PrivateData::initPost() | |||
// create view now, as a few methods we allow devs to use require it | |||
puglRealize(view); | |||
// FIXME this is bad, the enter/leave should be well scoped. try to find a better place for it.. | |||
puglBackendEnter(view); | |||
if (isEmbed) | |||
{ | |||
appData->oneWindowShown(); | |||
puglShow(view); | |||
} | |||
// give context back to transient parent window | |||
if (transientParentView != nullptr) | |||
puglBackendEnter(transientParentView); | |||
} | |||
// ----------------------------------------------------------------------- | |||
@@ -630,14 +636,6 @@ void Window::PrivateData::runAsModal(const bool blockWait) | |||
} | |||
} | |||
// ----------------------------------------------------------------------- | |||
// TESTING | |||
void Window::PrivateData::leaveContext() | |||
{ | |||
puglBackendLeave(view); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// pugl events | |||
@@ -44,6 +44,9 @@ struct Window::PrivateData : IdleCallback { | |||
/** Pugl view instance. */ | |||
PuglView* const view; | |||
/** Pugl view instance of the transient parent window. */ | |||
PuglView* const transientParentView; | |||
/** Reserved space for graphics context. */ | |||
mutable uint8_t graphicsContext[sizeof(void*)]; | |||
@@ -161,9 +164,6 @@ struct Window::PrivateData : IdleCallback { | |||
void stopModal(); | |||
void runAsModal(bool blockWait); | |||
// TESTING | |||
void leaveContext(); | |||
// pugl events | |||
void onPuglConfigure(double width, double height); | |||
void onPuglExpose(); | |||
@@ -118,8 +118,7 @@ puglWinGetWindowExFlags(const PuglView* const view) | |||
} | |||
PuglWorldInternals* | |||
puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type), | |||
PuglWorldFlags PUGL_UNUSED(flags)) | |||
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) | |||
{ | |||
PuglWorldInternals* impl = | |||
(PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); | |||
@@ -127,15 +126,17 @@ puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type), | |||
return NULL; | |||
} | |||
HMODULE user32 = LoadLibrary("user32.dll"); | |||
if (user32) { | |||
PFN_SetProcessDPIAware SetProcessDPIAware = | |||
(PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | |||
if (SetProcessDPIAware) { | |||
SetProcessDPIAware(); | |||
} | |||
if (type == PUGL_PROGRAM) { | |||
HMODULE user32 = LoadLibrary("user32.dll"); | |||
if (user32) { | |||
PFN_SetProcessDPIAware SetProcessDPIAware = | |||
(PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | |||
if (SetProcessDPIAware) { | |||
SetProcessDPIAware(); | |||
} | |||
FreeLibrary(user32); | |||
FreeLibrary(user32); | |||
} | |||
} | |||
LARGE_INTEGER frequency; | |||
@@ -177,10 +177,18 @@ void puglClearMinSize(PuglView* const view) | |||
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* view) | |||
const char* puglGetWindowTitle(const PuglView* const view) | |||
{ | |||
return view->title; | |||
} | |||
@@ -54,6 +54,10 @@ puglBackendLeave(PuglView* view); | |||
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); | |||
@@ -51,20 +51,20 @@ static const uint32_t kAudioPortIsSidechain = 0x2; | |||
static const uint32_t kCVPortHasBipolarRange = 0x10; | |||
/** | |||
CV port has negative unipolar range (0 to +1, or 0 to +10 if scaled). | |||
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 (-1 to 0, or -10 to 0 if scaled). | |||
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 range flag is required if this flag is set. | |||
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. | |||
*/ | |||
@@ -48,6 +48,10 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; | |||
typedef DGL_NAMESPACE::TopLevelWidget UIWidget; | |||
#endif | |||
START_NAMESPACE_DGL | |||
class PluginWindow; | |||
END_NAMESPACE_DGL | |||
START_NAMESPACE_DISTRHO | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
@@ -311,7 +315,7 @@ protected: | |||
private: | |||
struct PrivateData; | |||
PrivateData* const uiData; | |||
friend class PluginWindow; | |||
friend class DGL_NAMESPACE::PluginWindow; | |||
friend class UIExporter; | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) | |||
@@ -20,6 +20,9 @@ | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
typedef HMODULE lib_t; | |||
@@ -20,6 +20,9 @@ | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#endif | |||
@@ -20,6 +20,9 @@ | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#else | |||
@@ -70,6 +70,7 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por | |||
break; | |||
} | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Plugin private data | |||
@@ -157,9 +158,9 @@ struct Plugin::PrivateData { | |||
#ifdef DISTRHO_PLUGIN_TARGET_LV2 | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
# endif | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# endif | |||
# endif | |||
#endif | |||
} | |||
@@ -38,6 +38,10 @@ | |||
# define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name" | |||
#endif | |||
#ifndef JACK_METADATA_PORT_GROUP | |||
# define JACK_METADATA_PORT_GROUP "http://jackaudio.org/metadata/port-group" | |||
#endif | |||
#ifndef JACK_METADATA_SIGNAL_TYPE | |||
# define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type" | |||
#endif | |||
@@ -351,6 +355,12 @@ protected: | |||
fTimePosition.bbt.bar = pos.bar; | |||
fTimePosition.bbt.beat = pos.beat; | |||
fTimePosition.bbt.tick = pos.tick; | |||
#ifdef JACK_TICK_DOUBLE | |||
if (pos.valid & JackTickDouble) | |||
fTimePosition.bbt.tick = pos.tick_double; | |||
else | |||
#endif | |||
fTimePosition.bbt.tick = pos.tick; | |||
fTimePosition.bbt.barStartTick = pos.bar_start_tick; | |||
fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; | |||
@@ -588,6 +598,12 @@ private: | |||
jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer"); | |||
} | |||
if (port.groupId != kPortGroupNone) | |||
{ | |||
const PortGroupWithId& portGroup(fPlugin.getPortGroupById(port.groupId)); | |||
jackbridge_set_property(fClient, uuid, JACK_METADATA_PORT_GROUP, portGroup.name, "text/plain"); | |||
} | |||
if (port.hints & kAudioPortIsCV) | |||
{ | |||
jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain"); | |||
@@ -236,7 +236,7 @@ public: | |||
fTimePosition.bbt.barStartTick = 0; | |||
fTimePosition.bbt.beatsPerBar = 4; | |||
fTimePosition.bbt.beatType = 4; | |||
fTimePosition.bbt.ticksPerBeat = 960.0; | |||
fTimePosition.bbt.ticksPerBeat = 1920.0; | |||
fTimePosition.bbt.beatsPerMinute = 120.0; | |||
#endif | |||
fPlugin.activate(); | |||
@@ -359,7 +359,7 @@ public: | |||
if (obj->body.otype != fURIDs.timePosition) | |||
continue; | |||
LV2_Atom* bar = nullptr; | |||
LV2_Atom* bar = nullptr; | |||
LV2_Atom* barBeat = nullptr; | |||
LV2_Atom* beatUnit = nullptr; | |||
LV2_Atom* beatsPerBar = nullptr; | |||
@@ -430,9 +430,21 @@ void lv2_generate_ttl(const char* const basename) | |||
if (port.hints & kAudioPortIsSidechain) | |||
pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||
if (port.groupId != kPortGroupNone) | |||
switch (port.groupId) | |||
{ | |||
case kPortGroupNone: | |||
break; | |||
case kPortGroupMono: | |||
pluginString += " pg:group pg:MonoGroup ;\n"; | |||
break; | |||
case kPortGroupStereo: | |||
pluginString += " pg:group pg:StereoGroup ;\n"; | |||
break; | |||
default: | |||
pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |||
+ plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | |||
break; | |||
} | |||
// set ranges | |||
if (port.hints & kCVPortHasBipolarRange) | |||
@@ -508,9 +520,21 @@ void lv2_generate_ttl(const char* const basename) | |||
if (port.hints & kAudioPortIsSidechain) | |||
pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||
if (port.groupId != kPortGroupNone) | |||
switch (port.groupId) | |||
{ | |||
case kPortGroupNone: | |||
break; | |||
case kPortGroupMono: | |||
pluginString += " pg:group pg:MonoGroup ;\n"; | |||
break; | |||
case kPortGroupStereo: | |||
pluginString += " pg:group pg:StereoGroup ;\n"; | |||
break; | |||
default: | |||
pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |||
+ plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | |||
break; | |||
} | |||
// set ranges | |||
if (port.hints & kCVPortHasBipolarRange) | |||
@@ -731,7 +755,7 @@ void lv2_generate_ttl(const char* const basename) | |||
// unit | |||
const String& unit(plugin.getParameterUnit(i)); | |||
if (unit.isNotEmpty() && ! unit.contains(" ")) | |||
if (unit.isNotEmpty() && ! unit.contains(' ')) | |||
{ | |||
String lunit(unit); | |||
lunit.toLower(); | |||
@@ -770,7 +794,10 @@ void lv2_generate_ttl(const char* const basename) | |||
pluginString += " a unit:Unit ;\n"; | |||
pluginString += " rdfs:label \"" + unit + "\" ;\n"; | |||
pluginString += " unit:symbol \"" + unit + "\" ;\n"; | |||
pluginString += " unit:render \"%f " + unit + "\" ;\n"; | |||
if (plugin.getParameterHints(i) & kParameterIsInteger) | |||
pluginString += " unit:render \"%d " + unit + "\" ;\n"; | |||
else | |||
pluginString += " unit:render \"%f " + unit + "\" ;\n"; | |||
pluginString += " ] ;\n"; | |||
} | |||
} | |||
@@ -779,7 +806,12 @@ void lv2_generate_ttl(const char* const basename) | |||
const String& comment(plugin.getParameterDescription(i)); | |||
if (comment.isNotEmpty()) | |||
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; | |||
{ | |||
if (comment.contains('"') || comment.contains('\n')) | |||
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; | |||
else | |||
pluginString += " rdfs:comment \"" + comment + "\" ;\n"; | |||
} | |||
// hints | |||
const uint32_t hints(plugin.getParameterHints(i)); | |||
@@ -805,9 +837,22 @@ void lv2_generate_ttl(const char* const basename) | |||
// group | |||
const uint32_t groupId = plugin.getParameterGroupId(i); | |||
if (groupId != kPortGroupNone) | |||
switch (groupId) | |||
{ | |||
case kPortGroupNone: | |||
break; | |||
case kPortGroupMono: | |||
pluginString += " pg:group pg:MonoGroup ;\n"; | |||
break; | |||
case kPortGroupStereo: | |||
pluginString += " pg:group pg:StereoGroup ;\n"; | |||
break; | |||
default: | |||
pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | |||
+ plugin.getPortGroupSymbolForId(groupId) + "> ;\n"; | |||
break; | |||
} | |||
} // ! designated | |||
if (i+1 == count) | |||
@@ -823,8 +868,8 @@ void lv2_generate_ttl(const char* const basename) | |||
if (comment.isNotEmpty()) | |||
{ | |||
if (comment.contains('"')) | |||
pluginString += " rdfs:comment \"" + comment + "\" ;\n\n"; | |||
if (comment.contains('"') || comment.contains('\n')) | |||
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n"; | |||
else | |||
pluginString += " rdfs:comment \"" + comment + "\" ;\n\n"; | |||
} | |||
@@ -903,6 +948,13 @@ void lv2_generate_ttl(const char* const basename) | |||
DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.groupId != kPortGroupNone); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.symbol.isNotEmpty()); | |||
switch (portGroup.groupId) | |||
{ | |||
case kPortGroupMono: | |||
case kPortGroupStereo: | |||
continue; | |||
} | |||
pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + portGroup.symbol + ">\n"; | |||
isInput = isOutput = false; | |||
@@ -1063,7 +1063,7 @@ public: | |||
fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0); | |||
// ticksPerBeat is not possible with VST | |||
fTimePosition.bbt.ticksPerBeat = 960.0; | |||
fTimePosition.bbt.ticksPerBeat = 1920.0; | |||
if (vstTimeInfo->flags & kVstTempoValid) | |||
fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo; | |||
@@ -1099,7 +1099,9 @@ public: | |||
fTimePosition.bbt.beatType = 4.0f; | |||
} | |||
fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*fTimePosition.bbt.beatsPerBar*(fTimePosition.bbt.bar-1); | |||
fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* | |||
fTimePosition.bbt.beatsPerBar* | |||
(fTimePosition.bbt.bar-1); | |||
fPlugin.setTimePosition(fTimePosition); | |||
} | |||
@@ -1448,7 +1450,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||
memset(properties, 0, sizeof(VstParameterProperties)); | |||
// full name | |||
DISTRHO_NAMESPACE::strncpy(properties->label, plugin.getParameterName(index), VestigeMaxLabelLen); | |||
DISTRHO_NAMESPACE::strncpy(properties->label, | |||
plugin.getParameterName(index), | |||
sizeof(properties->label)); | |||
// short name | |||
const String& shortName(plugin.getParameterShortName(index)); | |||
@@ -1456,7 +1460,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||
if (shortName.isNotEmpty()) | |||
DISTRHO_NAMESPACE::strncpy(properties->shortLabel, | |||
plugin.getParameterShortName(index), | |||
VestigeMaxShortLabelLen); | |||
sizeof(properties->shortLabel)); | |||
// parameter hints | |||
const uint32_t hints = plugin.getParameterHints(index); | |||
@@ -1482,6 +1486,34 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||
properties->flags |= kVstParameterCanRamp; | |||
} | |||
// parameter group (category in vst) | |||
const uint32_t groupId = plugin.getParameterGroupId(index); | |||
if (groupId != kPortGroupNone) | |||
{ | |||
// we can't use groupId directly, so use the index array where this group is stored in | |||
for (uint32_t i=0, count=plugin.getPortGroupCount(); i < count; ++i) | |||
{ | |||
const PortGroupWithId& portGroup(plugin.getPortGroupByIndex(i)); | |||
if (portGroup.groupId == groupId) | |||
{ | |||
properties->category = i + 1; | |||
DISTRHO_NAMESPACE::strncpy(properties->categoryLabel, | |||
portGroup.name.buffer(), | |||
sizeof(properties->categoryLabel)); | |||
break; | |||
} | |||
} | |||
if (properties->category != 0) | |||
{ | |||
for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) | |||
if (plugin.getParameterGroupId(i) == groupId) | |||
++properties->numParametersInCategory; | |||
} | |||
} | |||
return 1; | |||
} | |||
} | |||
@@ -96,7 +96,7 @@ private: | |||
static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) | |||
{ | |||
return ((PluginVst*)ptr)->requestParameterValueChange(index, value); | |||
return ((PluginVst3*)ptr)->requestParameterValueChange(index, value); | |||
} | |||
#endif | |||
@@ -109,7 +109,7 @@ private: | |||
static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | |||
{ | |||
return ((PluginVst*)ptr)->writeMidi(midiEvent); | |||
return ((PluginVst3*)ptr)->writeMidi(midiEvent); | |||
} | |||
#endif | |||
@@ -39,7 +39,7 @@ UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr; | |||
PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height) | |||
{ | |||
UI::PrivateData* const pData = s_nextPrivateData; | |||
pData->window = new PluginWindow(ui, pData, width, height); | |||
pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor); | |||
return pData->window.getObject(); | |||
} | |||
@@ -260,7 +260,9 @@ public: | |||
void quit() | |||
{ | |||
uiData->window->close(); | |||
uiData->app.quit(); | |||
if (uiData->app.isStandalone()) | |||
uiData->app.quit(); | |||
} | |||
#endif | |||
@@ -316,20 +318,23 @@ public: | |||
return ! uiData->app.isQuiting(); | |||
} | |||
bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/) | |||
bool handlePluginKeyboard(const bool press, const uint key, const uint16_t mods) | |||
{ | |||
#if 0 /* TODO */ | |||
return glWindow.handlePluginKeyboard(press, key); | |||
#endif | |||
return false; | |||
// TODO also trigger Character input event | |||
Widget::KeyboardEvent ev; | |||
ev.press = press; | |||
ev.key = key; | |||
ev.mod = mods; | |||
return ui->onKeyboard(ev); | |||
} | |||
bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/, const uint16_t /*mods*/) | |||
bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key, const uint16_t mods) | |||
{ | |||
#if 0 /* TODO */ | |||
return glWindow.handlePluginSpecial(press, key); | |||
#endif | |||
return false; | |||
Widget::SpecialEvent ev; | |||
ev.press = press; | |||
ev.key = key; | |||
ev.mod = mods; | |||
return ui->onSpecial(ev); | |||
} | |||
#endif | |||
@@ -89,11 +89,14 @@ public: | |||
bgColor, | |||
fgColor), | |||
fUridMap(uridMap), | |||
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), | |||
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | |||
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | |||
fController(controller), | |||
fWriteFunction(writeFunc), | |||
fURIDs(uridMap), | |||
fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled") | |||
: LV2UI_INVALID_PORT_INDEX), | |||
fWinIdWasNull(winId == 0) | |||
{ | |||
if (widget != nullptr) | |||
@@ -159,7 +162,11 @@ public: | |||
DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | |||
const float value = *(const float*)buffer; | |||
float value = *(const float*)buffer; | |||
if (rindex == fBypassParameterIndex) | |||
value = 1.0f - value; | |||
fUI.parameterChanged(rindex-parameterOffset, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
@@ -253,10 +260,13 @@ protected: | |||
fUiTouch->touch(fUiTouch->handle, rindex, started); | |||
} | |||
void setParameterValue(const uint32_t rindex, const float value) | |||
void setParameterValue(const uint32_t rindex, float value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
if (rindex == fBypassParameterIndex) | |||
value = 1.0f - value; | |||
fWriteFunction(fController, rindex, sizeof(float), 0, &value); | |||
} | |||
@@ -345,6 +355,7 @@ private: | |||
// LV2 features | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2UI_Port_Map* const fUiPortMap; | |||
const LV2UI_Request_Value* const fUiRequestValue; | |||
const LV2UI_Touch* const fUiTouch; | |||
@@ -383,8 +394,11 @@ private: | |||
} | |||
} fURIDs; | |||
// index of bypass parameter, if present | |||
const uint32_t fBypassParameterIndex; | |||
// using ui:showInterface if true | |||
bool fWinIdWasNull; | |||
const bool fWinIdWasNull; | |||
// ------------------------------------------------------------------- | |||
// Callbacks | |||
@@ -20,8 +20,9 @@ | |||
#include "../DistrhoUI.hpp" | |||
#include "../../dgl/Application.hpp" | |||
#ifndef DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../../dgl/Window.hpp" | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../../dgl/src/WindowPrivateData.hpp" | |||
# include "../../dgl/src/pugl.hpp" | |||
#endif | |||
#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||
@@ -35,20 +36,9 @@ | |||
# define DISTRHO_UI_USER_RESIZABLE 0 | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
using DGL_NAMESPACE::Application; | |||
using DGL_NAMESPACE::Window; | |||
// ----------------------------------------------------------------------- | |||
// 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); | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
// Plugin Application, will set class name based on plugin details | |||
@@ -73,7 +63,118 @@ public: | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | |||
}; | |||
class PluginWindow; | |||
// ----------------------------------------------------------------------- | |||
// Plugin Window, will pass some Window events to UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// TODO external ui stuff | |||
class PluginWindow | |||
{ | |||
UI* const ui; | |||
public: | |||
explicit PluginWindow(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), | |||
ui(uiPtr) {} | |||
uint getWidth() const noexcept | |||
{ | |||
return ui->getWidth(); | |||
} | |||
uint getHeight() const noexcept | |||
{ | |||
return ui->getHeight(); | |||
} | |||
bool isVisible() const noexcept | |||
{ | |||
return ui->isRunning(); | |||
} | |||
uintptr_t getNativeWindowHandle() const noexcept | |||
{ | |||
return 0; | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
}; | |||
#else | |||
class PluginWindow : public Window | |||
{ | |||
UI* const ui; | |||
public: | |||
explicit PluginWindow(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), | |||
ui(uiPtr) | |||
{ | |||
puglBackendEnter(pData->view); | |||
} | |||
void leaveContext() | |||
{ | |||
puglBackendLeave(pData->view); | |||
} | |||
protected: | |||
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
ui->uiFocus(focus, mode); | |||
} | |||
void onReshape(const uint width, const uint height) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
ui->uiReshape(width, height); | |||
} | |||
void onScaleFactorChanged(const double scaleFactor) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
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 | |||
END_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
START_NAMESPACE_DISTRHO | |||
using DGL_NAMESPACE::PluginApplication; | |||
using DGL_NAMESPACE::PluginWindow; | |||
// ----------------------------------------------------------------------- | |||
// 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 | |||
@@ -143,16 +244,18 @@ struct UI::PrivateData { | |||
#ifdef DISTRHO_PLUGIN_TARGET_LV2 | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
# endif | |||
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||
parameterOffset += 1; | |||
# endif | |||
# endif | |||
#endif | |||
} | |||
~PrivateData() noexcept | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
std::free(uiStateFileKeyRequest); | |||
#endif | |||
} | |||
void editParamCallback(const uint32_t rindex, const bool started) | |||
@@ -192,100 +295,6 @@ struct UI::PrivateData { | |||
static PluginWindow& createNextWindow(UI* ui, uint width, uint height); | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Plugin Window, will pass some Window events to UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// TODO external ui stuff | |||
class PluginWindow | |||
{ | |||
UI* const ui; | |||
public: | |||
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height) | |||
: Window(pData->app, pData->winId, pData->scaleFactor, width, height, DISTRHO_UI_USER_RESIZABLE), | |||
ui(uiPtr) {} | |||
uint getWidth() const noexcept | |||
{ | |||
return ui->getWidth(); | |||
} | |||
uint getHeight() const noexcept | |||
{ | |||
return ui->getHeight(); | |||
} | |||
bool isVisible() const noexcept | |||
{ | |||
return ui->isRunning(); | |||
} | |||
uintptr_t getNativeWindowHandle() const noexcept | |||
{ | |||
return 0; | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
}; | |||
#else | |||
class PluginWindow : public Window | |||
{ | |||
UI* const ui; | |||
public: | |||
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height) | |||
: Window(pData->app, pData->winId, width, height, pData->scaleFactor, DISTRHO_UI_USER_RESIZABLE), | |||
ui(uiPtr) {} | |||
protected: | |||
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
ui->uiFocus(focus, mode); | |||
} | |||
void onReshape(const uint width, const uint height) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
ui->uiReshape(width, height); | |||
} | |||
void onScaleFactorChanged(const double scaleFactor) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
ui->uiScaleFactorChanged(scaleFactor); | |||
} | |||
# ifndef DGL_FILE_BROWSER_DISABLED | |||
void onFileSelected(const char* const filename) override | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (char* const key = ui->uiData->uiStateFileKeyRequest) | |||
{ | |||
ui->uiData->uiStateFileKeyRequest = nullptr; | |||
// notify DSP | |||
ui->setState(key, filename); | |||
// notify UI | |||
ui->stateChanged(key, filename); | |||
std::free(key); | |||
return; | |||
} | |||
# endif | |||
ui->uiFileBrowserSelected(filename); | |||
} | |||
# endif | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | |||
}; | |||
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// ----------------------------------------------------------------------- | |||
// UI private data fileRequestCallback, which requires PluginWindow definitions | |||
@@ -311,8 +320,37 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
return false; | |||
} | |||
END_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// PluginWindow onFileSelected that require UI::PrivateData definitions | |||
END_NAMESPACE_DISTRHO | |||
#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 DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (char* const key = ui->uiData->uiStateFileKeyRequest) | |||
{ | |||
ui->uiData->uiStateFileKeyRequest = 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 |
@@ -99,6 +99,8 @@ | |||
#define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */ | |||
#define JACK_UUID_EMPTY_INITIALIZER 0 | |||
#define JACK_TICK_DOUBLE | |||
extern "C" { | |||
enum JackOptions { | |||
@@ -153,7 +155,8 @@ enum JackPositionBits { | |||
JackPositionTimecode = 0x020, | |||
JackBBTFrameOffset = 0x040, | |||
JackAudioVideoRatio = 0x080, | |||
JackVideoFrameOffset = 0x100 | |||
JackVideoFrameOffset = 0x100, | |||
JackTickDouble = 0x200 | |||
}; | |||
enum JackSessionEventType { | |||
@@ -223,7 +226,8 @@ struct _jack_position { | |||
jack_nframes_t bbt_offset; | |||
float audio_frames_per_video_frame; | |||
jack_nframes_t video_offset; | |||
int32_t padding[7]; | |||
double tick_double; | |||
int32_t padding[5]; | |||
jack_unique_t unique_2; | |||
} POST_PACKED_STRUCTURE; | |||
@@ -0,0 +1,216 @@ | |||
/* | |||
* RtAudioBridge for DPF | |||
* Copyright (C) 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 RTAUDIOBRIDGE_HPP_INCLUDED | |||
#define RTAUDIOBRIDGE_HPP_INCLUDED | |||
#include "JackBridge.hpp" | |||
#if defined(DISTRHO_OS_MAC) | |||
# define __MACOSX_CORE__ | |||
# define RTAUDIO_API_TYPE MACOSX_CORE | |||
#elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) | |||
# define __WINDOWS_WASAPI__ | |||
# define RTAUDIO_API_TYPE WINDOWS_WASAPI | |||
#elif defined(HAVE_PULSEAUDIO) | |||
# define __LINUX_PULSE__ | |||
# define RTAUDIO_API_TYPE LINUX_PULSE | |||
#elif defined(HAVE_ALSA) | |||
# define __LINUX_ALSA__ | |||
# define RTAUDIO_API_TYPE LINUX_ALSA | |||
#endif | |||
#ifdef RTAUDIO_API_TYPE | |||
# define Point CorePoint /* fix conflict between DGL and macOS Point name */ | |||
# include "rtaudio/RtAudio.h" | |||
# undef Point | |||
# include "../../extra/ScopedPointer.hpp" | |||
struct RtAudioBridge { | |||
// pointer to RtAudio instance | |||
ScopedPointer<RtAudio> handle; | |||
// RtAudio information | |||
uint bufferSize = 0; | |||
uint sampleRate = 0; | |||
// Port caching information | |||
uint numAudioIns = 0; | |||
uint numAudioOuts = 0; | |||
uint numMidiIns = 0; | |||
uint numMidiOuts = 0; | |||
// JACK callbacks | |||
JackProcessCallback jackProcessCallback = nullptr; | |||
void* jackProcessArg = nullptr; | |||
// Runtime buffers | |||
float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
// TODO midi buffer, likely using ringbuffer class | |||
bool open() | |||
{ | |||
ScopedPointer<RtAudio> rtAudio; | |||
try { | |||
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); | |||
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); | |||
uint rtAudioBufferFrames = 512; | |||
RtAudio::StreamParameters inParams; | |||
inParams.deviceId = rtAudio->getDefaultInputDevice(); | |||
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; | |||
RtAudio::StreamParameters outParams; | |||
outParams.deviceId = rtAudio->getDefaultOutputDevice(); | |||
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; | |||
RtAudio::StreamOptions opts; | |||
opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT; | |||
try { | |||
rtAudio->openStream(&outParams, &inParams, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); | |||
} DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); | |||
handle = rtAudio; | |||
bufferSize = rtAudioBufferFrames; | |||
sampleRate = handle->getStreamSampleRate(); | |||
return true; | |||
} | |||
bool close() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |||
if (handle->isStreamRunning()) | |||
{ | |||
try { | |||
handle->abortStream(); | |||
} DISTRHO_SAFE_EXCEPTION("handle->abortStream()"); | |||
} | |||
handle = nullptr; | |||
return true; | |||
} | |||
bool activate() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |||
try { | |||
handle->startStream(); | |||
} DISTRHO_SAFE_EXCEPTION("handle->startStream()"); | |||
return true; | |||
} | |||
bool deactivate() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); | |||
try { | |||
handle->stopStream(); | |||
} DISTRHO_SAFE_EXCEPTION("handle->stopStream()"); | |||
return true; | |||
} | |||
jack_port_t* registerPort(const char* const type, const ulong flags) | |||
{ | |||
bool isAudio, isInput; | |||
if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) | |||
isAudio = true; | |||
else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) | |||
isAudio = false; | |||
else | |||
return nullptr; | |||
if (flags & JackPortIsInput) | |||
isInput = true; | |||
else if (flags & JackPortIsOutput) | |||
isInput = false; | |||
else | |||
return nullptr; | |||
const uintptr_t ret = (isAudio ? 0x1000 : 0x2000) | (isInput ? 0x4000 : 0x8000); | |||
return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) | |||
: (isInput ? numMidiIns++ : numMidiOuts++))); | |||
} | |||
void* getPortBuffer(jack_port_t* const port) | |||
{ | |||
const uintptr_t portMask = (uintptr_t)port; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
if (portMask & 0x1000) | |||
return audioBuffers[(portMask & 0x4000 ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; | |||
#endif | |||
return nullptr; | |||
} | |||
static int RtAudioCallback(void* const outputBuffer, | |||
void* const inputBuffer, | |||
const uint numFrames, | |||
const double /* streamTime */, | |||
const RtAudioStreamStatus /* status */, | |||
void* const userData) | |||
{ | |||
RtAudioBridge* const self = (RtAudioBridge*)userData; | |||
if (self->jackProcessCallback == nullptr) | |||
{ | |||
if (outputBuffer != nullptr) | |||
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
return 0; | |||
} | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
float** const selfAudioBuffers = self->audioBuffers; | |||
uint i = 0; | |||
# if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
if (float* const insPtr = (float*)inputBuffer) | |||
{ | |||
for (uint j=0; j<DISTRHO_PLUGIN_NUM_INPUTS; ++j, ++i) | |||
selfAudioBuffers[i] = insPtr + (j * numFrames); | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
if (float* const outsPtr = (float*)outputBuffer) | |||
{ | |||
for (uint j=0; j<DISTRHO_PLUGIN_NUM_OUTPUTS; ++j, ++i) | |||
selfAudioBuffers[i] = outsPtr + (j * numFrames); | |||
} | |||
# endif | |||
#endif | |||
self->jackProcessCallback(numFrames, self->jackProcessArg); | |||
return 0; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS == 0 | |||
// unused | |||
(void)inputBuffer; | |||
#endif | |||
} | |||
}; | |||
#endif // RTAUDIO_API_TYPE | |||
#endif // RTAUDIOBRIDGE_HPP_INCLUDED |
@@ -0,0 +1,27 @@ | |||
RtAudio: a set of realtime audio i/o C++ classes | |||
Copyright (c) 2001-2019 Gary P. Scavone | |||
Permission is hereby granted, free of charge, to any person | |||
obtaining a copy of this software and associated documentation files | |||
(the "Software"), to deal in the Software without restriction, | |||
including without limitation the rights to use, copy, modify, merge, | |||
publish, distribute, sublicense, and/or sell copies of the Software, | |||
and to permit persons to whom the Software is furnished to do so, | |||
subject to the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
Any person wishing to distribute modifications to the Software is | |||
asked to send the modifications to the original developer so that | |||
they can be incorporated into the canonical version. This is, | |||
however, not a binding provision of this license. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -404,33 +404,35 @@ public: | |||
DemoWindow(Application& app) | |||
: StandaloneWindow(app), | |||
wColor(this), | |||
wImages(this), | |||
wRects(this), | |||
wShapes(this), | |||
#ifdef DGL_OPENGL | |||
wText(this), | |||
#endif | |||
wLeft(this, this), | |||
resizer(this), | |||
curWidget(nullptr) | |||
{ | |||
wColor.hide(); | |||
wImages.hide(); | |||
wRects.hide(); | |||
wShapes.hide(); | |||
#ifdef DGL_OPENGL | |||
wText.hide(); | |||
#endif | |||
const ScopedGraphicsContext sgc(*this); | |||
wColor = new ExampleColorSubWidget(this); | |||
wColor->hide(); | |||
wColor->setAbsoluteX(kSidebarWidth); | |||
wImages = new ExampleImagesSubWidget(this); | |||
wImages->hide(); | |||
wImages->setAbsoluteX(kSidebarWidth); | |||
wRects = new ExampleRectanglesSubWidget(this); | |||
wRects->hide(); | |||
wRects->setAbsoluteX(kSidebarWidth); | |||
wShapes = new ExampleShapesSubWidget(this); | |||
wShapes->hide(); | |||
wShapes->setAbsoluteX(kSidebarWidth); | |||
wColor.setAbsoluteX(kSidebarWidth); | |||
wImages.setAbsoluteX(kSidebarWidth); | |||
wRects.setAbsoluteX(kSidebarWidth); | |||
wShapes.setAbsoluteX(kSidebarWidth); | |||
#ifdef DGL_OPENGL | |||
wText.setAbsoluteX(kSidebarWidth); | |||
wText = new ExampleTextSubWidget(this), | |||
wText->hide(); | |||
wText->setAbsoluteX(kSidebarWidth); | |||
#endif | |||
wLeft.setAbsolutePos(2, 2); | |||
wLeft = new LeftSideWidget(this, this), | |||
wLeft->setAbsolutePos(2, 2); | |||
resizer = new ResizeHandle(this); | |||
curPageChanged(0); | |||
} | |||
@@ -444,20 +446,20 @@ protected: | |||
switch (curPage) | |||
{ | |||
case 0: | |||
curWidget = &wColor; | |||
curWidget = wColor; | |||
break; | |||
case 1: | |||
curWidget = &wImages; | |||
curWidget = wImages; | |||
break; | |||
case 2: | |||
curWidget = &wRects; | |||
curWidget = wRects; | |||
break; | |||
case 3: | |||
curWidget = &wShapes; | |||
curWidget = wShapes; | |||
break; | |||
#ifdef DGL_OPENGL | |||
case 4: | |||
curWidget = &wText; | |||
curWidget = wText; | |||
break; | |||
#endif | |||
default: | |||
@@ -481,26 +483,26 @@ protected: | |||
return; | |||
Size<uint> size(width-kSidebarWidth, height); | |||
wColor.setSize(size); | |||
wImages.setSize(size); | |||
wRects.setSize(size); | |||
wShapes.setSize(size); | |||
wColor->setSize(size); | |||
wImages->setSize(size); | |||
wRects->setSize(size); | |||
wShapes->setSize(size); | |||
#ifdef DGL_OPENGL | |||
wText.setSize(size); | |||
wText->setSize(size); | |||
#endif | |||
wLeft.setSize(kSidebarWidth-4, height-4); | |||
wLeft->setSize(kSidebarWidth-4, height-4); | |||
} | |||
private: | |||
ExampleColorSubWidget wColor; | |||
ExampleImagesSubWidget wImages; | |||
ExampleRectanglesSubWidget wRects; | |||
ExampleShapesSubWidget wShapes; | |||
ScopedPointer<ExampleColorSubWidget> wColor; | |||
ScopedPointer<ExampleImagesSubWidget> wImages; | |||
ScopedPointer<ExampleRectanglesSubWidget> wRects; | |||
ScopedPointer<ExampleShapesSubWidget> wShapes; | |||
#ifdef DGL_OPENGL | |||
ExampleTextSubWidget wText; | |||
ScopedPointer<ExampleTextSubWidget> wText; | |||
#endif | |||
LeftSideWidget wLeft; | |||
ResizeHandle resizer; | |||
ScopedPointer<LeftSideWidget> wLeft; | |||
ScopedPointer<ResizeHandle> resizer; | |||
Widget* curWidget; | |||
}; | |||
@@ -1,13 +0,0 @@ | |||
###################################### | |||
## Generic README for DISTRHO Plugins | |||
This folder contains audio plugins downloaded from http://distrho.sourceforge.net/plugins | |||
The included *.lv2 folder is an LV2 bundle and the *.dll file is a VST. | |||
Check your host documentation to know where to place these files. | |||
Note that most hosts support VST but not LV2. | |||
If your host is not listing these plugins, make sure the binaries you downloaded match your host architecture. | |||
32bit plugins go into 32bit hosts, same for 64bit. | |||
Some hosts are able to load plugins with different architecture, but that's the exception rather than the rule. |
@@ -1,7 +1,7 @@ | |||
#!/bin/bash | |||
# function not available on some systems | |||
if ! which realpath 2>/dev/null; then | |||
if ! which realpath &>/dev/null; then | |||
function realpath() { | |||
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" | |||
} | |||
@@ -1,54 +0,0 @@ | |||
#!/bin/bash | |||
set -e | |||
# -------------------------------------------------------------------------------------------------------------------------------- | |||
# extract debs and pack them | |||
function compressFolderAsZip() { | |||
rm -f "$1.zip" | |||
zip -X -r "$1" "$1" | |||
rm -r "$1" | |||
} | |||
# -------------------------------------------------------------------------------------------------------------------------------- | |||
if [ "$1" == "" ]; then | |||
echo Missing argument | |||
exit | |||
fi | |||
# -------------------------------------------------------------------------------------------------------------------------------- | |||
cd bin | |||
mkdir -p tmp | |||
rm -rf tmp/* | |||
NAME="$1" | |||
_mingw32-build make -C .. clean | |||
_mingw32-build make -C .. | |||
for i in `ls *-vst.dll`; do mv $i `echo $i | awk 'sub("-vst","")'`; done | |||
rm -rf *ladspa* *dssi* *vst* | |||
mkdir -p "$NAME-win32bit" | |||
mv *.dll *.lv2/ "$NAME-win32bit" | |||
cp "../dpf/utils/README-DPF-Windows.txt" "$NAME-win32bit/README.txt" | |||
compressFolderAsZip "$NAME-win32bit" | |||
rm -rf tmp/* | |||
_mingw64-build make -C .. clean | |||
_mingw64-build make -C .. | |||
for i in `ls *-vst.dll`; do mv $i `echo $i | awk 'sub("-vst","")'`; done | |||
rm -rf *ladspa* *dssi* *vst* | |||
mkdir -p "$NAME-win64bit" | |||
mv *.dll *.lv2/ "$NAME-win64bit" | |||
cp "../dpf/utils/README-DPF-Windows.txt" "$NAME-win64bit/README.txt" | |||
compressFolderAsZip "$NAME-win64bit" | |||
rm -rf tmp/* | |||
_mingw64-build make -C .. clean | |||
cd .. | |||
# -------------------------------------------------------------------------------------------------------------------------------- |
@@ -0,0 +1,51 @@ | |||
#!/bin/bash | |||
set -e | |||
if [ -d bin ]; then | |||
cd bin | |||
else | |||
echo "Please run this script from the root folder" | |||
exit | |||
fi | |||
NAME="$(basename $(git rev-parse --show-toplevel))" | |||
SNAME="$(echo ${NAME} | tr -d ' ' | tr '/' '-')" | |||
rm -rf lv2 | |||
rm -rf vst2 | |||
mkdir lv2 vst2 | |||
mv *.lv2 lv2/ | |||
mv *.vst vst2/ | |||
pkgbuild \ | |||
--identifier "studio.kx.distrho.plugins.${SNAME}.lv2bundles" \ | |||
--install-location "/Library/Audio/Plug-Ins/LV2/" \ | |||
--root "${PWD}/lv2/" \ | |||
../dpf-${SNAME}-lv2bundles.pkg | |||
pkgbuild \ | |||
--identifier "studio.kx.distrho.plugins.${SNAME}.vst2bundles" \ | |||
--install-location "/Library/Audio/Plug-Ins/VST/" \ | |||
--root "${PWD}/vst2/" \ | |||
../dpf-${SNAME}-vst2bundles.pkg | |||
cd .. | |||
DPF_UTILS_DIR=$(dirname ${0}) | |||
sed -e "s|@name@|${NAME}|" ${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in > build/welcome.txt | |||
sed -e "s|@builddir@|${PWD}/build|" \ | |||
-e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \ | |||
-e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \ | |||
-e "s|@name@|${NAME}|g" \ | |||
-e "s|@sname@|${SNAME}|g" \ | |||
${DPF_UTILS_DIR}/plugin.pkg/package.xml.in > build/package.xml | |||
productbuild \ | |||
--distribution build/package.xml \ | |||
--identifier "studio.kx.distrho.${SNAME}" \ | |||
--package-path "${PWD}" \ | |||
--version 0 \ | |||
${SNAME}-macOS.pkg |
@@ -0,0 +1,18 @@ | |||
<?xml version="1.0" encoding="utf-8" standalone="no"?> | |||
<installer-gui-script minSpecVersion="1"> | |||
<title>@name@</title> | |||
<domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true" /> | |||
<options customize="always" hostArchitectures="x86_64" require-scripts="false" rootVolumeOnly="true" /> | |||
<pkg-ref id="studio.kx.distrho.@sname@" /> | |||
<welcome file="@builddir@/welcome.txt" mime-type="text/plain" /> | |||
<choice id="studio.kx.distrho.@sname@-lv2" title="LV2" description="Install LV2 plugins" visible="true"> | |||
<pkg-ref id="studio.kx.distrho.@sname@-lv2bundles" version="0">@lv2bundleref@</pkg-ref> | |||
</choice> | |||
<choice id="studio.kx.distrho.@sname@-vst2" title="VST2" description="Install VST2 plugins" visible="true"> | |||
<pkg-ref id="studio.kx.distrho.@sname@-vst2bundles" version="0">@vst2bundleref@</pkg-ref> | |||
</choice> | |||
<choices-outline> | |||
<line choice="studio.kx.distrho.@sname@-lv2"/> | |||
<line choice="studio.kx.distrho.@sname@-vst2"/> | |||
</choices-outline> | |||
</installer-gui-script> |
@@ -0,0 +1,3 @@ | |||
@name@ | |||
This is an audio plugin installer based on DPF. |
@@ -9,7 +9,7 @@ | |||
<key>CFBundleIconFile</key> | |||
<string></string> | |||
<key>CFBundleIdentifier</key> | |||
<string>net.sf.distrho.X-PROJECTNAME-X</string> | |||
<string>studio.kx.distrho.X-PROJECTNAME-X</string> | |||
<key>CFBundleInfoDictionaryVersion</key> | |||
<string>6.0</string> | |||
<key>CFBundlePackageType</key> | |||
@@ -1,5 +1,4 @@ | |||
DISTRHO Plugin Framework (DPF) | |||
Copyright (C) 2012-2014 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 | |||
@@ -29,7 +29,7 @@ using DGL::Color; | |||
DistrhoUIMVerb::DistrhoUIMVerb() | |||
: UI(Art::backgroundWidth, Art::backgroundHeight), | |||
fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, GL_BGR) | |||
fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, kImageFormatBGR) | |||
{ | |||
// text | |||
fNanoText.loadSharedResources(); | |||
@@ -122,6 +122,9 @@ DistrhoUIMVerb::DistrhoUIMVerb() | |||
// set initial values | |||
programLoaded(0); | |||
// TODO auto-scale but non-resizable | |||
// setGeometryConstraints(Art::backgroundWidth, Art::backgroundHeight, true, true); | |||
} | |||
DistrhoUIMVerb::~DistrhoUIMVerb() | |||
@@ -225,7 +228,9 @@ void DistrhoUIMVerb::imageKnobValueChanged(ImageKnob* knob, float value) | |||
void DistrhoUIMVerb::onDisplay() | |||
{ | |||
fImgBackground.draw(); | |||
const GraphicsContext& context(getGraphicsContext()); | |||
fImgBackground.draw(context); | |||
// text display | |||
fNanoText.beginFrame(this); | |||