Browse Source

Update everything

master
falkTX 3 months ago
parent
commit
5130b6201d
64 changed files with 14488 additions and 1041 deletions
  1. +189
    -0
      .github/workflows/build.yml
  2. +0
    -1
      dpf/LICENSE
  3. +27
    -3
      dpf/Makefile.base.mk
  4. +32
    -16
      dpf/Makefile.plugins.mk
  5. +9
    -0
      dpf/cmake/DPF-plugin.cmake
  6. +11
    -1
      dpf/dgl/Application.hpp
  7. +5
    -0
      dpf/dgl/Color.hpp
  8. +154
    -0
      dpf/dgl/EventHandlers.hpp
  9. +11
    -22
      dpf/dgl/ImageBaseWidgets.hpp
  10. +1
    -1
      dpf/dgl/Makefile
  11. +16
    -0
      dpf/dgl/SubWidget.hpp
  12. +9
    -9
      dpf/dgl/Widget.hpp
  13. +40
    -16
      dpf/dgl/Window.hpp
  14. +5
    -0
      dpf/dgl/src/Application.cpp
  15. +3
    -3
      dpf/dgl/src/ApplicationPrivateData.cpp
  16. +3
    -3
      dpf/dgl/src/ApplicationPrivateData.hpp
  17. +1
    -3
      dpf/dgl/src/Cairo.cpp
  18. +7
    -0
      dpf/dgl/src/Color.cpp
  19. +0
    -188
      dpf/dgl/src/Common.hpp
  20. +632
    -0
      dpf/dgl/src/EventHandlers.cpp
  21. +163
    -311
      dpf/dgl/src/ImageBaseWidgets.cpp
  22. +4
    -1
      dpf/dgl/src/NanoVG.cpp
  23. +3
    -3
      dpf/dgl/src/OpenGL.cpp
  24. +20
    -5
      dpf/dgl/src/SubWidget.cpp
  25. +1
    -0
      dpf/dgl/src/SubWidgetPrivateData.cpp
  26. +1
    -0
      dpf/dgl/src/SubWidgetPrivateData.hpp
  27. +6
    -6
      dpf/dgl/src/WidgetPrivateData.cpp
  28. +14
    -5
      dpf/dgl/src/Window.cpp
  29. +11
    -13
      dpf/dgl/src/WindowPrivateData.cpp
  30. +3
    -3
      dpf/dgl/src/WindowPrivateData.hpp
  31. +11
    -10
      dpf/dgl/src/pugl-upstream/src/win.c
  32. +9
    -1
      dpf/dgl/src/pugl.cpp
  33. +4
    -0
      dpf/dgl/src/pugl.hpp
  34. +3
    -3
      dpf/distrho/DistrhoPlugin.hpp
  35. +5
    -1
      dpf/distrho/DistrhoUI.hpp
  36. +3
    -0
      dpf/distrho/extra/LibraryUtils.hpp
  37. +3
    -0
      dpf/distrho/extra/Mutex.hpp
  38. +3
    -0
      dpf/distrho/extra/Sleep.hpp
  39. +3
    -2
      dpf/distrho/src/DistrhoPluginInternal.hpp
  40. +16
    -0
      dpf/distrho/src/DistrhoPluginJACK.cpp
  41. +2
    -2
      dpf/distrho/src/DistrhoPluginLV2.cpp
  42. +60
    -8
      dpf/distrho/src/DistrhoPluginLV2export.cpp
  43. +36
    -4
      dpf/distrho/src/DistrhoPluginVST2.cpp
  44. +2
    -2
      dpf/distrho/src/DistrhoPluginVST3.cpp
  45. +1
    -1
      dpf/distrho/src/DistrhoUI.cpp
  46. +16
    -11
      dpf/distrho/src/DistrhoUIInternal.hpp
  47. +17
    -3
      dpf/distrho/src/DistrhoUILV2.cpp
  48. +150
    -112
      dpf/distrho/src/DistrhoUIPrivateData.hpp
  49. +294
    -153
      dpf/distrho/src/jackbridge/JackBridge.cpp
  50. +6
    -2
      dpf/distrho/src/jackbridge/JackBridge.hpp
  51. +216
    -0
      dpf/distrho/src/jackbridge/RtAudioBridge.hpp
  52. +27
    -0
      dpf/distrho/src/jackbridge/rtaudio/LICENSE
  53. +10911
    -0
      dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp
  54. +1185
    -0
      dpf/distrho/src/jackbridge/rtaudio/RtAudio.h
  55. +42
    -40
      dpf/tests/Demo.cpp
  56. +0
    -13
      dpf/utils/README-DPF-Windows.txt
  57. +1
    -1
      dpf/utils/generate-ttl.sh
  58. +0
    -54
      dpf/utils/pack-win-dlls.sh
  59. +51
    -0
      dpf/utils/package-osx-bundles.sh
  60. +18
    -0
      dpf/utils/plugin.pkg/package.xml.in
  61. +3
    -0
      dpf/utils/plugin.pkg/welcome.txt.in
  62. +1
    -1
      dpf/utils/plugin.vst/Contents/Info.plist
  63. +1
    -2
      plugins/Kars/LICENSE
  64. +7
    -2
      plugins/MVerb/DistrhoUIMVerb.cpp

+ 189
- 0
.github/workflows/build.yml View File

@@ -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

+ 0
- 1
dpf/LICENSE View File

@@ -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


+ 27
- 3
dpf/Makefile.base.mk View File

@@ -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

# ---------------------------------------------------------------------------------------------------------------------


+ 32
- 16
dpf/Makefile.plugins.mk View File

@@ -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)


+ 9
- 0
dpf/cmake/DPF-plugin.cmake View File

@@ -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"


+ 11
- 1
dpf/dgl/Application.hpp View File

@@ -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);



+ 5
- 0
dpf/dgl/Color.hpp View File

@@ -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.


+ 154
- 0
dpf/dgl/EventHandlers.hpp View File

@@ -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


+ 11
- 22
dpf/dgl/ImageBaseWidgets.hpp View File

@@ -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;


+ 1
- 1
dpf/dgl/Makefile View File

@@ -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

# ---------------------------------------------------------------------------------------------------------------------



+ 16
- 0
dpf/dgl/SubWidget.hpp View File

@@ -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.
*/


+ 9
- 9
dpf/dgl/Widget.hpp View File

@@ -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) {}


+ 40
- 16
dpf/dgl/Window.hpp View File

@@ -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

+ 5
- 0
dpf/dgl/src/Application.cpp View File

@@ -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,)


+ 3
- 3
dpf/dgl/src/ApplicationPrivateData.cpp View File

@@ -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)
{


+ 3
- 3
dpf/dgl/src/ApplicationPrivateData.hpp View File

@@ -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;


+ 1
- 3
dpf/dgl/src/Cairo.cpp View File

@@ -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;



+ 7
- 0
dpf/dgl/src/Color.cpp View File

@@ -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;


+ 0
- 188
dpf/dgl/src/Common.hpp View File

@@ -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

+ 632
- 0
dpf/dgl/src/EventHandlers.cpp View File

@@ -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

+ 163
- 311
dpf/dgl/src/ImageBaseWidgets.cpp View File

@@ -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();