| @@ -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> | Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | ||||
| Permission to use, copy, modify, and/or distribute this software for any purpose with | Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
| @@ -198,7 +198,7 @@ endif | |||||
| ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
| # Always build statically on windows | # Always build statically on windows | ||||
| LINK_FLAGS += -static | |||||
| LINK_FLAGS += -static -static-libgcc -static-libstdc++ | |||||
| endif | endif | ||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -247,6 +247,20 @@ endif | |||||
| HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) | 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 | # backwards compat | ||||
| HAVE_JACK = true | HAVE_JACK = true | ||||
| @@ -357,9 +371,19 @@ endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Set optional libraries specific stuff | # 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) | 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 | endif | ||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -7,14 +7,12 @@ | |||||
| # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | # 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)) | ifeq (,$(wildcard ../../Makefile.base.mk)) | ||||
| DPF_PATH=../../dpf | DPF_PATH=../../dpf | ||||
| else | else | ||||
| DPF_PATH=../.. | DPF_PATH=../.. | ||||
| endif | endif | ||||
| else | |||||
| DPF_PATH = $(DPF_CUSTOM_PATH) | |||||
| endif | endif | ||||
| include $(DPF_PATH)/Makefile.base.mk | include $(DPF_PATH)/Makefile.base.mk | ||||
| @@ -22,31 +20,49 @@ include $(DPF_PATH)/Makefile.base.mk | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Basic setup | # Basic setup | ||||
| ifeq ($(DPF_CUSTOM_PATH),) | |||||
| ifeq ($(DPF_TARGET_DIR),) | |||||
| TARGET_DIR = ../../bin | TARGET_DIR = ../../bin | ||||
| BUILD_DIR = ../../build/$(NAME) | |||||
| else | |||||
| ifeq ($(DPF_CUSTOM_TARGET_DIR),) | |||||
| $(error DPF_CUSTOM_TARGET_DIR is not set) | |||||
| else | else | ||||
| TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR) | |||||
| TARGET_DIR = $(DPF_TARGET_DIR) | |||||
| endif | endif | ||||
| ifeq ($(DPF_CUSTOM_BUILD_DIR),) | |||||
| $(error DPF_CUSTOM_BUILD_DIR is not set) | |||||
| ifeq ($(DPF_BUILD_DIR),) | |||||
| BUILD_DIR = ../../build/$(NAME) | |||||
| else | else | ||||
| BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR) | |||||
| endif | |||||
| BUILD_DIR = $(DPF_BUILD_DIR) | |||||
| endif | endif | ||||
| BUILD_C_FLAGS += -I. | BUILD_C_FLAGS += -I. | ||||
| BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | 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) | ifeq ($(HAVE_LIBLO),true) | ||||
| BASE_FLAGS += -DHAVE_LIBLO | BASE_FLAGS += -DHAVE_LIBLO | ||||
| endif | 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 | endif | ||||
| # backwards compat | # 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 | $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp | ||||
| -@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
| @echo "Compiling DistrhoPluginMain.cpp (JACK)" | @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 | $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp | ||||
| -@mkdir -p $(BUILD_DIR) | -@mkdir -p $(BUILD_DIR) | ||||
| @@ -187,6 +187,13 @@ function(dpf__build_jack NAME DGL_LIBRARY) | |||||
| if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | ||||
| target_link_libraries("${NAME}-jack" PRIVATE "dl") | target_link_libraries("${NAME}-jack" PRIVATE "dl") | ||||
| endif() | 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() | endfunction() | ||||
| # dpf__build_ladspa | # dpf__build_ladspa | ||||
| @@ -326,6 +333,7 @@ function(dpf__add_dgl_cairo) | |||||
| "${DPF_ROOT_DIR}/dgl/src/Application.cpp" | "${DPF_ROOT_DIR}/dgl/src/Application.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | "${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/Color.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/Geometry.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.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/Application.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | "${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/Color.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/Geometry.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | ||||
| "${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.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. | There's no single/global application instance in DGL, and multiple windows can share the same app instance. | ||||
| In standalone mode an application will automatically quit its event-loop when all its windows are closed. | In standalone mode an application will automatically quit its event-loop when all its windows are closed. | ||||
| Unless stated otherwise, functions within this class are not thread-safe. | |||||
| */ | */ | ||||
| class Application | class Application | ||||
| { | { | ||||
| @@ -61,6 +63,7 @@ public: | |||||
| /** | /** | ||||
| Quit the application. | Quit the application. | ||||
| This stops the event-loop and closes all Windows. | 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. | @note This function is meant for standalones only, *never* call this from plugins. | ||||
| */ | */ | ||||
| void quit(); | void quit(); | ||||
| @@ -68,14 +71,21 @@ public: | |||||
| /** | /** | ||||
| Check if the application is about to quit. | Check if the application is about to quit. | ||||
| Returning true means there's no event-loop running at the moment (or it's just about to stop). | 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; | 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. | Add a callback function to be triggered on every idle cycle. | ||||
| You can add more than one, and remove them at anytime with removeIdleCallback(). | 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). | 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); | void addIdleCallback(IdleCallback* callback); | ||||
| @@ -65,6 +65,11 @@ struct Color { | |||||
| */ | */ | ||||
| Color(const Color& color1, const Color& color2, float u) noexcept; | 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. | Create a color specified by hue, saturation and lightness. | ||||
| Values must in [0..1] range. | 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 | #ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | ||||
| #define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | #define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED | ||||
| #include "EventHandlers.hpp" | |||||
| #include "StandaloneWindow.hpp" | #include "StandaloneWindow.hpp" | ||||
| #include "SubWidget.hpp" | #include "SubWidget.hpp" | ||||
| @@ -47,7 +48,8 @@ private: | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| template <class ImageType> | template <class ImageType> | ||||
| class ImageBaseButton : public SubWidget | |||||
| class ImageBaseButton : public SubWidget, | |||||
| public ButtonEventHandler | |||||
| { | { | ||||
| public: | public: | ||||
| class Callback | class Callback | ||||
| @@ -80,14 +82,10 @@ private: | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| template <class ImageType> | template <class ImageType> | ||||
| class ImageBaseKnob : public SubWidget | |||||
| class ImageBaseKnob : public SubWidget, | |||||
| public KnobEventHandler | |||||
| { | { | ||||
| public: | public: | ||||
| enum Orientation { | |||||
| Horizontal, | |||||
| Vertical | |||||
| }; | |||||
| class Callback | class Callback | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -102,25 +100,16 @@ public: | |||||
| ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob); | ||||
| ~ImageBaseKnob() override; | ~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 setCallback(Callback* callback) noexcept; | ||||
| void setOrientation(Orientation orientation) noexcept; | |||||
| void setRotationAngle(int angle); | |||||
| void setImageLayerCount(uint count) noexcept; | void setImageLayerCount(uint count) noexcept; | ||||
| void setRotationAngle(int angle); | |||||
| bool setValue(float value, bool sendCallback = false) noexcept override; | |||||
| protected: | 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: | private: | ||||
| struct PrivateData; | struct PrivateData; | ||||
| @@ -27,6 +27,7 @@ OBJS_common = \ | |||||
| ../build/dgl/Application.cpp.o \ | ../build/dgl/Application.cpp.o \ | ||||
| ../build/dgl/ApplicationPrivateData.cpp.o \ | ../build/dgl/ApplicationPrivateData.cpp.o \ | ||||
| ../build/dgl/Color.cpp.o \ | ../build/dgl/Color.cpp.o \ | ||||
| ../build/dgl/EventHandlers.cpp.o \ | |||||
| ../build/dgl/Geometry.cpp.o \ | ../build/dgl/Geometry.cpp.o \ | ||||
| ../build/dgl/ImageBase.cpp.o \ | ../build/dgl/ImageBase.cpp.o \ | ||||
| ../build/dgl/ImageBaseWidgets.cpp.o \ | ../build/dgl/ImageBaseWidgets.cpp.o \ | ||||
| @@ -39,7 +40,6 @@ OBJS_common = \ | |||||
| ../build/dgl/WidgetPrivateData.cpp.o \ | ../build/dgl/WidgetPrivateData.cpp.o \ | ||||
| ../build/dgl/Window.cpp.o \ | ../build/dgl/Window.cpp.o \ | ||||
| ../build/dgl/WindowPrivateData.cpp.o | ../build/dgl/WindowPrivateData.cpp.o | ||||
| # ../build/dgl/WindowFileBrowser.cpp.o | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -111,6 +111,22 @@ public: | |||||
| */ | */ | ||||
| void setAbsolutePos(const Point<int>& pos) noexcept; | 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. | Get parent Widget, as passed in the constructor. | ||||
| */ | */ | ||||
| @@ -66,7 +66,7 @@ public: | |||||
| uint flags; | uint flags; | ||||
| uint time; | uint time; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} | ||||
| /** Destuctor */ | /** Destuctor */ | ||||
| virtual ~BaseEvent() noexcept {} | virtual ~BaseEvent() noexcept {} | ||||
| @@ -96,7 +96,7 @@ public: | |||||
| uint key; | uint key; | ||||
| uint keycode; | uint keycode; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| KeyboardEvent() noexcept | KeyboardEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| press(false), | press(false), | ||||
| @@ -118,7 +118,7 @@ public: | |||||
| bool press; | bool press; | ||||
| Key key; | Key key; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| SpecialEvent() noexcept | SpecialEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| press(false), | press(false), | ||||
| @@ -145,7 +145,7 @@ public: | |||||
| uint character; | uint character; | ||||
| char string[8]; | char string[8]; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| CharacterInputEvent() noexcept | CharacterInputEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| keycode(0), | keycode(0), | ||||
| @@ -168,7 +168,7 @@ public: | |||||
| Point<double> pos; | Point<double> pos; | ||||
| Point<double> absolutePos; | Point<double> absolutePos; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| MouseEvent() noexcept | MouseEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| button(0), | button(0), | ||||
| @@ -188,7 +188,7 @@ public: | |||||
| Point<double> pos; | Point<double> pos; | ||||
| Point<double> absolutePos; | Point<double> absolutePos; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| MotionEvent() noexcept | MotionEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| pos(0.0, 0.0), | pos(0.0, 0.0), | ||||
| @@ -216,7 +216,7 @@ public: | |||||
| Point<double> delta; | Point<double> delta; | ||||
| ScrollDirection direction; | ScrollDirection direction; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| ScrollEvent() noexcept | ScrollEvent() noexcept | ||||
| : BaseEvent(), | : BaseEvent(), | ||||
| pos(0.0, 0.0), | pos(0.0, 0.0), | ||||
| @@ -235,7 +235,7 @@ public: | |||||
| Size<uint> size; | Size<uint> size; | ||||
| Size<uint> oldSize; | Size<uint> oldSize; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| ResizeEvent() noexcept | ResizeEvent() noexcept | ||||
| : size(0, 0), | : size(0, 0), | ||||
| oldSize(0, 0) {} | oldSize(0, 0) {} | ||||
| @@ -251,7 +251,7 @@ public: | |||||
| Point<int> pos; | Point<int> pos; | ||||
| Point<int> oldPos; | Point<int> oldPos; | ||||
| /** Constuctor */ | |||||
| /** Constructor */ | |||||
| PositionChangedEvent() noexcept | PositionChangedEvent() noexcept | ||||
| : pos(0, 0), | : pos(0, 0), | ||||
| oldPos(0, 0) {} | oldPos(0, 0) {} | ||||
| @@ -87,14 +87,14 @@ public: | |||||
| /** Whether to show list of places (bookmarks) */ | /** Whether to show list of places (bookmarks) */ | ||||
| ButtonState showPlaces; | ButtonState showPlaces; | ||||
| /** Constuctor for default values */ | |||||
| /** Constructor for default values */ | |||||
| Buttons() | Buttons() | ||||
| : listAllFiles(kButtonVisibleChecked), | : listAllFiles(kButtonVisibleChecked), | ||||
| showHidden(kButtonVisibleUnchecked), | showHidden(kButtonVisibleUnchecked), | ||||
| showPlaces(kButtonVisibleUnchecked) {} | showPlaces(kButtonVisibleUnchecked) {} | ||||
| } buttons; | } buttons; | ||||
| /** Constuctor for default values */ | |||||
| /** Constructor for default values */ | |||||
| FileBrowserOptions() | FileBrowserOptions() | ||||
| : startDir(nullptr), | : startDir(nullptr), | ||||
| title(nullptr), | title(nullptr), | ||||
| @@ -104,6 +104,43 @@ public: | |||||
| }; | }; | ||||
| #endif // DGL_FILE_BROWSER_DISABLED | #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. | Constructor for a regular, standalone window. | ||||
| */ | */ | ||||
| @@ -362,9 +399,6 @@ public: | |||||
| DISTRHO_DEPRECATED_BY("runAsModal(bool)") | DISTRHO_DEPRECATED_BY("runAsModal(bool)") | ||||
| inline void exec(bool blockWait = false) { runAsModal(blockWait); } | inline void exec(bool blockWait = false) { runAsModal(blockWait); } | ||||
| // TESTING, DO NOT USE | |||||
| void leaveContext(); | |||||
| protected: | protected: | ||||
| /** | /** | ||||
| A function called when the window is attempted to be closed. | A function called when the window is attempted to be closed. | ||||
| @@ -414,6 +448,7 @@ private: | |||||
| struct PrivateData; | struct PrivateData; | ||||
| PrivateData* const pData; | PrivateData* const pData; | ||||
| friend class Application; | friend class Application; | ||||
| friend class PluginWindow; | |||||
| friend class TopLevelWidget; | friend class TopLevelWidget; | ||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); | ||||
| @@ -423,15 +458,4 @@ private: | |||||
| END_NAMESPACE_DGL | 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 | #endif // DGL_WINDOW_HPP_INCLUDED | ||||
| @@ -53,6 +53,11 @@ bool Application::isQuiting() const noexcept | |||||
| return pData->isQuitting || pData->isQuittingInNextCycle; | return pData->isQuitting || pData->isQuittingInNextCycle; | ||||
| } | } | ||||
| bool Application::isStandalone() const noexcept | |||||
| { | |||||
| return pData->isStandalone; | |||||
| } | |||||
| void Application::addIdleCallback(IdleCallback* const callback) | void Application::addIdleCallback(IdleCallback* const callback) | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | ||||
| @@ -25,7 +25,7 @@ START_NAMESPACE_DGL | |||||
| typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator; | typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator; | ||||
| static ThreadHandle getCurrentThreadHandle() noexcept | |||||
| static d_ThreadHandle getCurrentThreadHandle() noexcept | |||||
| { | { | ||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| return GetCurrentThread(); | return GetCurrentThread(); | ||||
| @@ -34,7 +34,7 @@ static ThreadHandle getCurrentThreadHandle() noexcept | |||||
| #endif | #endif | ||||
| } | } | ||||
| static bool isThisMainThread(const ThreadHandle mainThreadHandle) noexcept | |||||
| static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept | |||||
| { | { | ||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| return GetCurrentThread() == mainThreadHandle; // IsGUIThread ? | return GetCurrentThread() == mainThreadHandle; // IsGUIThread ? | ||||
| @@ -127,7 +127,7 @@ void Application::PrivateData::quit() | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); | DISTRHO_SAFE_ASSERT_RETURN(isStandalone,); | ||||
| if (! isThisMainThread(mainThreadHandle)) | |||||
| if (! isThisTheMainThread(mainThreadHandle)) | |||||
| { | { | ||||
| if (! isQuittingInNextCycle) | if (! isQuittingInNextCycle) | ||||
| { | { | ||||
| @@ -24,10 +24,10 @@ | |||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| # include <winsock2.h> | # include <winsock2.h> | ||||
| # include <windows.h> | # include <windows.h> | ||||
| typedef HANDLE ThreadHandle; | |||||
| typedef HANDLE d_ThreadHandle; | |||||
| #else | #else | ||||
| # include <pthread.h> | # include <pthread.h> | ||||
| typedef pthread_t ThreadHandle; | |||||
| typedef pthread_t d_ThreadHandle; | |||||
| #endif | #endif | ||||
| typedef struct PuglWorldImpl PuglWorld; | typedef struct PuglWorldImpl PuglWorld; | ||||
| @@ -59,7 +59,7 @@ struct Application::PrivateData { | |||||
| uint visibleWindows; | uint visibleWindows; | ||||
| /** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */ | /** 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`. */ | /** List of windows for this application. Only used during `close`. */ | ||||
| std::list<DGL_NAMESPACE::Window*> windows; | std::list<DGL_NAMESPACE::Window*> windows; | ||||
| @@ -24,7 +24,6 @@ | |||||
| #include "../Color.hpp" | #include "../Color.hpp" | ||||
| #include "../ImageBaseWidgets.hpp" | #include "../ImageBaseWidgets.hpp" | ||||
| #include "Common.hpp" | |||||
| #include "SubWidgetPrivateData.hpp" | #include "SubWidgetPrivateData.hpp" | ||||
| #include "TopLevelWidgetPrivateData.hpp" | #include "TopLevelWidgetPrivateData.hpp" | ||||
| #include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
| @@ -657,8 +656,7 @@ void ImageBaseKnob<CairoImage>::onDisplay() | |||||
| { | { | ||||
| const GraphicsContext& context(getGraphicsContext()); | const GraphicsContext& context(getGraphicsContext()); | ||||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | 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; | 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); | 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) | Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | ||||
| { | { | ||||
| float m1, m2; | 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 "../ImageBaseWidgets.hpp" | ||||
| #include "../Color.hpp" | #include "../Color.hpp" | ||||
| #include "Common.hpp" | |||||
| START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
| @@ -89,18 +88,25 @@ bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev) | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| template <class ImageType> | template <class ImageType> | ||||
| struct ImageBaseButton<ImageType>::PrivateData { | |||||
| ButtonImpl<ImageType> impl; | |||||
| struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback { | |||||
| ImageBaseButton<ImageType>::Callback* callback; | |||||
| ImageType imageNormal; | ImageType imageNormal; | ||||
| ImageType imageHover; | ImageType imageHover; | ||||
| ImageType imageDown; | 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), | imageNormal(normal), | ||||
| imageHover(hover), | imageHover(hover), | ||||
| imageDown(down) {} | 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) | DISTRHO_DECLARE_NON_COPYABLE(PrivateData) | ||||
| }; | }; | ||||
| @@ -109,28 +115,34 @@ struct ImageBaseButton<ImageType>::PrivateData { | |||||
| template <class ImageType> | template <class ImageType> | ||||
| ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) | ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image) | ||||
| : SubWidget(parentWidget), | : SubWidget(parentWidget), | ||||
| pData(new PrivateData(this, image, image, image)) | |||||
| ButtonEventHandler(this), | |||||
| pData(new PrivateData(image, image, image)) | |||||
| { | { | ||||
| ButtonEventHandler::setCallback(pData); | |||||
| setSize(image.getSize()); | setSize(image.getSize()); | ||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) | ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown) | ||||
| : SubWidget(parentWidget), | : SubWidget(parentWidget), | ||||
| pData(new PrivateData(this, imageNormal, imageNormal, imageDown)) | |||||
| ButtonEventHandler(this), | |||||
| pData(new PrivateData(imageNormal, imageNormal, imageDown)) | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize()); | ||||
| ButtonEventHandler::setCallback(pData); | |||||
| setSize(imageNormal.getSize()); | setSize(imageNormal.getSize()); | ||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) | ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown) | ||||
| : SubWidget(parentWidget), | : 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()); | DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize()); | ||||
| ButtonEventHandler::setCallback(pData); | |||||
| setSize(imageNormal.getSize()); | setSize(imageNormal.getSize()); | ||||
| } | } | ||||
| @@ -143,7 +155,7 @@ ImageBaseButton<ImageType>::~ImageBaseButton() | |||||
| template <class ImageType> | template <class ImageType> | ||||
| void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept | ||||
| { | { | ||||
| pData->impl.callback_img = callback; | |||||
| pData->callback = callback; | |||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| @@ -151,137 +163,162 @@ void ImageBaseButton<ImageType>::onDisplay() | |||||
| { | { | ||||
| const GraphicsContext& context(getGraphicsContext()); | const GraphicsContext& context(getGraphicsContext()); | ||||
| switch (pData->impl.state) | |||||
| { | |||||
| case ButtonImpl<ImageType>::kStateDown: | |||||
| const State state = ButtonEventHandler::getState(); | |||||
| if (state & kButtonStateActive) | |||||
| pData->imageDown.draw(context); | pData->imageDown.draw(context); | ||||
| break; | |||||
| case ButtonImpl<ImageType>::kStateHover: | |||||
| else if (state & kButtonStateHover) | |||||
| pData->imageHover.draw(context); | pData->imageHover.draw(context); | ||||
| break; | |||||
| default: | |||||
| else | |||||
| pData->imageNormal.draw(context); | pData->imageNormal.draw(context); | ||||
| break; | |||||
| } | |||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev) | 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> | template <class ImageType> | ||||
| bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev) | 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> | 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> | 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), | : SubWidget(parentWidget), | ||||
| pData(new PrivateData(image, orientation)) | |||||
| KnobEventHandler(this), | |||||
| pData(new PrivateData(image)) | |||||
| { | { | ||||
| KnobEventHandler::setCallback(pData); | |||||
| setOrientation(orientation); | |||||
| setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob) | ||||
| : SubWidget(imageKnob.getParentWidget()), | : SubWidget(imageKnob.getParentWidget()), | ||||
| KnobEventHandler(this, imageKnob), | |||||
| pData(new PrivateData(imageKnob.pData)) | pData(new PrivateData(imageKnob.pData)) | ||||
| { | { | ||||
| KnobEventHandler::setCallback(pData); | |||||
| setOrientation(imageKnob.getOrientation()); | |||||
| setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
| } | } | ||||
| template <class ImageType> | template <class ImageType> | ||||
| ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob) | ||||
| { | { | ||||
| KnobEventHandler::operator=(imageKnob); | |||||
| pData->assignFrom(imageKnob.pData); | pData->assignFrom(imageKnob.pData); | ||||
| setSize(pData->imgLayerWidth, pData->imgLayerHeight); | setSize(pData->imgLayerWidth, pData->imgLayerHeight); | ||||
| return *this; | return *this; | ||||
| @@ -293,116 +330,12 @@ ImageBaseKnob<ImageType>::~ImageBaseKnob() | |||||
| delete pData; | 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> | template <class ImageType> | ||||
| void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept | ||||
| { | { | ||||
| pData->callback = callback; | 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> | template <class ImageType> | ||||
| void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | ||||
| { | { | ||||
| @@ -419,38 +352,23 @@ void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept | |||||
| } | } | ||||
| template <class ImageType> | 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; | return true; | ||||
| } | } | ||||
| @@ -458,93 +376,27 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||||
| } | } | ||||
| template <class ImageType> | 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> | template <class ImageType> | ||||
| bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev) | 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; | return; | ||||
| if (TopLevelWidget* const tlw = widget->getTopLevelWidget()) | 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() | void NanoVG::cancelFrame() | ||||
| @@ -23,7 +23,6 @@ | |||||
| #include "../Color.hpp" | #include "../Color.hpp" | ||||
| #include "../ImageWidgets.hpp" | #include "../ImageWidgets.hpp" | ||||
| #include "Common.hpp" | |||||
| #include "SubWidgetPrivateData.hpp" | #include "SubWidgetPrivateData.hpp" | ||||
| #include "TopLevelWidgetPrivateData.hpp" | #include "TopLevelWidgetPrivateData.hpp" | ||||
| #include "WidgetPrivateData.hpp" | #include "WidgetPrivateData.hpp" | ||||
| @@ -317,6 +316,8 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, con | |||||
| setupCalled = true; | setupCalled = true; | ||||
| } | } | ||||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||||
| glEnable(GL_TEXTURE_2D); | glEnable(GL_TEXTURE_2D); | ||||
| glBindTexture(GL_TEXTURE_2D, textureId); | glBindTexture(GL_TEXTURE_2D, textureId); | ||||
| @@ -486,8 +487,7 @@ template <> | |||||
| void ImageBaseKnob<OpenGLImage>::onDisplay() | void ImageBaseKnob<OpenGLImage>::onDisplay() | ||||
| { | { | ||||
| const GraphicsContext& context(getGraphicsContext()); | 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); | glEnable(GL_TEXTURE_2D); | ||||
| glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | ||||
| @@ -32,9 +32,9 @@ SubWidget::~SubWidget() | |||||
| } | } | ||||
| template<typename T> | 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> | template<typename T> | ||||
| @@ -70,17 +70,17 @@ Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept | |||||
| getSize()); | getSize()); | ||||
| } | } | ||||
| void SubWidget::setAbsoluteX(int x) noexcept | |||||
| void SubWidget::setAbsoluteX(const int x) noexcept | |||||
| { | { | ||||
| setAbsolutePos(Point<int>(x, getAbsoluteY())); | setAbsolutePos(Point<int>(x, getAbsoluteY())); | ||||
| } | } | ||||
| void SubWidget::setAbsoluteY(int y) noexcept | |||||
| void SubWidget::setAbsoluteY(const int y) noexcept | |||||
| { | { | ||||
| setAbsolutePos(Point<int>(getAbsoluteX(), y)); | 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)); | setAbsolutePos(Point<int>(x, y)); | ||||
| } | } | ||||
| @@ -100,6 +100,21 @@ void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept | |||||
| repaint(); | 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 | Widget* SubWidget::getParentWidget() const noexcept | ||||
| { | { | ||||
| return pData->parentWidget; | return pData->parentWidget; | ||||
| @@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) | |||||
| selfw((Widget*)s), | selfw((Widget*)s), | ||||
| parentWidget(pw), | parentWidget(pw), | ||||
| absolutePos(), | absolutePos(), | ||||
| margin(), | |||||
| needsFullViewportForDrawing(false), | needsFullViewportForDrawing(false), | ||||
| needsViewportScaling(false), | needsViewportScaling(false), | ||||
| skipDrawing(false), | skipDrawing(false), | ||||
| @@ -28,6 +28,7 @@ struct SubWidget::PrivateData { | |||||
| Widget* const selfw; | Widget* const selfw; | ||||
| Widget* const parentWidget; | Widget* const parentWidget; | ||||
| Point<int> absolutePos; | Point<int> absolutePos; | ||||
| Point<int> margin; | |||||
| bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds | bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds | ||||
| bool needsViewportScaling; // needed for NanoVG | bool needsViewportScaling; // needed for NanoVG | ||||
| bool skipDrawing; // for context reuse in NanoVG based guis | bool skipDrawing; // for context reuse in NanoVG based guis | ||||
| @@ -152,8 +152,8 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) | |||||
| if (! widget->isVisible()) | if (! widget->isVisible()) | ||||
| continue; | 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)) | if (widget->onMouse(ev)) | ||||
| return true; | return true; | ||||
| @@ -191,8 +191,8 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) | |||||
| if (! widget->isVisible()) | if (! widget->isVisible()) | ||||
| continue; | 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)) | if (widget->onMotion(ev)) | ||||
| return true; | return true; | ||||
| @@ -230,8 +230,8 @@ bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) | |||||
| if (! widget->isVisible()) | if (! widget->isVisible()) | ||||
| continue; | 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)) | if (widget->onScroll(ev)) | ||||
| return true; | return true; | ||||
| @@ -20,6 +20,20 @@ | |||||
| START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
| // ----------------------------------------------------------------------- | |||||
| // ScopedGraphicsContext | |||||
| Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win) | |||||
| : window(win) | |||||
| { | |||||
| puglBackendEnter(window.pData->view); | |||||
| } | |||||
| Window::ScopedGraphicsContext::~ScopedGraphicsContext() | |||||
| { | |||||
| puglBackendLeave(window.pData->view); | |||||
| } | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // Window | // Window | ||||
| @@ -265,11 +279,6 @@ void Window::runAsModal(bool blockWait) | |||||
| pData->runAsModal(blockWait); | pData->runAsModal(blockWait); | ||||
| } | } | ||||
| void Window::leaveContext() | |||||
| { | |||||
| pData->leaveContext(); | |||||
| } | |||||
| void Window::setGeometryConstraints(const uint minimumWidth, | void Window::setGeometryConstraints(const uint minimumWidth, | ||||
| const uint minimumHeight, | const uint minimumHeight, | ||||
| const bool keepAspectRatio, | const bool keepAspectRatio, | ||||
| @@ -79,6 +79,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) | |||||
| appData(a.pData), | appData(a.pData), | ||||
| self(s), | self(s), | ||||
| view(puglNewView(appData->world)), | view(puglNewView(appData->world)), | ||||
| transientParentView(nullptr), | |||||
| topLevelWidgets(), | topLevelWidgets(), | ||||
| isClosed(true), | isClosed(true), | ||||
| isVisible(false), | isVisible(false), | ||||
| @@ -102,6 +103,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||||
| appData(a.pData), | appData(a.pData), | ||||
| self(s), | self(s), | ||||
| view(puglNewView(appData->world)), | view(puglNewView(appData->world)), | ||||
| transientParentView(ppData->view), | |||||
| topLevelWidgets(), | topLevelWidgets(), | ||||
| isClosed(true), | isClosed(true), | ||||
| isVisible(false), | isVisible(false), | ||||
| @@ -117,9 +119,10 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||||
| #endif | #endif | ||||
| modal(ppData) | 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, | Window::PrivateData::PrivateData(Application& a, Window* const s, | ||||
| @@ -129,6 +132,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
| appData(a.pData), | appData(a.pData), | ||||
| self(s), | self(s), | ||||
| view(puglNewView(appData->world)), | view(puglNewView(appData->world)), | ||||
| transientParentView(nullptr), | |||||
| topLevelWidgets(), | topLevelWidgets(), | ||||
| isClosed(parentWindowHandle == 0), | isClosed(parentWindowHandle == 0), | ||||
| isVisible(parentWindowHandle != 0), | isVisible(parentWindowHandle != 0), | ||||
| @@ -158,6 +162,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
| appData(a.pData), | appData(a.pData), | ||||
| self(s), | self(s), | ||||
| view(puglNewView(appData->world)), | view(puglNewView(appData->world)), | ||||
| transientParentView(nullptr), | |||||
| topLevelWidgets(), | topLevelWidgets(), | ||||
| isClosed(parentWindowHandle == 0), | isClosed(parentWindowHandle == 0), | ||||
| isVisible(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 | // create view now, as a few methods we allow devs to use require it | ||||
| puglRealize(view); | 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) | if (isEmbed) | ||||
| { | { | ||||
| appData->oneWindowShown(); | appData->oneWindowShown(); | ||||
| puglShow(view); | 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 | // pugl events | ||||
| @@ -44,6 +44,9 @@ struct Window::PrivateData : IdleCallback { | |||||
| /** Pugl view instance. */ | /** Pugl view instance. */ | ||||
| PuglView* const view; | PuglView* const view; | ||||
| /** Pugl view instance of the transient parent window. */ | |||||
| PuglView* const transientParentView; | |||||
| /** Reserved space for graphics context. */ | /** Reserved space for graphics context. */ | ||||
| mutable uint8_t graphicsContext[sizeof(void*)]; | mutable uint8_t graphicsContext[sizeof(void*)]; | ||||
| @@ -161,9 +164,6 @@ struct Window::PrivateData : IdleCallback { | |||||
| void stopModal(); | void stopModal(); | ||||
| void runAsModal(bool blockWait); | void runAsModal(bool blockWait); | ||||
| // TESTING | |||||
| void leaveContext(); | |||||
| // pugl events | // pugl events | ||||
| void onPuglConfigure(double width, double height); | void onPuglConfigure(double width, double height); | ||||
| void onPuglExpose(); | void onPuglExpose(); | ||||
| @@ -118,8 +118,7 @@ puglWinGetWindowExFlags(const PuglView* const view) | |||||
| } | } | ||||
| PuglWorldInternals* | PuglWorldInternals* | ||||
| puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type), | |||||
| PuglWorldFlags PUGL_UNUSED(flags)) | |||||
| puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) | |||||
| { | { | ||||
| PuglWorldInternals* impl = | PuglWorldInternals* impl = | ||||
| (PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); | (PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); | ||||
| @@ -127,15 +126,17 @@ puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type), | |||||
| return NULL; | 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; | LARGE_INTEGER frequency; | ||||
| @@ -177,10 +177,18 @@ void puglClearMinSize(PuglView* const view) | |||||
| view->minHeight = 0; | view->minHeight = 0; | ||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // missing in pugl, directly returns transient parent | |||||
| PuglNativeView puglGetTransientParent(const PuglView* const view) | |||||
| { | |||||
| return view->transientParent; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| // missing in pugl, directly returns title char* pointer | // missing in pugl, directly returns title char* pointer | ||||
| const char* puglGetWindowTitle(const PuglView* view) | |||||
| const char* puglGetWindowTitle(const PuglView* const view) | |||||
| { | { | ||||
| return view->title; | return view->title; | ||||
| } | } | ||||
| @@ -54,6 +54,10 @@ puglBackendLeave(PuglView* view); | |||||
| PUGL_API void | PUGL_API void | ||||
| puglClearMinSize(PuglView* view); | 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 | // missing in pugl, directly returns title char* pointer | ||||
| PUGL_API const char* | PUGL_API const char* | ||||
| puglGetWindowTitle(const PuglView* view); | puglGetWindowTitle(const PuglView* view); | ||||
| @@ -51,20 +51,20 @@ static const uint32_t kAudioPortIsSidechain = 0x2; | |||||
| static const uint32_t kCVPortHasBipolarRange = 0x10; | 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. | This is merely a hint to tell the host what value range to expect. | ||||
| */ | */ | ||||
| static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20; | 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. | This is merely a hint to tell the host what value range to expect. | ||||
| */ | */ | ||||
| static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40; | static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40; | ||||
| /** | /** | ||||
| CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar). | 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. | 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; | typedef DGL_NAMESPACE::TopLevelWidget UIWidget; | ||||
| #endif | #endif | ||||
| START_NAMESPACE_DGL | |||||
| class PluginWindow; | |||||
| END_NAMESPACE_DGL | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| /* ------------------------------------------------------------------------------------------------------------ | /* ------------------------------------------------------------------------------------------------------------ | ||||
| @@ -311,7 +315,7 @@ protected: | |||||
| private: | private: | ||||
| struct PrivateData; | struct PrivateData; | ||||
| PrivateData* const uiData; | PrivateData* const uiData; | ||||
| friend class PluginWindow; | |||||
| friend class DGL_NAMESPACE::PluginWindow; | |||||
| friend class UIExporter; | friend class UIExporter; | ||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) | ||||
| @@ -20,6 +20,9 @@ | |||||
| #include "../DistrhoUtils.hpp" | #include "../DistrhoUtils.hpp" | ||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| # ifndef NOMINMAX | |||||
| # define NOMINMAX | |||||
| # endif | |||||
| # include <winsock2.h> | # include <winsock2.h> | ||||
| # include <windows.h> | # include <windows.h> | ||||
| typedef HMODULE lib_t; | typedef HMODULE lib_t; | ||||
| @@ -20,6 +20,9 @@ | |||||
| #include "../DistrhoUtils.hpp" | #include "../DistrhoUtils.hpp" | ||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| # ifndef NOMINMAX | |||||
| # define NOMINMAX | |||||
| # endif | |||||
| # include <winsock2.h> | # include <winsock2.h> | ||||
| # include <windows.h> | # include <windows.h> | ||||
| #endif | #endif | ||||
| @@ -20,6 +20,9 @@ | |||||
| #include "../DistrhoUtils.hpp" | #include "../DistrhoUtils.hpp" | ||||
| #ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
| # ifndef NOMINMAX | |||||
| # define NOMINMAX | |||||
| # endif | |||||
| # include <winsock2.h> | # include <winsock2.h> | ||||
| # include <windows.h> | # include <windows.h> | ||||
| #else | #else | ||||
| @@ -70,6 +70,7 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // Plugin private data | // Plugin private data | ||||
| @@ -157,9 +158,9 @@ struct Plugin::PrivateData { | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_LV2 | #ifdef DISTRHO_PLUGIN_TARGET_LV2 | ||||
| # if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | # if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | ||||
| parameterOffset += 1; | parameterOffset += 1; | ||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| # endif | |||||
| # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||||
| parameterOffset += 1; | parameterOffset += 1; | ||||
| # endif | |||||
| # endif | # endif | ||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -38,6 +38,10 @@ | |||||
| # define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name" | # define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name" | ||||
| #endif | #endif | ||||
| #ifndef JACK_METADATA_PORT_GROUP | |||||
| # define JACK_METADATA_PORT_GROUP "http://jackaudio.org/metadata/port-group" | |||||
| #endif | |||||
| #ifndef JACK_METADATA_SIGNAL_TYPE | #ifndef JACK_METADATA_SIGNAL_TYPE | ||||
| # define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type" | # define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type" | ||||
| #endif | #endif | ||||
| @@ -351,6 +355,12 @@ protected: | |||||
| fTimePosition.bbt.bar = pos.bar; | fTimePosition.bbt.bar = pos.bar; | ||||
| fTimePosition.bbt.beat = pos.beat; | fTimePosition.bbt.beat = pos.beat; | ||||
| fTimePosition.bbt.tick = pos.tick; | 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.barStartTick = pos.bar_start_tick; | ||||
| fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; | 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"); | 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) | if (port.hints & kAudioPortIsCV) | ||||
| { | { | ||||
| jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain"); | jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain"); | ||||
| @@ -236,7 +236,7 @@ public: | |||||
| fTimePosition.bbt.barStartTick = 0; | fTimePosition.bbt.barStartTick = 0; | ||||
| fTimePosition.bbt.beatsPerBar = 4; | fTimePosition.bbt.beatsPerBar = 4; | ||||
| fTimePosition.bbt.beatType = 4; | fTimePosition.bbt.beatType = 4; | ||||
| fTimePosition.bbt.ticksPerBeat = 960.0; | |||||
| fTimePosition.bbt.ticksPerBeat = 1920.0; | |||||
| fTimePosition.bbt.beatsPerMinute = 120.0; | fTimePosition.bbt.beatsPerMinute = 120.0; | ||||
| #endif | #endif | ||||
| fPlugin.activate(); | fPlugin.activate(); | ||||
| @@ -359,7 +359,7 @@ public: | |||||
| if (obj->body.otype != fURIDs.timePosition) | if (obj->body.otype != fURIDs.timePosition) | ||||
| continue; | continue; | ||||
| LV2_Atom* bar = nullptr; | |||||
| LV2_Atom* bar = nullptr; | |||||
| LV2_Atom* barBeat = nullptr; | LV2_Atom* barBeat = nullptr; | ||||
| LV2_Atom* beatUnit = nullptr; | LV2_Atom* beatUnit = nullptr; | ||||
| LV2_Atom* beatsPerBar = nullptr; | LV2_Atom* beatsPerBar = nullptr; | ||||
| @@ -430,9 +430,21 @@ void lv2_generate_ttl(const char* const basename) | |||||
| if (port.hints & kAudioPortIsSidechain) | if (port.hints & kAudioPortIsSidechain) | ||||
| pluginString += " lv2:portProperty lv2:isSideChain;\n"; | 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_" | pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | ||||
| + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | ||||
| break; | |||||
| } | |||||
| // set ranges | // set ranges | ||||
| if (port.hints & kCVPortHasBipolarRange) | if (port.hints & kCVPortHasBipolarRange) | ||||
| @@ -508,9 +520,21 @@ void lv2_generate_ttl(const char* const basename) | |||||
| if (port.hints & kAudioPortIsSidechain) | if (port.hints & kAudioPortIsSidechain) | ||||
| pluginString += " lv2:portProperty lv2:isSideChain;\n"; | 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_" | pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | ||||
| + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; | ||||
| break; | |||||
| } | |||||
| // set ranges | // set ranges | ||||
| if (port.hints & kCVPortHasBipolarRange) | if (port.hints & kCVPortHasBipolarRange) | ||||
| @@ -731,7 +755,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
| // unit | // unit | ||||
| const String& unit(plugin.getParameterUnit(i)); | const String& unit(plugin.getParameterUnit(i)); | ||||
| if (unit.isNotEmpty() && ! unit.contains(" ")) | |||||
| if (unit.isNotEmpty() && ! unit.contains(' ')) | |||||
| { | { | ||||
| String lunit(unit); | String lunit(unit); | ||||
| lunit.toLower(); | lunit.toLower(); | ||||
| @@ -770,7 +794,10 @@ void lv2_generate_ttl(const char* const basename) | |||||
| pluginString += " a unit:Unit ;\n"; | pluginString += " a unit:Unit ;\n"; | ||||
| pluginString += " rdfs:label \"" + unit + "\" ;\n"; | pluginString += " rdfs:label \"" + unit + "\" ;\n"; | ||||
| pluginString += " unit:symbol \"" + 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"; | pluginString += " ] ;\n"; | ||||
| } | } | ||||
| } | } | ||||
| @@ -779,7 +806,12 @@ void lv2_generate_ttl(const char* const basename) | |||||
| const String& comment(plugin.getParameterDescription(i)); | const String& comment(plugin.getParameterDescription(i)); | ||||
| if (comment.isNotEmpty()) | 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 | // hints | ||||
| const uint32_t hints(plugin.getParameterHints(i)); | const uint32_t hints(plugin.getParameterHints(i)); | ||||
| @@ -805,9 +837,22 @@ void lv2_generate_ttl(const char* const basename) | |||||
| // group | // group | ||||
| const uint32_t groupId = plugin.getParameterGroupId(i); | 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_" | pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" | ||||
| + plugin.getPortGroupSymbolForId(groupId) + "> ;\n"; | + plugin.getPortGroupSymbolForId(groupId) + "> ;\n"; | ||||
| break; | |||||
| } | |||||
| } // ! designated | } // ! designated | ||||
| if (i+1 == count) | if (i+1 == count) | ||||
| @@ -823,8 +868,8 @@ void lv2_generate_ttl(const char* const basename) | |||||
| if (comment.isNotEmpty()) | 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 | else | ||||
| pluginString += " rdfs:comment \"" + comment + "\" ;\n\n"; | 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.groupId != kPortGroupNone); | ||||
| DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.symbol.isNotEmpty()); | DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.symbol.isNotEmpty()); | ||||
| switch (portGroup.groupId) | |||||
| { | |||||
| case kPortGroupMono: | |||||
| case kPortGroupStereo: | |||||
| continue; | |||||
| } | |||||
| pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + portGroup.symbol + ">\n"; | pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + portGroup.symbol + ">\n"; | ||||
| isInput = isOutput = false; | isInput = isOutput = false; | ||||
| @@ -1063,7 +1063,7 @@ public: | |||||
| fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0); | fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0); | ||||
| // ticksPerBeat is not possible with VST | // ticksPerBeat is not possible with VST | ||||
| fTimePosition.bbt.ticksPerBeat = 960.0; | |||||
| fTimePosition.bbt.ticksPerBeat = 1920.0; | |||||
| if (vstTimeInfo->flags & kVstTempoValid) | if (vstTimeInfo->flags & kVstTempoValid) | ||||
| fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo; | fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo; | ||||
| @@ -1099,7 +1099,9 @@ public: | |||||
| fTimePosition.bbt.beatType = 4.0f; | 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); | fPlugin.setTimePosition(fTimePosition); | ||||
| } | } | ||||
| @@ -1448,7 +1450,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||||
| memset(properties, 0, sizeof(VstParameterProperties)); | memset(properties, 0, sizeof(VstParameterProperties)); | ||||
| // full name | // full name | ||||
| DISTRHO_NAMESPACE::strncpy(properties->label, plugin.getParameterName(index), VestigeMaxLabelLen); | |||||
| DISTRHO_NAMESPACE::strncpy(properties->label, | |||||
| plugin.getParameterName(index), | |||||
| sizeof(properties->label)); | |||||
| // short name | // short name | ||||
| const String& shortName(plugin.getParameterShortName(index)); | 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()) | if (shortName.isNotEmpty()) | ||||
| DISTRHO_NAMESPACE::strncpy(properties->shortLabel, | DISTRHO_NAMESPACE::strncpy(properties->shortLabel, | ||||
| plugin.getParameterShortName(index), | plugin.getParameterShortName(index), | ||||
| VestigeMaxShortLabelLen); | |||||
| sizeof(properties->shortLabel)); | |||||
| // parameter hints | // parameter hints | ||||
| const uint32_t hints = plugin.getParameterHints(index); | 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; | 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; | return 1; | ||||
| } | } | ||||
| } | } | ||||
| @@ -96,7 +96,7 @@ private: | |||||
| static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) | 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 | #endif | ||||
| @@ -109,7 +109,7 @@ private: | |||||
| static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | ||||
| { | { | ||||
| return ((PluginVst*)ptr)->writeMidi(midiEvent); | |||||
| return ((PluginVst3*)ptr)->writeMidi(midiEvent); | |||||
| } | } | ||||
| #endif | #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) | PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height) | ||||
| { | { | ||||
| UI::PrivateData* const pData = s_nextPrivateData; | 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(); | return pData->window.getObject(); | ||||
| } | } | ||||
| @@ -260,7 +260,9 @@ public: | |||||
| void quit() | void quit() | ||||
| { | { | ||||
| uiData->window->close(); | uiData->window->close(); | ||||
| uiData->app.quit(); | |||||
| if (uiData->app.isStandalone()) | |||||
| uiData->app.quit(); | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -316,20 +318,23 @@ public: | |||||
| return ! uiData->app.isQuiting(); | 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 | #endif | ||||
| @@ -89,11 +89,14 @@ public: | |||||
| bgColor, | bgColor, | ||||
| fgColor), | fgColor), | ||||
| fUridMap(uridMap), | fUridMap(uridMap), | ||||
| fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), | |||||
| fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | ||||
| fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | ||||
| fController(controller), | fController(controller), | ||||
| fWriteFunction(writeFunc), | fWriteFunction(writeFunc), | ||||
| fURIDs(uridMap), | fURIDs(uridMap), | ||||
| fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled") | |||||
| : LV2UI_INVALID_PORT_INDEX), | |||||
| fWinIdWasNull(winId == 0) | fWinIdWasNull(winId == 0) | ||||
| { | { | ||||
| if (widget != nullptr) | if (widget != nullptr) | ||||
| @@ -159,7 +162,11 @@ public: | |||||
| DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | 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); | fUI.parameterChanged(rindex-parameterOffset, value); | ||||
| } | } | ||||
| #if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
| @@ -253,10 +260,13 @@ protected: | |||||
| fUiTouch->touch(fUiTouch->handle, rindex, started); | 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,); | DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | ||||
| if (rindex == fBypassParameterIndex) | |||||
| value = 1.0f - value; | |||||
| fWriteFunction(fController, rindex, sizeof(float), 0, &value); | fWriteFunction(fController, rindex, sizeof(float), 0, &value); | ||||
| } | } | ||||
| @@ -345,6 +355,7 @@ private: | |||||
| // LV2 features | // LV2 features | ||||
| const LV2_URID_Map* const fUridMap; | const LV2_URID_Map* const fUridMap; | ||||
| const LV2UI_Port_Map* const fUiPortMap; | |||||
| const LV2UI_Request_Value* const fUiRequestValue; | const LV2UI_Request_Value* const fUiRequestValue; | ||||
| const LV2UI_Touch* const fUiTouch; | const LV2UI_Touch* const fUiTouch; | ||||
| @@ -383,8 +394,11 @@ private: | |||||
| } | } | ||||
| } fURIDs; | } fURIDs; | ||||
| // index of bypass parameter, if present | |||||
| const uint32_t fBypassParameterIndex; | |||||
| // using ui:showInterface if true | // using ui:showInterface if true | ||||
| bool fWinIdWasNull; | |||||
| const bool fWinIdWasNull; | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Callbacks | // Callbacks | ||||
| @@ -20,8 +20,9 @@ | |||||
| #include "../DistrhoUI.hpp" | #include "../DistrhoUI.hpp" | ||||
| #include "../../dgl/Application.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 | #endif | ||||
| #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | ||||
| @@ -35,20 +36,9 @@ | |||||
| # define DISTRHO_UI_USER_RESIZABLE 0 | # define DISTRHO_UI_USER_RESIZABLE 0 | ||||
| #endif | #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 | // Plugin Application, will set class name based on plugin details | ||||
| @@ -73,7 +63,118 @@ public: | |||||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) | 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 | // UI private data | ||||
| @@ -143,16 +244,18 @@ struct UI::PrivateData { | |||||
| #ifdef DISTRHO_PLUGIN_TARGET_LV2 | #ifdef DISTRHO_PLUGIN_TARGET_LV2 | ||||
| # if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | # if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | ||||
| parameterOffset += 1; | parameterOffset += 1; | ||||
| # if DISTRHO_PLUGIN_WANT_STATE | |||||
| # endif | |||||
| # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |||||
| parameterOffset += 1; | parameterOffset += 1; | ||||
| # endif | |||||
| # endif | # endif | ||||
| #endif | #endif | ||||
| } | } | ||||
| ~PrivateData() noexcept | ~PrivateData() noexcept | ||||
| { | { | ||||
| #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||||
| std::free(uiStateFileKeyRequest); | std::free(uiStateFileKeyRequest); | ||||
| #endif | |||||
| } | } | ||||
| void editParamCallback(const uint32_t rindex, const bool started) | 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); | 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 | // UI private data fileRequestCallback, which requires PluginWindow definitions | ||||
| @@ -311,8 +320,37 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||||
| return false; | 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 | #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_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */ | ||||
| #define JACK_UUID_EMPTY_INITIALIZER 0 | #define JACK_UUID_EMPTY_INITIALIZER 0 | ||||
| #define JACK_TICK_DOUBLE | |||||
| extern "C" { | extern "C" { | ||||
| enum JackOptions { | enum JackOptions { | ||||
| @@ -153,7 +155,8 @@ enum JackPositionBits { | |||||
| JackPositionTimecode = 0x020, | JackPositionTimecode = 0x020, | ||||
| JackBBTFrameOffset = 0x040, | JackBBTFrameOffset = 0x040, | ||||
| JackAudioVideoRatio = 0x080, | JackAudioVideoRatio = 0x080, | ||||
| JackVideoFrameOffset = 0x100 | |||||
| JackVideoFrameOffset = 0x100, | |||||
| JackTickDouble = 0x200 | |||||
| }; | }; | ||||
| enum JackSessionEventType { | enum JackSessionEventType { | ||||
| @@ -223,7 +226,8 @@ struct _jack_position { | |||||
| jack_nframes_t bbt_offset; | jack_nframes_t bbt_offset; | ||||
| float audio_frames_per_video_frame; | float audio_frames_per_video_frame; | ||||
| jack_nframes_t video_offset; | jack_nframes_t video_offset; | ||||
| int32_t padding[7]; | |||||
| double tick_double; | |||||
| int32_t padding[5]; | |||||
| jack_unique_t unique_2; | jack_unique_t unique_2; | ||||
| } POST_PACKED_STRUCTURE; | } 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) | DemoWindow(Application& app) | ||||
| : StandaloneWindow(app), | : StandaloneWindow(app), | ||||
| wColor(this), | |||||
| wImages(this), | |||||
| wRects(this), | |||||
| wShapes(this), | |||||
| #ifdef DGL_OPENGL | |||||
| wText(this), | |||||
| #endif | |||||
| wLeft(this, this), | |||||
| resizer(this), | |||||
| curWidget(nullptr) | 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 | #ifdef DGL_OPENGL | ||||
| wText.setAbsoluteX(kSidebarWidth); | |||||
| wText = new ExampleTextSubWidget(this), | |||||
| wText->hide(); | |||||
| wText->setAbsoluteX(kSidebarWidth); | |||||
| #endif | #endif | ||||
| wLeft.setAbsolutePos(2, 2); | |||||
| wLeft = new LeftSideWidget(this, this), | |||||
| wLeft->setAbsolutePos(2, 2); | |||||
| resizer = new ResizeHandle(this); | |||||
| curPageChanged(0); | curPageChanged(0); | ||||
| } | } | ||||
| @@ -444,20 +446,20 @@ protected: | |||||
| switch (curPage) | switch (curPage) | ||||
| { | { | ||||
| case 0: | case 0: | ||||
| curWidget = &wColor; | |||||
| curWidget = wColor; | |||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| curWidget = &wImages; | |||||
| curWidget = wImages; | |||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| curWidget = &wRects; | |||||
| curWidget = wRects; | |||||
| break; | break; | ||||
| case 3: | case 3: | ||||
| curWidget = &wShapes; | |||||
| curWidget = wShapes; | |||||
| break; | break; | ||||
| #ifdef DGL_OPENGL | #ifdef DGL_OPENGL | ||||
| case 4: | case 4: | ||||
| curWidget = &wText; | |||||
| curWidget = wText; | |||||
| break; | break; | ||||
| #endif | #endif | ||||
| default: | default: | ||||
| @@ -481,26 +483,26 @@ protected: | |||||
| return; | return; | ||||
| Size<uint> size(width-kSidebarWidth, height); | 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 | #ifdef DGL_OPENGL | ||||
| wText.setSize(size); | |||||
| wText->setSize(size); | |||||
| #endif | #endif | ||||
| wLeft.setSize(kSidebarWidth-4, height-4); | |||||
| wLeft->setSize(kSidebarWidth-4, height-4); | |||||
| } | } | ||||
| private: | private: | ||||
| ExampleColorSubWidget wColor; | |||||
| ExampleImagesSubWidget wImages; | |||||
| ExampleRectanglesSubWidget wRects; | |||||
| ExampleShapesSubWidget wShapes; | |||||
| ScopedPointer<ExampleColorSubWidget> wColor; | |||||
| ScopedPointer<ExampleImagesSubWidget> wImages; | |||||
| ScopedPointer<ExampleRectanglesSubWidget> wRects; | |||||
| ScopedPointer<ExampleShapesSubWidget> wShapes; | |||||
| #ifdef DGL_OPENGL | #ifdef DGL_OPENGL | ||||
| ExampleTextSubWidget wText; | |||||
| ScopedPointer<ExampleTextSubWidget> wText; | |||||
| #endif | #endif | ||||
| LeftSideWidget wLeft; | |||||
| ResizeHandle resizer; | |||||
| ScopedPointer<LeftSideWidget> wLeft; | |||||
| ScopedPointer<ResizeHandle> resizer; | |||||
| Widget* curWidget; | 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 | #!/bin/bash | ||||
| # function not available on some systems | # function not available on some systems | ||||
| if ! which realpath 2>/dev/null; then | |||||
| if ! which realpath &>/dev/null; then | |||||
| function realpath() { | function realpath() { | ||||
| [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" | [[ $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> | <key>CFBundleIconFile</key> | ||||
| <string></string> | <string></string> | ||||
| <key>CFBundleIdentifier</key> | <key>CFBundleIdentifier</key> | ||||
| <string>net.sf.distrho.X-PROJECTNAME-X</string> | |||||
| <string>studio.kx.distrho.X-PROJECTNAME-X</string> | |||||
| <key>CFBundleInfoDictionaryVersion</key> | <key>CFBundleInfoDictionaryVersion</key> | ||||
| <string>6.0</string> | <string>6.0</string> | ||||
| <key>CFBundlePackageType</key> | <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 | 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 | or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -29,7 +29,7 @@ using DGL::Color; | |||||
| DistrhoUIMVerb::DistrhoUIMVerb() | DistrhoUIMVerb::DistrhoUIMVerb() | ||||
| : UI(Art::backgroundWidth, Art::backgroundHeight), | : UI(Art::backgroundWidth, Art::backgroundHeight), | ||||
| fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, GL_BGR) | |||||
| fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, kImageFormatBGR) | |||||
| { | { | ||||
| // text | // text | ||||
| fNanoText.loadSharedResources(); | fNanoText.loadSharedResources(); | ||||
| @@ -122,6 +122,9 @@ DistrhoUIMVerb::DistrhoUIMVerb() | |||||
| // set initial values | // set initial values | ||||
| programLoaded(0); | programLoaded(0); | ||||
| // TODO auto-scale but non-resizable | |||||
| // setGeometryConstraints(Art::backgroundWidth, Art::backgroundHeight, true, true); | |||||
| } | } | ||||
| DistrhoUIMVerb::~DistrhoUIMVerb() | DistrhoUIMVerb::~DistrhoUIMVerb() | ||||
| @@ -225,7 +228,9 @@ void DistrhoUIMVerb::imageKnobValueChanged(ImageKnob* knob, float value) | |||||
| void DistrhoUIMVerb::onDisplay() | void DistrhoUIMVerb::onDisplay() | ||||
| { | { | ||||
| fImgBackground.draw(); | |||||
| const GraphicsContext& context(getGraphicsContext()); | |||||
| fImgBackground.draw(context); | |||||
| // text display | // text display | ||||
| fNanoText.beginFrame(this); | fNanoText.beginFrame(this); | ||||