Browse Source

Merge pull request #272 from DISTRHO/pugl-upstream-v2

Pugl upstream v2 to develop
pull/274/head
Filipe Coelho GitHub 4 years ago
parent
commit
07771af564
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 12505 additions and 4904 deletions
  1. +3
    -0
      .gitmodules
  2. +20
    -10
      .travis.yml
  3. +11
    -0
      .travis/before_install.sh
  4. +24
    -14
      .travis/install.sh
  5. +1
    -1
      LICENSE
  6. +4
    -3
      Makefile
  7. +66
    -8
      Makefile.base.mk
  8. +53
    -6
      Makefile.plugins.mk
  9. +33
    -14
      dgl/Application.hpp
  10. +83
    -25
      dgl/Base.hpp
  11. +160
    -7
      dgl/Cairo.hpp
  12. +13
    -8
      dgl/Color.hpp
  13. +115
    -36
      dgl/Geometry.hpp
  14. +10
    -99
      dgl/Image.hpp
  15. +47
    -16
      dgl/ImageBase.hpp
  16. +223
    -0
      dgl/ImageBaseWidgets.hpp
  17. +13
    -255
      dgl/ImageWidgets.hpp
  18. +87
    -25
      dgl/Makefile
  19. +39
    -17
      dgl/NanoVG.hpp
  20. +175
    -3
      dgl/OpenGL.hpp
  21. +34
    -20
      dgl/StandaloneWindow.hpp
  22. +148
    -0
      dgl/SubWidget.hpp
  23. +101
    -0
      dgl/TopLevelWidget.hpp
  24. +103
    -0
      dgl/Vulkan.hpp
  25. +145
    -119
      dgl/Widget.hpp
  26. +322
    -64
      dgl/Window.hpp
  27. +33
    -30
      dgl/src/Application.cpp
  28. +137
    -0
      dgl/src/ApplicationPrivateData.cpp
  29. +56
    -38
      dgl/src/ApplicationPrivateData.hpp
  30. +728
    -36
      dgl/src/Cairo.cpp
  31. +34
    -37
      dgl/src/Color.cpp
  32. +72
    -8
      dgl/src/Common.hpp
  33. +210
    -182
      dgl/src/Geometry.cpp
  34. +0
    -150
      dgl/src/Image.cpp
  35. +56
    -31
      dgl/src/ImageBase.cpp
  36. +1061
    -0
      dgl/src/ImageBaseWidgets.cpp
  37. +0
    -1108
      dgl/src/ImageWidgets.cpp
  38. +39
    -68
      dgl/src/NanoVG.cpp
  39. +558
    -58
      dgl/src/OpenGL.cpp
  40. +144
    -0
      dgl/src/SubWidget.cpp
  41. +42
    -0
      dgl/src/SubWidgetPrivateData.cpp
  42. +47
    -0
      dgl/src/SubWidgetPrivateData.hpp
  43. +78
    -0
      dgl/src/TopLevelWidget.cpp
  44. +163
    -0
      dgl/src/TopLevelWidgetPrivateData.cpp
  45. +51
    -0
      dgl/src/TopLevelWidgetPrivateData.hpp
  46. +243
    -0
      dgl/src/Vulkan.cpp
  47. +33
    -86
      dgl/src/Widget.cpp
  48. +176
    -63
      dgl/src/WidgetPrivateData.cpp
  49. +22
    -47
      dgl/src/WidgetPrivateData.hpp
  50. +150
    -1664
      dgl/src/Window.cpp
  51. +223
    -0
      dgl/src/WindowFileBrowser.cpp
  52. +1090
    -0
      dgl/src/WindowPrivateData.cpp
  53. +296
    -0
      dgl/src/WindowPrivateData.hpp
  54. +0
    -0
      dgl/src/pugl-custom/pugl.h
  55. +0
    -0
      dgl/src/pugl-custom/pugl_haiku.cpp
  56. +0
    -0
      dgl/src/pugl-custom/pugl_internal.h
  57. +0
    -0
      dgl/src/pugl-custom/pugl_osx.m
  58. +0
    -0
      dgl/src/pugl-custom/pugl_win.cpp
  59. +0
    -0
      dgl/src/pugl-custom/pugl_x11.c
  60. +29
    -0
      dgl/src/pugl-extra/extras.c
  61. +50
    -0
      dgl/src/pugl-extra/extras.h
  62. +81
    -0
      dgl/src/pugl-extra/haiku.cpp
  63. +35
    -0
      dgl/src/pugl-extra/haiku.h
  64. +48
    -0
      dgl/src/pugl-extra/mac.m
  65. +118
    -0
      dgl/src/pugl-extra/win.c
  66. +111
    -0
      dgl/src/pugl-extra/x11.c
  67. +1
    -0
      dgl/src/pugl-upstream
  68. +458
    -0
      dgl/src/pugl.cpp
  69. +121
    -0
      dgl/src/pugl.hpp
  70. +1
    -0
      dgl/src/pugl.mm
  71. +20
    -15
      dgl/src/sofd/libsofd.c
  72. +21
    -2
      dgl/src/sofd/libsofd.h
  73. +22
    -0
      distrho/DistrhoInfo.hpp
  74. +1
    -1
      distrho/DistrhoPlugin.hpp
  75. +36
    -30
      distrho/DistrhoUI.hpp
  76. +33
    -0
      distrho/DistrhoUI_macOS.mm
  77. +14
    -1
      distrho/DistrhoUtils.hpp
  78. +1
    -1
      distrho/extra/ExternalWindow.hpp
  79. +2
    -2
      distrho/extra/LeakDetector.hpp
  80. +17
    -14
      distrho/extra/Mutex.hpp
  81. +136
    -0
      distrho/extra/ScopedSafeLocale.hpp
  82. +158
    -65
      distrho/extra/String.hpp
  83. +65
    -22
      distrho/extra/Thread.hpp
  84. +57
    -29
      distrho/src/DistrhoDefines.h
  85. +14
    -5
      distrho/src/DistrhoPluginChecks.h
  86. +1
    -1
      distrho/src/DistrhoPluginInternal.hpp
  87. +2
    -11
      distrho/src/DistrhoPluginJack.cpp
  88. +122
    -94
      distrho/src/DistrhoPluginVST.cpp
  89. +69
    -57
      distrho/src/DistrhoUI.cpp
  90. +1
    -0
      distrho/src/DistrhoUIDSSI.cpp
  91. +51
    -163
      distrho/src/DistrhoUIInternal.hpp
  92. +2
    -1
      distrho/src/DistrhoUILV2.cpp
  93. +152
    -0
      distrho/src/DistrhoUIPrivateData.hpp
  94. +2600
    -0
      examples/CairoUI/Artwork.cpp
  95. +19
    -0
      examples/CairoUI/Artwork.hpp
  96. +8
    -1
      examples/CairoUI/CairoExamplePlugin.cpp
  97. +25
    -8
      examples/CairoUI/CairoExampleUI.cpp
  98. +9
    -10
      examples/CairoUI/DemoWidgetBanner.cpp
  99. +10
    -5
      examples/CairoUI/DemoWidgetBanner.hpp
  100. +9
    -10
      examples/CairoUI/DemoWidgetClickable.cpp

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "dgl/src/pugl-upstream"]
path = dgl/src/pugl-upstream
url = https://github.com/DISTRHO/pugl.git

+ 20
- 10
.travis.yml View File

@@ -1,15 +1,25 @@
os: linux
dist: bionic

languages: c++
compiler: gcc
cache:
directories:
- ${HOME}/debs

jobs:
# linux native build
- name: "Linux native"
os: linux
compiler: gcc
dist: bionic
env:
- TARGET="linux"
services:
- xvfb

before_install:
- sh ${TRAVIS_BUILD_DIR}/.travis/before_install.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/before_install.sh

install:
- sh ${TRAVIS_BUILD_DIR}/.travis/install.sh
- bash ${TRAVIS_BUILD_DIR}/.travis/install.sh

script:
- sh ${TRAVIS_BUILD_DIR}/.travis/script-linux.sh
#- sh ${TRAVIS_BUILD_DIR}/.travis/script-macos.sh
- sh ${TRAVIS_BUILD_DIR}/.travis/script-win32.sh
- sh ${TRAVIS_BUILD_DIR}/.travis/script-win64.sh
- make
- make -C tests

+ 11
- 0
.travis/before_install.sh View File

@@ -2,6 +2,17 @@

set -e

# Special macOS native handling
if [ "${TARGET}" = "macos" ] || [ "${TARGET}" = "macos-universal" ]; then
exit 0
fi

if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then
wget -qO- https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add -
sudo apt-add-repository -y 'deb https://dl.winehq.org/wine-builds/ubuntu/ focal main'
sudo dpkg --add-architecture i386
fi

sudo add-apt-repository -y ppa:kxstudio-debian/kxstudio
sudo add-apt-repository -y ppa:kxstudio-debian/mingw
sudo add-apt-repository -y ppa:kxstudio-debian/toolchain


+ 24
- 14
.travis/install.sh View File

@@ -2,18 +2,28 @@

set -e

sudo apt-get install -y \
g++ \
pkg-config \
libcairo2-dev \
libjack-jackd2-dev \
liblo-dev \
libgl1-mesa-dev \
libx11-dev \
mingw-w64 \
binutils-mingw-w64-i686 \
binutils-mingw-w64-x86-64 \
g++-mingw-w64-i686 \
g++-mingw-w64-x86-64 \
# Special macOS native handling
if [ "${TARGET}" = "macos" ] || [ "${TARGET}" = "macos-universal" ]; then
HOMEBREW_NO_AUTO_UPDATE=1 brew install cairo jack2 liblo
exit 0
fi

# apple-x86-setup
# Special handling for caching deb archives
if [ "$(ls ${HOME}/debs | wc -l)" -ne 0 ]; then
sudo cp ${HOME}/debs/*.deb /var/cache/apt/archives/
fi

# common
sudo apt-get install -y build-essential pkg-config

# specific
if [ "${TARGET}" = "win32" ]; then
sudo apt-get install -y binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 winehq-stable
elif [ "${TARGET}" = "win64" ]; then
sudo apt-get install -y binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 winehq-stable
else
sudo apt-get install -y libcairo2-dev libgl1-mesa-dev libglu1-mesa-dev libjack-jackd2-dev liblo-dev libx11-dev
fi

# Special handling for caching deb archives
sudo mv /var/cache/apt/archives/*.deb ${HOME}/debs/

+ 1
- 1
LICENSE View File

@@ -1,5 +1,5 @@
DISTRHO Plugin Framework (DPF)
Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with
or without fee is hereby granted, provided that the above copyright notice and this


+ 4
- 3
Makefile View File

@@ -26,11 +26,9 @@ examples: dgl
$(MAKE) all -C examples/MidiThrough
$(MAKE) all -C examples/Parameters
$(MAKE) all -C examples/States

ifeq ($(HAVE_CAIRO),true)
$(MAKE) all -C examples/CairoUI
endif

ifneq ($(MACOS_OR_WINDOWS),true)
# ExternalUI is WIP
$(MAKE) all -C examples/ExternalUI
@@ -54,6 +52,9 @@ else
gen:
endif

tests: dgl
$(MAKE) -C tests

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

clean:
@@ -73,4 +74,4 @@ endif

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

.PHONY: dgl examples
.PHONY: dgl examples tests

+ 66
- 8
Makefile.base.mk View File

@@ -38,6 +38,9 @@ endif
ifneq (,$(findstring mingw,$(TARGET_MACHINE)))
WINDOWS=true
endif
ifneq (,$(findstring windows,$(TARGET_MACHINE)))
WINDOWS=true
endif

endif
endif
@@ -148,6 +151,9 @@ endif
ifeq ($(MACOS),true)
# MacOS linker flags
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs
ifneq ($(SKIP_STRIPPING),true)
LINK_OPTS += -Wl,-x
endif
else
# Common linker flags
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed
@@ -162,10 +168,7 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections
endif

ifeq ($(WINDOWS),true)
# mingw has issues with this specific optimization
# See https://github.com/falkTX/Carla/issues/696
BASE_OPTS += -fno-rerun-cse-after-loop
# See https://github.com/falkTX/Carla/issues/855
# Needed for windows, see https://github.com/falkTX/Carla/issues/855
BASE_OPTS += -mstackrealign
else
# Not needed for Windows
@@ -203,7 +206,7 @@ endif

ifeq ($(TESTBUILD),true)
BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings
BASE_FLAGS += -Wpointer-arith -Wabi -Winit-self -Wuninitialized -Wstrict-overflow=5
BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5
# BASE_FLAGS += -Wfloat-equal
ifeq ($(CC),clang)
BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command
@@ -225,13 +228,17 @@ endif
# Check for required libraries

HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true)
HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true)

ifeq ($(MACOS_OR_WINDOWS),true)
HAVE_OPENGL = true
else
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true)
ifneq ($(HAIKU),true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true)
HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true)
HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true)
HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true)
endif
endif

@@ -249,7 +256,7 @@ DGL_SYSTEM_LIBS += -lbe
endif

ifeq ($(MACOS),true)
DGL_SYSTEM_LIBS += -framework Cocoa
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo
endif

ifeq ($(WINDOWS),true)
@@ -258,8 +265,21 @@ endif

ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11)
ifeq ($(HAVE_XCURSOR),true)
# TODO -DHAVE_XCURSOR
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor)
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xcursor)
endif
ifeq ($(HAVE_XEXT),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xext) -DHAVE_XEXT -DHAVE_XSYNC
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xext)
endif
ifeq ($(HAVE_XRANDR),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr)
endif
endif
endif

@@ -307,6 +327,31 @@ HAVE_CAIRO_OR_OPENGL = true

endif

# ---------------------------------------------------------------------------------------------------------------------
# Set Stub specific stuff

ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
HAVE_STUB = true
else
HAVE_STUB = $(HAVE_X11)
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set Vulkan specific stuff

ifeq ($(HAVE_VULKAN),true)

DGL_FLAGS += -DHAVE_VULKAN

VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan)
VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan)

ifneq ($(WINDOWS),true)
VULKAN_LIBS += -ldl
endif

endif

# ---------------------------------------------------------------------------------------------------------------------
# Set optional libraries specific stuff

@@ -320,6 +365,19 @@ LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo)
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo)
endif

# ---------------------------------------------------------------------------------------------------------------------
# Backwards-compatible HAVE_DGL

ifeq ($(MACOS_OR_WINDOWS),true)
HAVE_DGL = true
else ifeq ($(HAVE_OPENGL),true)
ifeq ($(HAIKU),true)
HAVE_DGL = true
else
HAVE_DGL = $(HAVE_X11)
endif
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set app extension



+ 53
- 6
Makefile.plugins.mk View File

@@ -55,6 +55,10 @@ endif
OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o)
OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o)

ifeq ($(MACOS),true)
OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set plugin binary file targets

@@ -67,10 +71,22 @@ lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT)
lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT)
vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT)

# ---------------------------------------------------------------------------------------------------------------------
# Set plugin symbols to export

ifeq ($(MACOS),true)
SYMBOLS_LADSPA = -Wl,-exported_symbol,_ladspa_descriptor
SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_dssi_descriptor
SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl
SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor
SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain
endif

# ---------------------------------------------------------------------------------------------------------------------
# Handle UI stuff, disable UI support automatically

ifeq ($(FILES_UI),)
HAVE_DGL = false
UI_TYPE = none
endif

@@ -102,11 +118,32 @@ HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),vulkan)
ifeq ($(HAVE_VULKAN),true)
DGL_FLAGS += -DDGL_VULKAN
DGL_FLAGS += $(VULKAN_FLAGS)
DGL_LIBS += $(VULKAN_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),external)
DGL_FLAGS += -DDGL_EXTERNAL
HAVE_DGL = true
endif

ifeq ($(UI_TYPE),stub)
ifeq ($(HAVE_STUB),true)
DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm

ifneq ($(HAVE_DGL),true)
@@ -131,6 +168,11 @@ all:
# ---------------------------------------------------------------------------------------------------------------------
# Common

$(BUILD_DIR)/%.S.o: %.S
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@

$(BUILD_DIR)/%.c.o: %.c
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
@echo "Compiling $<"
@@ -162,6 +204,11 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp
@echo "Compiling DistrhoUIMain.cpp ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@

$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUI_macOS.mm ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$* -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@

$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoPluginMain.cpp (JACK)"
@@ -194,7 +241,7 @@ ladspa: $(ladspa_dsp)
$(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating LADSPA plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# DSSI
@@ -206,7 +253,7 @@ dssi_ui: $(dssi_ui)
$(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating DSSI plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@

$(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@@ -223,17 +270,17 @@ lv2_sep: $(lv2_dsp) $(lv2_ui)
$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) $(SYMBOLS_LV2UI) -o $@

$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2) -o $@

$(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin UI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# VST
@@ -247,7 +294,7 @@ $(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating VST plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@

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



+ 33
- 14
dgl/Application.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,22 +21,15 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Forward class names

class Window;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Base DGL Application class.

One application instance is required for creating a window.
There's no single/global application instance in DGL, and multiple
windows can share the same app instance.
There's no single/global application instance in DGL, and multiple windows can share the same app instance.

In standalone mode an application will automatically quit its
event-loop when all its windows are closed.
In standalone mode an application will automatically quit its event-loop when all its windows are closed.
*/
class Application
{
@@ -44,7 +37,8 @@ public:
/**
Constructor.
*/
Application();
// NOTE: the default value is not yet passed, so we catch where we use this
Application(bool isStandalone = true);

/**
Destructor.
@@ -62,11 +56,12 @@ public:
idle() is called at regular intervals.
@note This function is meant for standalones only, *never* call this from plugins.
*/
void exec(int idleTime = 10);
void exec(uint idleTimeInMs = 10);

/**
Quit the application.
This stops the event-loop and closes all Windows.
@note This function is meant for standalones only, *never* call this from plugins.
*/
void quit();

@@ -76,6 +71,30 @@ public:
*/
bool isQuiting() const noexcept;

/**
Add a callback function to be triggered on every idle cycle.
You can add more than one, and remove them at anytime with removeIdleCallback().
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing.
*/
void addIdleCallback(IdleCallback* callback);

/**
Remove an idle callback previously added via addIdleCallback().
*/
void removeIdleCallback(IdleCallback* callback);

/**
Set the class name of the application.

This is a stable identifier for the application, used as the window class/instance name on X11 and Windows.
It is not displayed to the user, but can be used in scripts and by window managers,
so it should be the same for every instance of the application, but different from other applications.

Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name.
*/
void setClassName(const char* name);

private:
struct PrivateData;
PrivateData* const pData;
@@ -84,7 +103,7 @@ private:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 83
- 25
dgl/Base.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,7 +20,7 @@
#include "../distrho/extra/LeakDetector.hpp"
#include "../distrho/extra/ScopedPointer.hpp"

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Define namespace

#ifndef DGL_NAMESPACE
@@ -33,33 +33,46 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Base DGL enums

/**
Convenience symbols for ASCII control characters.
*/
enum Char {
kCharBackspace = 0x08,
kCharEscape = 0x1B,
kCharDelete = 0x7F
};

/**
Keyboard modifier flags.
*/
enum Modifier {
kModifierShift = 1 << 0, /**< Shift key */
kModifierControl = 1 << 1, /**< Control key */
kModifierAlt = 1 << 2, /**< Alt/Option key */
kModifierSuper = 1 << 3 /**< Mod4/Command/Windows key */
kModifierShift = 1u << 0u, ///< Shift key
kModifierControl = 1u << 1u, ///< Control key
kModifierAlt = 1u << 2u, ///< Alt/Option key
kModifierSuper = 1u << 3u ///< Mod4/Command/Windows key
};

/**
Special (non-Unicode) keyboard keys.
Keyboard key codepoints.

All keys are identified by a Unicode code point in PuglEventKey::key. This
enumeration defines constants for special keys that do not have a standard
code point, and some convenience constants for control characters. Note
that all keys are handled in the same way, this enumeration is just for
convenience when writing hard-coded key bindings.

Keys that do not have a standard code point use values in the Private Use
Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications
must take care to not interpret these values beyond key detection, the
mapping used here is arbitrary and specific to DPF.
*/
enum Key {
kKeyF1 = 1,
// Convenience symbols for ASCII control characters
kKeyBackspace = 0x08,
kKeyEscape = 0x1B,
kKeyDelete = 0x7F,

// Backwards compatibility with old DPF
kCharBackspace DISTRHO_DEPRECATED_BY("kKeyBackspace") = kKeyBackspace,
kCharEscape DISTRHO_DEPRECATED_BY("kKeyEscape") = kKeyEscape,
kCharDelete DISTRHO_DEPRECATED_BY("kKeyDelete") = kKeyDelete,

// Unicode Private Use Area
kKeyF1 = 0xE000,
kKeyF2,
kKeyF3,
kKeyF4,
@@ -81,30 +94,75 @@ enum Key {
kKeyEnd,
kKeyInsert,
kKeyShift,
kKeyShiftL = kKeyShift,
kKeyShiftR,
kKeyControl,
kKeyControlL = kKeyControl,
kKeyControlR,
kKeyAlt,
kKeySuper
kKeyAltL = kKeyAlt,
kKeyAltR,
kKeySuper,
kKeySuperL = kKeySuper,
kKeySuperR,
kKeyMenu,
kKeyCapsLock,
kKeyScrollLock,
kKeyNumLock,
kKeyPrintScreen,
kKeyPause
};

/**
Common flags for all events.
*/
enum Flag {
kFlagSendEvent = 1, ///< Event is synthetic
kFlagIsHint = 2 ///< Event is a hint (not direct user input)
};

/**
Reason for a crossing event.
*/
enum CrossingMode {
kCrossingNormal, ///< Crossing due to pointer motion
kCrossingGrab, ///< Crossing due to a grab
kCrossingUngrab ///< Crossing due to a grab release
};

/**
Scroll direction.

Describes the direction of a scroll event along with whether the scroll is a "smooth" scroll.
The discrete directions are for devices like mouse wheels with constrained axes,
while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads.
*/
enum ScrollDirection {
kScrollUp, ///< Scroll up
kScrollDown, ///< Scroll down
kScrollLeft, ///< Scroll left
kScrollRight, ///< Scroll right
kScrollSmooth ///< Smooth scroll in any direction
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Base DGL classes

/**
Graphics context, definition depends on build type.
*/
struct GraphicsContext;
struct GraphicsContext {};

/**
Idle callback.
*/
class IdleCallback
struct IdleCallback
{
public:
virtual ~IdleCallback() {}
virtual void idleCallback() = 0;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

@@ -114,6 +172,6 @@ END_NAMESPACE_DGL
using namespace DGL_NAMESPACE;
#endif

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

#endif // DGL_BASE_HPP_INCLUDED

+ 160
- 7
dgl/Cairo.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,23 +17,176 @@
#ifndef DGL_CAIRO_HPP_INCLUDED
#define DGL_CAIRO_HPP_INCLUDED

#include "Base.hpp"
#include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"

#include <cairo/cairo.h>

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Graphics context.
Cairo Graphics context.
*/
struct GraphicsContext
struct CairoGraphicsContext : GraphicsContext
{
cairo_t* cairo; // FIXME proper name..
cairo_t* handle;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Cairo Image class.

TODO ...
*/
class CairoImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
CairoImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
CairoImage(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
CairoImage(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
*/
CairoImage(const CairoImage& image);

/**
Destructor.
*/
~CairoImage() override;

/**
Load raw image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Load PNG image from memory.
Image size is read from PNG contents.
@note @a pngData must remain valid for the lifetime of this Image.
*/
void loadFromPNG(const char* pngData, uint dataSize) noexcept;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
Get the cairo surface currently associated with this image.
FIXME might be removed
*/
inline cairo_surface_t* getSurface() const noexcept
{
return surface;
}

/**
TODO document this.
*/
CairoImage& operator=(const CairoImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };

private:
cairo_surface_t* surface;
uchar* surfacedata;
int* datarefcount;
};

// --------------------------------------------------------------------------------------------------------------------

/**
CairoWidget, handy class that takes graphics context during onDisplay and passes it in a new function.
*/
template <class BaseWidget>
class CairoBaseWidget : public BaseWidget
{
public:
/**
Constructor for a CairoSubWidget.
*/
explicit CairoBaseWidget(Widget* const parentGroupWidget);

/**
Constructor for a CairoTopLevelWidget.
*/
explicit CairoBaseWidget(Window& windowToMapTo);

/**
Constructor for a CairoStandaloneWindow without parent window.
*/
explicit CairoBaseWidget(Application& app);

/**
Constructor for a CairoStandaloneWindow with parent window.
*/
explicit CairoBaseWidget(Application& app, Window& parentWindow);

/**
Destructor.
*/
virtual ~CairoBaseWidget() {}

protected:
/**
New virtual onDisplay function.
@see onDisplay
*/
virtual void onCairoDisplay(const CairoGraphicsContext& context) = 0;

private:
/**
Widget display function.
Implemented internally to pass context into the drawing function.
*/
void onDisplay() override
{
const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext());
onCairoDisplay(context);
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget);
};

typedef CairoBaseWidget<SubWidget> CairoSubWidget;
typedef CairoBaseWidget<TopLevelWidget> CairoTopLevelWidget;
typedef CairoBaseWidget<StandaloneWindow> CairoStandaloneWindow;

// --------------------------------------------------------------------------------------------------------------------

typedef ImageBaseAboutWindow<CairoImage> CairoImageAboutWindow;
typedef ImageBaseButton<CairoImage> CairoImageButton;
typedef ImageBaseKnob<CairoImage> CairoImageKnob;
typedef ImageBaseSlider<CairoImage> CairoImageSlider;
typedef ImageBaseSwitch<CairoImage> CairoImageSwitch;

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 13
- 8
dgl/Color.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -23,7 +23,7 @@ struct NVGcolor;

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
A color made from red, green, blue and alpha floating-point values in [0..1] range.
@@ -44,13 +44,13 @@ struct Color {

/**
Create a color from red, green, blue and alpha numeric values.
Values must be in [0..255] range.
All values except alpha must be in [0..255] range, with alpha in [0..1] range.
*/
Color(int red, int green, int blue, int alpha = 255) noexcept;
Color(int red, int green, int blue, float alpha = 1.0f) noexcept;

/**
Create a color from red, green, blue and alpha floating-point values.
Values must in [0..1] range.
All values must in [0..1] range.
*/
Color(float red, float green, float blue, float alpha = 1.0f) noexcept;

@@ -74,7 +74,7 @@ struct Color {
/**
Create a color from a HTML string like "#333" or "#112233".
*/
static Color fromHTML(const char* rgb, float alpha = 1.0f);
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept;

/**
Linearly interpolate this color against another.
@@ -83,7 +83,7 @@ struct Color {

/**
Check if this color matches another.
@note Comparison is forced within 8-bit color values.
@note Comparison is done within 8-bit color space.
*/
bool isEqual(const Color& color, bool withAlpha = true) noexcept;
bool isNotEqual(const Color& color, bool withAlpha = true) noexcept;
@@ -95,6 +95,11 @@ struct Color {
*/
void fixBounds() noexcept;

/**
Set this color for use in the next drawing operation for the provided context.
*/
void setFor(const GraphicsContext& context, bool includeAlpha = false);

/**
@internal
Needed for NanoVG compatibility.
@@ -103,7 +108,7 @@ struct Color {
operator NVGcolor() const noexcept;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 115
- 36
dgl/Geometry.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,7 +21,7 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names

template<typename> class Line;
@@ -29,7 +29,7 @@ template<typename> class Circle;
template<typename> class Triangle;
template<typename> class Rectangle;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
DGL Point class.
@@ -114,14 +114,14 @@ public:
bool operator!=(const Point<T>& pos) const noexcept;

private:
T fX, fY;
T x, y;
template<typename> friend class Line;
template<typename> friend class Circle;
template<typename> friend class Triangle;
template<typename> friend class Rectangle;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
DGL Size class.
@@ -195,7 +195,7 @@ public:

/**
Return true if size is not null (0x0).
A non-null size is still invalid if its width or height is negative.
A non-null size is still invalid if its width or height are negative.
*/
bool isNotNull() const noexcept;

@@ -210,6 +210,8 @@ public:
*/
bool isInvalid() const noexcept;

Size<int> toInt() const noexcept;

Size<T> operator+(const Size<T>& size) noexcept;
Size<T> operator-(const Size<T>& size) noexcept;
Size<T>& operator=(const Size<T>& size) noexcept;
@@ -346,11 +348,6 @@ public:
*/
void moveBy(const Point<T>& pos) noexcept;

/**
Draw this line using the current OpenGL state.
*/
void draw();

/**
Return true if line is null (start and end pos are equal).
*/
@@ -361,12 +358,28 @@ public:
*/
bool isNotNull() const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the provided graphics context, optionally specifying line width.
*/
void draw(const GraphicsContext& context, T width = 1);
#endif

Line<T>& operator=(const Line<T>& line) noexcept;
bool operator==(const Line<T>& line) const noexcept;
bool operator!=(const Line<T>& line) const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();
#endif

private:
Point<T> fPosStart, fPosEnd;
Point<T> posStart, posEnd;
};

// -----------------------------------------------------------------------
@@ -461,19 +474,35 @@ public:
void setNumSegments(const uint num);

/**
Draw this circle using the current OpenGL state.
Draw this circle using the provided graphics context.
*/
void draw();
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this circle) using the current OpenGL state.
Draw lines (outline of this circle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline();
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Circle<T>& operator=(const Circle<T>& cir) noexcept;
bool operator==(const Circle<T>& cir) const noexcept;
bool operator!=(const Circle<T>& cir) const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this circle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw lines (outline of this circle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> fPos;
float fSize;
@@ -481,9 +510,6 @@ private:

// cached values
float fTheta, fCos, fSin;

/** @internal */
void _draw(const bool outline);
};

// -----------------------------------------------------------------------
@@ -541,24 +567,37 @@ public:
bool isInvalid() const noexcept;

/**
Draw this triangle using the current OpenGL state.
Draw this triangle using the provided graphics context.
*/
void draw();
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this triangle) using the current OpenGL state.
Draw lines (outline of this triangle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline();
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Triangle<T>& operator=(const Triangle<T>& tri) noexcept;
bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept;

private:
Point<T> fPos1, fPos2, fPos3;
#ifndef DPF_TEST_POINT_CPP
/**
Draw this triangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/** @internal */
void _draw(const bool outline);
/**
Draw lines (outline of this triangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> pos1, pos2, pos3;
};

// -----------------------------------------------------------------------
@@ -712,6 +751,12 @@ public:
*/
bool contains(const Point<T>& pos) const noexcept;

/**
Check if this rectangle contains the point @a pos of another type.
*/
template<typename T2>
bool contains(const Point<T2>& pos) const noexcept;

/**
Check if this rectangle contains X.
*/
@@ -723,14 +768,37 @@ public:
bool containsY(const T& y) const noexcept;

/**
Draw this rectangle using the current OpenGL state.
Return true if size is null (0x0).
An null size is also invalid.
*/
void draw();
bool isNull() const noexcept;

/**
Draw lines (outline of this rectangle) using the current OpenGL state.
Return true if size is not null (0x0).
A non-null size is still invalid if its width or height are negative.
*/
void drawOutline();
bool isNotNull() const noexcept;

/**
Return true if size is valid (width and height are higher than zero).
*/
bool isValid() const noexcept;

/**
Return true if size is invalid (width or height are lower or equal to zero).
An invalid size might not be null under some circumstances.
*/
bool isInvalid() const noexcept;

/**
Draw this rectangle using the provided graphics context.
*/
void draw(const GraphicsContext& context);

/**
Draw lines (outline of this rectangle) using the provided graphics context, optionally specifying line width.
*/
void drawOutline(const GraphicsContext& context, T lineWidth = 1);

Rectangle<T>& operator=(const Rectangle<T>& rect) noexcept;
Rectangle<T>& operator*=(double m) noexcept;
@@ -738,12 +806,23 @@ public:
bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept;

private:
Point<T> fPos;
Size<T> fSize;
/**
Draw this rectangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/** @internal */
void _draw(const bool outline);
/**
Draw lines (outline of this rectangle) using the current OpenGL state.@n
DEPRECATED Please use drawOutline(const GraphicsContext&,T) instead.
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();

private:
Point<T> pos;
Size<T> size;
};

// -----------------------------------------------------------------------


+ 10
- 99
dgl/Image.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,108 +17,19 @@
#ifndef DGL_IMAGE_HPP_INCLUDED
#define DGL_IMAGE_HPP_INCLUDED

#include "ImageBase.hpp"
#ifdef DGL_CAIRO
#include "Cairo.hpp"
#else
#include "OpenGL.hpp"
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

/**
OpenGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class Image : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
Image();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData,
const uint width,
const uint height,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
Image(const char* const rawData,
const Size<uint>& size,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE);

/**
Constructor using another image data.
*/
Image(const Image& image);

/**
Destructor.
*/
~Image() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData,
const uint width,
const uint height,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* const rawData,
const Size<uint>& size,
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
Get the image format.
*/
GLenum getFormat() const noexcept;

/**
Get the image type.
*/
GLenum getType() const noexcept;

/**
TODO document this.
*/
Image& operator=(const Image& image) noexcept;

protected:
/** @internal */
void _drawAt(const Point<int>& pos) override;

private:
GLenum fFormat;
GLenum fType;
GLuint fTextureId;
bool fIsReady;
};

// -----------------------------------------------------------------------
#ifdef DGL_CAIRO
typedef CairoImage Image;
#else
typedef OpenGLImage Image;
#endif

END_NAMESPACE_DGL



+ 47
- 16
dgl/ImageBase.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,7 +21,16 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

enum ImageFormat {
kImageFormatNull,
kImageFormatGrayscale,
kImageFormatBGR,
kImageFormatBGRA,
kImageFormatRGB,
kImageFormatRGBA,
};

/**
Base DGL Image class.
@@ -44,13 +53,13 @@ protected:
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
ImageBase(const char* const rawData, const uint width, const uint height);
ImageBase(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
ImageBase(const char* const rawData, const Size<uint>& size);
ImageBase(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
@@ -68,6 +77,11 @@ public:
*/
bool isValid() const noexcept;

/**
Check if this image is not valid.
*/
bool isInvalid() const noexcept;

/**
Get width.
*/
@@ -89,19 +103,38 @@ public:
const char* getRawData() const noexcept;

/**
Draw this image at (0, 0) point.
Get the image format.
*/
void draw();
ImageFormat getFormat() const noexcept;

/**
Draw this image at (x, y) point.
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void drawAt(const int x, const int y);
void loadFromMemory(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA) noexcept;

/**
Draw this image at position @a pos.
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void drawAt(const Point<int>& pos);
virtual void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept;

/**
Draw this image at (0, 0) point using the current OpenGL context.
*/
void draw(const GraphicsContext& context);

/**
Draw this image at (x, y) point using the current OpenGL context.
*/
void drawAt(const GraphicsContext& context, int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
*/
virtual void drawAt(const GraphicsContext& context, const Point<int>& pos) = 0;

/**
TODO document this.
@@ -111,14 +144,12 @@ public:
bool operator!=(const ImageBase& image) const noexcept;

protected:
/** @internal */
virtual void _drawAt(const Point<int>& pos) = 0;

const char* fRawData;
Size<uint> fSize;
const char* rawData;
Size<uint> size;
ImageFormat format;
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 223
- 0
dgl/ImageBaseWidgets.hpp View File

@@ -0,0 +1,223 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

#include "StandaloneWindow.hpp"
#include "SubWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseAboutWindow : public StandaloneWindow
{
public:
explicit ImageBaseAboutWindow(Window& parentWindow, const ImageType& image = ImageType());
explicit ImageBaseAboutWindow(TopLevelWidget* parentTopLevelWidget, const ImageType& image = ImageType());

void setImage(const ImageType& image);

protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;

private:
ImageType img;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseAboutWindow)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseButton : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageBaseButton* imageButton, int button) = 0;
};

explicit ImageBaseButton(Widget* parentWidget, const ImageType& image);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown);
explicit ImageBaseButton(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown);

~ImageBaseButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseButton)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseKnob : public SubWidget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageBaseKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageBaseKnob* imageKnob, float value) = 0;
};

explicit ImageBaseKnob(Widget* parentWidget, const ImageType& image, Orientation orientation = Vertical) noexcept;
explicit ImageBaseKnob(const ImageBaseKnob& imageKnob);
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob);
~ImageBaseKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseKnob)
};

// --------------------------------------------------------------------------------------------------------------------

// note set range and step before setting the value

template <class ImageType>
class ImageBaseSlider : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageBaseSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageBaseSlider* imageSlider, float value) = 0;
};

explicit ImageBaseSlider(Widget* parentWidget, const ImageType& image) noexcept;
~ImageBaseSlider() override;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageBaseSlider)
};

// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseSwitch : public SubWidget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageBaseSwitch* imageSwitch, bool down) = 0;
};

explicit ImageBaseSwitch(Widget* parentWidget, const ImageType& imageNormal, const ImageType& imageDown) noexcept;
explicit ImageBaseSwitch(const ImageBaseSwitch& imageSwitch) noexcept;
ImageBaseSwitch& operator=(const ImageBaseSwitch& imageSwitch) noexcept;
~ImageBaseSwitch() override;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(ImageBaseSwitch)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

+ 13
- 255
dgl/ImageWidgets.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,266 +18,24 @@
#define DGL_IMAGE_WIDGETS_HPP_INCLUDED

#include "Image.hpp"
#include "Widget.hpp"
#include "Window.hpp"
#include "ImageBaseWidgets.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

#ifndef DISTRHO_OS_HAIKU
class ImageAboutWindow : public Window,
public Widget
#else
// crash when creating or opening 2nd window
class ImageAboutWindow
#endif
{
public:
explicit ImageAboutWindow(Window& parent, const Image& image = Image());
explicit ImageAboutWindow(Widget* widget, const Image& image = Image());

void setImage(const Image& image);

#ifndef DISTRHO_OS_HAIKU
protected:
void onDisplay() override;
bool onKeyboard(const KeyboardEvent&) override;
bool onMouse(const MouseEvent&) override;
void onReshape(uint width, uint height) override;
#ifdef DGL_CAIRO
typedef CairoImageAboutWindow ImageAboutWindow;
typedef CairoImageButton ImageButton;
typedef CairoImageKnob ImageKnob;
typedef CairoImageSlider ImageSlider;
typedef CairoImageSwitch ImageSwitch;
#else
void exec() {}
typedef OpenGLImageAboutWindow ImageAboutWindow;
typedef OpenGLImageButton ImageButton;
typedef OpenGLImageKnob ImageKnob;
typedef OpenGLImageSlider ImageSlider;
typedef OpenGLImageSwitch ImageSwitch;
#endif

private:
Image fImgBackground;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageAboutWindow)
};

// -----------------------------------------------------------------------

class ImageButton : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageButtonClicked(ImageButton* imageButton, int button) = 0;
};

explicit ImageButton(Window& parent, const Image& image);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Window& parent, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

explicit ImageButton(Widget* widget, const Image& image);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageDown);
explicit ImageButton(Widget* widget, const Image& imageNormal, const Image& imageHover, const Image& imageDown);

~ImageButton() override;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageButton)
};

// -----------------------------------------------------------------------

class ImageKnob : public Widget
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
virtual ~Callback() {}
virtual void imageKnobDragStarted(ImageKnob* imageKnob) = 0;
virtual void imageKnobDragFinished(ImageKnob* imageKnob) = 0;
virtual void imageKnobValueChanged(ImageKnob* imageKnob, float value) = 0;
};

explicit ImageKnob(Window& parent, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(Widget* widget, const Image& image, Orientation orientation = Vertical) noexcept;
explicit ImageKnob(const ImageKnob& imageKnob);
ImageKnob& operator=(const ImageKnob& imageKnob);
~ImageKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;
bool fUsingLog;
Orientation fOrientation;

int fRotationAngle;
bool fDragging;
int fLastX;
int fLastY;

Callback* fCallback;

bool fIsImgVertical;
uint fImgLayerWidth;
uint fImgLayerHeight;
uint fImgLayerCount;
bool fIsReady;
GLuint fTextureId;

float _logscale(float value) const;
float _invlogscale(float value) const;

DISTRHO_LEAK_DETECTOR(ImageKnob)
};

// -----------------------------------------------------------------------

// note set range and step before setting the value

class ImageSlider : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSliderDragStarted(ImageSlider* imageSlider) = 0;
virtual void imageSliderDragFinished(ImageSlider* imageSlider) = 0;
virtual void imageSliderValueChanged(ImageSlider* imageSlider, float value) = 0;
};

explicit ImageSlider(Window& parent, const Image& image) noexcept;
explicit ImageSlider(Widget* widget, const Image& image) noexcept;

float getValue() const noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setDefault(float def) noexcept;

void setStartPos(const Point<int>& startPos) noexcept;
void setStartPos(int x, int y) noexcept;
void setEndPos(const Point<int>& endPos) noexcept;
void setEndPos(int x, int y) noexcept;

void setInverted(bool inverted) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;

private:
Image fImage;
float fMinimum;
float fMaximum;
float fStep;
float fValue;
float fValueDef;
float fValueTmp;
bool fUsingDefault;

bool fDragging;
bool fInverted;
bool fValueIsSet;
int fStartedX;
int fStartedY;

Callback* fCallback;

Point<int> fStartPos;
Point<int> fEndPos;
Rectangle<int> fSliderArea;

void _recheckArea() noexcept;

// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const Point<int>&) const noexcept {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ImageSlider)
};

// -----------------------------------------------------------------------

class ImageSwitch : public Widget
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void imageSwitchClicked(ImageSwitch* imageSwitch, bool down) = 0;
};

explicit ImageSwitch(Window& parent, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(Widget* widget, const Image& imageNormal, const Image& imageDown) noexcept;
explicit ImageSwitch(const ImageSwitch& imageSwitch) noexcept;
ImageSwitch& operator=(const ImageSwitch& imageSwitch) noexcept;

bool isDown() const noexcept;
void setDown(bool down) noexcept;

void setCallback(Callback* callback) noexcept;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;

private:
Image fImageNormal;
Image fImageDown;
bool fIsDown;

Callback* fCallback;

DISTRHO_LEAK_DETECTOR(ImageSwitch)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_IMAGE_WIDGETS_HPP_INCLUDED

+ 87
- 25
dgl/Makefile View File

@@ -10,8 +10,16 @@ include ../Makefile.base.mk

BUILD_C_FLAGS += $(DGL_FLAGS) -I. -Isrc
BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-unused-parameter
BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include
LINK_FLAGS += $(DGL_LIBS)

# TODO fix these after pugl-upstream is done
BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -Wno-narrowing

ifeq ($(MACOS),true)
BUILD_CXX_FLAGS += -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations
endif

# ifneq ($(MACOS_OLD),true)
# needed by sofd right now, fix later
# BUILD_CXX_FLAGS += -Wno-type-limits -fpermissive
@@ -21,39 +29,64 @@ LINK_FLAGS += $(DGL_LIBS)

OBJS_common = \
../build/dgl/Application.cpp.o \
../build/dgl/ApplicationPrivateData.cpp.o \
../build/dgl/Color.cpp.o \
../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \
../build/dgl/Resources.cpp.o \
../build/dgl/Widget.cpp.o

# TODO: ImageWidgets.cpp
../build/dgl/SubWidget.cpp.o \
../build/dgl/SubWidgetPrivateData.cpp.o \
../build/dgl/TopLevelWidget.cpp.o \
../build/dgl/TopLevelWidgetPrivateData.cpp.o \
../build/dgl/Widget.cpp.o \
../build/dgl/WidgetPrivateData.cpp.o \
../build/dgl/Window.cpp.o \
../build/dgl/WindowPrivateData.cpp.o
# ../build/dgl/WindowFileBrowser.cpp.o

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

OBJS_cairo = $(OBJS_common) \
../build/dgl/Cairo.cpp.cairo.o \
../build/dgl/WidgetPrivateData.cpp.cairo.o
../build/dgl/Cairo.cpp.cairo.o

ifeq ($(MACOS),true)
OBJS_cairo += ../build/dgl/Window.mm.cairo.o
OBJS_cairo += ../build/dgl/pugl.mm.cairo.o
else
OBJS_cairo += ../build/dgl/Window.cpp.cairo.o
OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o
endif

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

OBJS_opengl = $(OBJS_common) \
../build/dgl/OpenGL.cpp.opengl.o \
../build/dgl/Image.cpp.opengl.o \
../build/dgl/ImageWidgets.cpp.opengl.o \
../build/dgl/NanoVG.cpp.opengl.o \
../build/dgl/WidgetPrivateData.cpp.opengl.o
../build/dgl/NanoVG.cpp.opengl.o

ifeq ($(MACOS),true)
OBJS_opengl += ../build/dgl/pugl.mm.opengl.o
else
OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o
endif

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

OBJS_stub = $(OBJS_common)

ifeq ($(MACOS),true)
OBJS_opengl += ../build/dgl/Window.mm.opengl.o
OBJS_stub += ../build/dgl/pugl.mm.o
else
OBJS_opengl += ../build/dgl/Window.cpp.opengl.o
OBJS_stub += ../build/dgl/pugl.cpp.o
endif

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

OBJS_vulkan = $(OBJS_common) \
../build/dgl/Vulkan.cpp.vulkan.o

ifeq ($(MACOS),true)
OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o
else
OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o
endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -68,6 +101,14 @@ TARGETS += ../build/libdgl-opengl.a
TARGETS += ../build/libdgl.a
endif

ifeq ($(HAVE_STUB),true)
TARGETS += ../build/libdgl-stub.a
endif

ifeq ($(HAVE_VULKAN),true)
TARGETS += ../build/libdgl-vulkan.a
endif

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

all: $(TARGETS)
@@ -86,6 +127,18 @@ all: $(TARGETS)
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-stub.a: $(OBJS_stub)
-@mkdir -p ../build
@echo "Creating libdgl-stub.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-vulkan.a: $(OBJS_vulkan)
-@mkdir -p ../build
@echo "Creating libdgl-vulkan.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

# Compat name, to be removed soon
../build/libdgl.a: ../build/libdgl-opengl.a
@echo "Symlinking libdgl.a"
@@ -103,6 +156,11 @@ all: $(TARGETS)
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

../build/dgl/%.mm.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@

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

../build/dgl/%.cpp.cairo.o: src/%.cpp
@@ -110,15 +168,10 @@ all: $(TARGETS)
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@

../build/dgl/Window.cpp.cairo.o: src/Window.cpp src/sofd/* src/pugl/*
-@mkdir -p ../build/dgl
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@

../build/dgl/Window.mm.cairo.o: src/Window.cpp src/sofd/* src/pugl/*
../build/dgl/%.mm.cairo.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -ObjC++ -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@

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

@@ -127,15 +180,22 @@ all: $(TARGETS)
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@

../build/dgl/Window.cpp.opengl.o: src/Window.cpp src/sofd/* src/pugl/*
../build/dgl/%.mm.opengl.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@

../build/dgl/Window.mm.opengl.o: src/Window.cpp src/sofd/* src/pugl/*
# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.vulkan.o: src/%.cpp
-@mkdir -p ../build/dgl
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -ObjC++ -c -o $@
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@

../build/dgl/%.mm.vulkan.o: src/%.mm
-@mkdir -p ../build/dgl
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@

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

@@ -150,5 +210,7 @@ debug:
-include $(OBJS_common:%.o=%.d)
-include $(OBJS_cairo:%.o=%.d)
-include $(OBJS_opengl:%.o=%.d)
-include $(OBJS_stub:%.o=%.d)
-include $(OBJS_vulkan:%.o=%.d)

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

+ 39
- 17
dgl/NanoVG.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,7 +19,9 @@

#include "Color.hpp"
#include "OpenGL.hpp"
#include "Widget.hpp"
#include "SubWidget.hpp"
#include "TopLevelWidget.hpp"
#include "StandaloneWindow.hpp"

#ifndef DGL_NO_SHARED_RESOURCES
# define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__"
@@ -305,7 +307,9 @@ public:
/**
Constructor reusing a NanoVG context, used for subwidgets.
*/
/*
NanoVG(NanoWidget* groupWidget);
*/

/**
Destructor.
@@ -851,7 +855,7 @@ public:
/**
Load DPF's internal shared resources for this NanoVG class.
*/
virtual void loadSharedResources();
virtual bool loadSharedResources();
#endif

private:
@@ -873,30 +877,39 @@ private:
The drawing function onDisplay() is implemented internally but a
new onNanoDisplay() needs to be overridden instead.
*/
class NanoWidget : public Widget,
public NanoVG
template <class BaseWidget>
class NanoBaseWidget : public BaseWidget,
public NanoVG
{
public:
/**
Constructor.
Constructor for a NanoSubWidget.
@see CreateFlags
*/
explicit NanoWidget(Window& parent, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS);

/**
Constructor for a subwidget.
Constructor for a NanoTopLevelWidget.
@see CreateFlags
*/
explicit NanoWidget(Widget* groupWidget, int flags = CREATE_ANTIALIAS);
explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS);

/**
Constructor for a subwidget, reusing a NanoVG context.
Constructor for a NanoStandaloneWindow without parent window.
@see CreateFlags
*/
explicit NanoWidget(NanoWidget* groupWidget);
explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS);

/**
Constructor for a NanoStandaloneWindow with parent window.
@see CreateFlags
*/
explicit NanoBaseWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS);

/**
Destructor.
*/
virtual ~NanoWidget();
virtual ~NanoBaseWidget() {}

protected:
/**
@@ -906,14 +919,16 @@ protected:
virtual void onNanoDisplay() = 0;

private:
struct PrivateData;
PrivateData* const nData;

/**
Widget display function.
Implemented internally to wrap begin/endFrame() automatically.
*/
void onDisplay() override;
inline void onDisplay() override
{
NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight());
onNanoDisplay();
NanoVG::endFrame();
}

// these should not be used
void beginFrame(uint,uint) {}
@@ -922,9 +937,16 @@ private:
void cancelFrame() {}
void endFrame() {}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoWidget)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NanoBaseWidget)
};

typedef NanoBaseWidget<SubWidget> NanoSubWidget;
typedef NanoBaseWidget<TopLevelWidget> NanoTopLevelWidget;
typedef NanoBaseWidget<StandaloneWindow> NanoStandaloneWindow;

DISTRHO_DEPRECATED_BY("NanoSubWidget")
typedef NanoSubWidget NanoWidget;

// -----------------------------------------------------------------------

END_NAMESPACE_DGL


+ 175
- 3
dgl/OpenGL.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,7 @@
#define DGL_OPENGL_HPP_INCLUDED

#include "ImageBase.hpp"
#include "ImageBaseWidgets.hpp"

// -----------------------------------------------------------------------
// Fix OpenGL includes for Windows, based on glfw code (part 1)
@@ -108,14 +109,185 @@ START_NAMESPACE_DGL
// -----------------------------------------------------------------------

/**
Graphics context.
OpenGL Graphics context.
*/
struct GraphicsContext
struct OpenGLGraphicsContext : GraphicsContext
{
};

// -----------------------------------------------------------------------

static inline
ImageFormat asDISTRHOImageFormat(const GLenum format)
{
switch (format)
{
case GL_LUMINANCE:
return kImageFormatGrayscale;
case GL_BGR:
return kImageFormatBGR;
case GL_BGRA:
return kImageFormatBGRA;
case GL_RGB:
return kImageFormatRGB;
case GL_RGBA:
return kImageFormatRGBA;
}

return kImageFormatNull;
}

static inline
GLenum asOpenGLImageFormat(const ImageFormat format)
{
switch (format)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
return GL_LUMINANCE;
case kImageFormatBGR:
return GL_BGR;
case kImageFormatBGRA:
return GL_BGRA;
case kImageFormatRGB:
return GL_RGB;
case kImageFormatRGBA:
return GL_RGBA;
}

return 0x0;
}

// -----------------------------------------------------------------------

/**
OpenGL Image class.

This is an Image class that handles raw image data in pixels.
You can init the image data on the contructor or later on by calling loadFromMemory().

To generate raw data useful for this class see the utils/png2rgba.py script.
Be careful when using a PNG without alpha channel, for those the format is 'GL_BGR'
instead of the default 'GL_BGRA'.

Images are drawn on screen via 2D textures.
*/
class OpenGLImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
OpenGLImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, uint width, uint height, ImageFormat format = kImageFormatBGRA);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
OpenGLImage(const char* rawData, const Size<uint>& size, ImageFormat format = kImageFormatBGRA);

/**
Constructor using another image data.
*/
OpenGLImage(const OpenGLImage& image);

/**
Destructor.
*/
~OpenGLImage() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
TODO document this.
*/
OpenGLImage& operator=(const OpenGLImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, uint, uint, ImageFormat)")
explicit OpenGLImage(const char* rawData, uint width, uint height, GLenum glFormat);

/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
DEPRECATED This constructor uses OpenGL image format instead of DISTRHO one.
*/
DISTRHO_DEPRECATED_BY("OpenGLImage(const char*, const Size<uint>&, ImageFormat)")
explicit OpenGLImage(const char* rawData, const Size<uint>& size, GLenum glFormat);

/**
Draw this image at (0, 0) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();

/**
Draw this image at (x, y) point using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, int, int)")
void drawAt(int x, int y);

/**
Draw this image at position @a pos using the current OpenGL context.
DEPRECATED This function does not take into consideration the current graphics context and only works in OpenGL.
*/
DISTRHO_DEPRECATED_BY("drawAt(const GraphicsContext&, const Point<int>&)")
void drawAt(const Point<int>& pos);

/**
Get the image type.
DEPRECATED Type is always assumed to be GL_UNSIGNED_BYTE.
*/
DISTRHO_DEPRECATED
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; }

private:
GLuint textureId;
bool setupCalled;
};

// -----------------------------------------------------------------------

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;
typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif

+ 34
- 20
dgl/StandaloneWindow.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,39 +17,53 @@
#ifndef DGL_STANDALONE_WINDOW_HPP_INCLUDED
#define DGL_STANDALONE_WINDOW_HPP_INCLUDED

#include "Application.hpp"
#include "Widget.hpp"
#include "TopLevelWidget.hpp"
#include "Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

class StandaloneWindow : public Application,
public Window
class StandaloneWindow : public Window,
public TopLevelWidget
{
public:
/**
Constructor.
Constructor without parent.
*/
StandaloneWindow();
StandaloneWindow(Application& app)
: Window(app),
TopLevelWidget((Window&)*this) {}

/**
Show window and execute application.
Constructor with parent window, typically used to run as modal.
*/
void exec();
StandaloneWindow(Application& app, Window& parentWindow)
: Window(app, parentWindow),
TopLevelWidget((Window&)*this) {}

private:
Widget* fWidget;

/** @internal */
void onReshape(uint width, uint height) override;

/** @internal */
void _addWidget(Widget* widget) override;

/** @internal */
void _removeWidget(Widget* widget) override;
/**
Overloaded functions to ensure they apply to the Window class.
*/
bool isVisible() const noexcept { return Window::isVisible(); }
void setVisible(bool yesNo) { Window::setVisible(yesNo); }
void hide() { Window::hide(); }
void show() { Window::show(); }
uint getWidth() const noexcept { return Window::getWidth(); }
uint getHeight() const noexcept { return Window::getHeight(); }
const Size<uint> getSize() const noexcept { return Window::getSize(); }
void repaint() noexcept { Window::repaint(); }
void setWidth(uint width) { Window::setWidth(width); }
void setHeight(uint height) { Window::setHeight(height); }
void setSize(uint width, uint height) { Window::setSize(width, height); }
void setSize(const Size<uint>& size) { Window::setSize(size); }
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0)
{ return Window::addIdleCallback(callback, timerFrequencyInMs); }
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); }
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); }
void setGeometryConstraints(uint minimumWidth, uint minimumHeight,
bool keepAspectRatio = false, bool automaticallyScale = false)
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); }

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow)
};


+ 148
- 0
dgl/SubWidget.hpp View File

@@ -0,0 +1,148 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_HPP_INCLUDED
#define DGL_SUBWIDGET_HPP_INCLUDED

#include "Widget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

/**
Sub-Widget class.

This class is the main entry point for creating any reusable widgets from within DGL.
It can be freely positioned from within a parent widget, thus being named subwidget.

Many subwidgets can share the same parent, and subwidgets themselves can also have its own subwidgets.
It is subwidgets all the way down.

TODO check absolute vs relative position and see what makes more sense.

@see CairoSubWidget
*/
class SubWidget : public Widget
{
public:
/**
Constructor.
*/
explicit SubWidget(Widget* parentWidget);

/**
Destructor.
*/
virtual ~SubWidget();

/**
Check if this widget contains the point defined by @a x and @a y.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(T x, T y) const noexcept;

/**
Check if this widget contains the point @a pos.
*/
// TODO rename as containsRelativePos
template<typename T>
bool contains(const Point<T>& pos) const noexcept;

/**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
Point<int> getAbsolutePos() const noexcept;

/**
Get absolute area of this subwidget.
This is the same as `Rectangle<int>(getAbsolutePos(), getSize());`
@see getConstrainedAbsoluteArea()
*/
Rectangle<int> getAbsoluteArea() const noexcept;

/**
Get absolute area of this subwidget, with special consideration for not allowing negative values.
@see getAbsoluteArea()
*/
Rectangle<uint> getConstrainedAbsoluteArea() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
*/
void setAbsolutePos(int x, int y) noexcept;

/**
Set absolute position.
*/
void setAbsolutePos(const Point<int>& pos) noexcept;

/**
Get parent Widget, as passed in the constructor.
*/
Widget* getParentWidget() const noexcept;

/**
Request repaint of this subwidget's area to the window this widget belongs to.
*/
void repaint() noexcept override;

/**
Indicate that this subwidget will draw out of bounds, and thus needs the entire viewport available for drawing.
*/
void setNeedsFullViewportDrawing(bool needsFullViewportForDrawing = true);

protected:
/**
A function called when the subwidget's absolute position is changed.
*/
virtual void onPositionChanged(const PositionChangedEvent&);

private:
struct PrivateData;
PrivateData* const pData;
friend class Widget;
template <class BaseWidget> friend class NanoBaseWidget;
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SubWidget)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_HPP_INCLUDED


+ 101
- 0
dgl/TopLevelWidget.hpp View File

@@ -0,0 +1,101 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

#include "Widget.hpp"

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Window;

// -----------------------------------------------------------------------

/**
Top-Level Widget class.

This is the only Widget class that is allowed to be used directly on a Window.

This widget takes the full size of the Window it is mapped to.
Sub-widgets can be added on top of this top-level widget, by creating them with this class as parent.
Doing so allows for custom position and sizes.

This class is used as the type for DPF Plugin UIs.
So anything that a plugin UI might need that does not belong in a simple Widget will go here.
*/
class TopLevelWidget : public Widget
{
public:
/**
Constructor.
*/
explicit TopLevelWidget(Window& windowToMapTo);

/**
Destructor.
*/
virtual ~TopLevelWidget();

/**
Get the application associated with this top-level widget's window.
*/
Application& getApp() const noexcept;

/**
Get the window associated with this top-level widget.
*/
Window& getWindow() const noexcept;

// TODO group stuff after here, convenience functions present in Window class
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);
bool removeIdleCallback(IdleCallback* callback);
double getScaleFactor() const noexcept;
void repaint() noexcept;
void repaint(const Rectangle<uint>& rect) noexcept;
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false);

DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }

DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }

private:
struct PrivateData;
PrivateData* const pData;
friend class Window;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_HPP_INCLUDED

+ 103
- 0
dgl/Vulkan.hpp View File

@@ -0,0 +1,103 @@
/*
* 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_VULKAN_HPP_INCLUDED
#define DGL_VULKAN_HPP_INCLUDED

#include "ImageBase.hpp"

#include <vulkan/vulkan_core.h>

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

/**
Vulkan Graphics context.
*/
struct VulkanGraphicsContext : GraphicsContext
{
};

// --------------------------------------------------------------------------------------------------------------------

/**
Vulkan Image class.

TODO ...
*/
class VulkanImage : public ImageBase
{
public:
/**
Constructor for a null Image.
*/
VulkanImage();

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
VulkanImage(const char* rawData, uint width, uint height, ImageFormat format);

/**
Constructor using raw image data.
@note @a rawData must remain valid for the lifetime of this Image.
*/
VulkanImage(const char* rawData, const Size<uint>& size, ImageFormat format);

/**
Constructor using another image data.
*/
VulkanImage(const VulkanImage& image);

/**
Destructor.
*/
~VulkanImage() override;

/**
Load image data from memory.
@note @a rawData must remain valid for the lifetime of this Image.
*/
void loadFromMemory(const char* rawData,
const Size<uint>& size,
ImageFormat format = kImageFormatBGRA) noexcept override;

/**
Draw this image at position @a pos using the graphics context @a context.
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

/**
TODO document this.
*/
VulkanImage& operator=(const VulkanImage& image) noexcept;

// FIXME this should not be needed
inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA)
{ loadFromMemory(rdata, Size<uint>(w, h), fmt); };
inline void draw(const GraphicsContext& context)
{ drawAt(context, Point<int>(0, 0)); };
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); };
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif

+ 145
- 119
dgl/Widget.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,82 +19,97 @@

#include "Geometry.hpp"

#include <vector>
START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Forward class names

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UI;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Application;
class ImageSlider;
class NanoWidget;
class SubWidget;
class TopLevelWidget;
class Window;
class StandaloneWindow;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

/**
Base DGL Widget class.

This is the base Widget class, from which all widgets are built.

All widgets have a parent Window where they'll be drawn.
This parent is never changed during the widget lifetime.
All widgets have a parent widget where they'll be drawn, this can be the top-level widget or a group widget.
This parent is never changed during a widget's lifetime.

Widgets receive events in relative coordinates.
(0, 0) means its top-left position.
Widgets receive events in relative coordinates. (0, 0) means its top-left position.

Windows paint widgets in the order they are constructed.
Early widgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widget gets
The top-level widget will draw subwidgets in the order they are constructed.
Early subwidgets are drawn first, at the bottom, then newer ones on top.
Events are sent in the inverse order so that the top-most widgets get
a chance to catch the event and stop its propagation.

All widget event callbacks do nothing by default.
All widget event callbacks do nothing by default and onDisplay MUST be reimplemented by subclasses.

@note It is not possible to subclass this Widget class directly, you must use SubWidget or TopLevelWidget instead.
*/
class Widget
{
public:
/**
Base event data.
@a mod The currently active keyboard modifiers, @see Modifier.
@a time The timestamp (if any).
These are the fields present on all Widget events.

@a mod Currently active keyboard modifiers, @see Modifier.
@a mod Event flags, @see Flag.
@a time Event timestamp (if any).
*/
struct BaseEvent {
uint mod;
uint32_t time;
uint mod;
uint flags;
uint time;

/** Constuctor */
BaseEvent() noexcept : mod(0x0), time(0) {}
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {}
/** Destuctor */
virtual ~BaseEvent() noexcept {}
};

/**
Keyboard event.
@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.

This event represents low-level key presses and releases.
This can be used for "direct" keyboard handing like key bindings, but must not be interpreted as text input.

Keys are represented portably as Unicode code points, using the "natural" code point for the key.
The @a key field is the code for the pressed key, without any modifiers applied.
For example, a press or release of the 'A' key will have `key` 97 ('a')
regardless of whether shift or control are being held.

Alternatively, the raw @a keycode can be used to work directly with physical keys,
but note that this value is not portable and differs between platforms and hardware.

@a press True if the key was pressed, false if released.
@a key Unicode point of the key pressed.
@a keycode Raw keycode.
@see onKeyboard
*/
struct KeyboardEvent : BaseEvent {
bool press;
uint key;
uint keycode;

/** Constuctor */
KeyboardEvent() noexcept
: BaseEvent(),
press(false),
key(0) {}
key(0),
keycode(0) {}
};

/**
Special keyboard event.

This event allows the use of keys that do not have unicode points.
Note that some are non-printable keys.

@a press True if the key was pressed, false if released.
@a key The key pressed.
@see onSpecial
@@ -111,54 +126,94 @@ public:
};

/**
Mouse event.
@a button The button number (1 = left, 2 = middle, 3 = right).
Character input event.

This event represents text input, usually as the result of a key press.
The text is given both as a Unicode character code and a UTF-8 string.

Note that this event is generated by the platform's input system,
so there is not necessarily a direct correspondence between text events and physical key presses.
For example, with some input methods a sequence of several key presses will generate a single character.

@a keycode Raw key code.
@a character Unicode character code.
@a string UTF-8 string.
@see onCharacterInput
*/
struct CharacterInputEvent : BaseEvent {
uint keycode;
uint character;
char string[8];

/** Constuctor */
CharacterInputEvent() noexcept
: BaseEvent(),
keycode(0),
character(0),
string{'\0','\0','\0','\0','\0','\0','\0','\0'} {}
};

/**
Mouse press or release event.

@a button The button number starting from 1 (1 = left, 2 = middle, 3 = right).
@a press True if the button was pressed, false if released.
@a pos The widget-relative coordinates of the pointer.
@see onMouse
*/
struct MouseEvent : BaseEvent {
int button;
uint button;
bool press;
Point<int> pos;
Point<double> pos;

/** Constuctor */
MouseEvent() noexcept
: BaseEvent(),
button(0),
press(false),
pos(0, 0) {}
pos(0.0, 0.0) {}
};

/**
Mouse motion event.

@a pos The widget-relative coordinates of the pointer.
@see onMotion
*/
struct MotionEvent : BaseEvent {
Point<int> pos;
Point<double> pos;

/** Constuctor */
MotionEvent() noexcept
: BaseEvent(),
pos(0, 0) {}
pos(0.0, 0.0) {}
};

/**
Mouse scroll event.
@a pos The widget-relative coordinates of the pointer.
@a delta The scroll distance.

The scroll distance is expressed in "lines",
an arbitrary unit that corresponds to a single tick of a detented mouse wheel.
For example, `delta.y` = 1.0 scrolls 1 line up.
Some systems and devices support finer resolution and/or higher values for fast scrolls,
so programs should handle any value gracefully.

@a pos The widget-relative coordinates of the pointer.
@a delta The scroll distance.
@a direction The direction of the scroll or "smooth".
@see onScroll
*/
struct ScrollEvent : BaseEvent {
Point<int> pos;
Point<float> delta;
Point<double> pos;
Point<double> delta;
ScrollDirection direction;

/** Constuctor */
ScrollEvent() noexcept
: BaseEvent(),
pos(0, 0),
delta(0.0f, 0.0f) {}
pos(0.0, 0.0),
delta(0.0, 0.0),
direction(kScrollSmooth) {}
};

/**
@@ -193,16 +248,18 @@ public:
oldPos(0, 0) {}
};

private:
/**
Constructor.
Private constructor, reserved for TopLevelWidget class.
*/
explicit Widget(Window& parent);
explicit Widget(TopLevelWidget* topLevelWidget);

/**
Constructor for a subwidget.
Private constructor, reserved for SubWidget class.
*/
explicit Widget(Widget* groupWidget);
explicit Widget(Widget* widgetToGroupTo);

public:
/**
Destructor.
*/
@@ -215,9 +272,9 @@ public:
bool isVisible() const noexcept;

/**
Set widget visible (or not) according to @a yesNo.
Set widget visible (or not) according to @a visible.
*/
void setVisible(bool yesNo);
void setVisible(bool visible);

/**
Show widget.
@@ -244,7 +301,7 @@ public:
/**
Get size.
*/
const Size<uint>& getSize() const noexcept;
const Size<uint> getSize() const noexcept;

/**
Set width.
@@ -267,81 +324,58 @@ public:
void setSize(const Size<uint>& size) noexcept;

/**
Get absolute X.
*/
int getAbsoluteX() const noexcept;

/**
Get absolute Y.
*/
int getAbsoluteY() const noexcept;

/**
Get absolute position.
*/
const Point<int>& getAbsolutePos() const noexcept;

/**
Set absolute X.
*/
void setAbsoluteX(int x) noexcept;

/**
Set absolute Y.
*/
void setAbsoluteY(int y) noexcept;

/**
Set absolute position using @a x and @a y values.
Get the Id associated with this widget.
@see setId
*/
void setAbsolutePos(int x, int y) noexcept;
uint getId() const noexcept;

/**
Set absolute position.
Set an Id to be associated with this widget.
@see getId
*/
void setAbsolutePos(const Point<int>& pos) noexcept;
void setId(uint id) noexcept;

/**
Get this widget's window application.
Same as calling getParentWindow().getApp().
Get the application associated with this widget's window.
This is the same as calling `getTopLevelWidget()->getApp()`.
*/
Application& getParentApp() const noexcept;
Application& getApp() const noexcept;

/**
Get parent window, as passed in the constructor.
Get the window associated with this widget.
This is the same as calling `getTopLevelWidget()->getWindow()`.
*/
Window& getParentWindow() const noexcept;
Window& getWindow() const noexcept;

/**
Check if this widget contains the point defined by @a x and @a y.
Get the graphics context associated with this widget's window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/
bool contains(int x, int y) const noexcept;
const GraphicsContext& getGraphicsContext() const noexcept;

/**
Check if this widget contains the point @a pos.
Get top-level widget, as passed directly in the constructor
or going up the chain of group widgets until it finds the top-level one.
*/
bool contains(const Point<int>& pos) const noexcept;
TopLevelWidget* getTopLevelWidget() const noexcept;

/**
Tell this widget's window to repaint itself.
Request repaint of this widget's area to the window this widget belongs to.
On the raw Widget class this function does nothing.
*/
void repaint() noexcept;
virtual void repaint() noexcept;

/**
Get the Id associated with this widget.
@see setId
*/
uint getId() const noexcept;
DISTRHO_DEPRECATED_BY("getApp()")
Application& getParentApp() const noexcept { return getApp(); }

/**
Set an Id to be associated with this widget.
@see getId
*/
void setId(uint id) noexcept;
DISTRHO_DEPRECATED_BY("getWindow()")
Window& getParentWindow() const noexcept { return getWindow(); }

protected:
/**
A function called to draw the view contents with OpenGL.
A function called to draw the widget contents.
*/
virtual void onDisplay() = 0;

@@ -357,6 +391,12 @@ protected:
*/
virtual bool onSpecial(const SpecialEvent&);

/**
A function called when an UTF-8 character is received.
@return True to stop event propagation, false otherwise.
*/
virtual bool onCharacterInput(const CharacterInputEvent&);

/**
A function called when a mouse button is pressed or released.
@return True to stop event propagation, false otherwise.
@@ -380,30 +420,16 @@ protected:
*/
virtual void onResize(const ResizeEvent&);

/**
A function called when the widget's absolute position is changed.
*/
virtual void onPositionChanged(const PositionChangedEvent&);

private:
struct PrivateData;
PrivateData* const pData;

/** @internal */
explicit Widget(Widget* groupWidget, bool addToSubWidgets);

friend class ImageSlider;
friend class NanoWidget;
friend class Window;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UI;
#endif
friend class SubWidget;
friend class TopLevelWidget;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 322
- 64
dgl/Window.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,50 +19,79 @@

#include "Geometry.hpp"

#ifdef DISTRHO_DEFINES_H_INCLUDED
START_NAMESPACE_DISTRHO
class UIExporter;
END_NAMESPACE_DISTRHO
#endif

START_NAMESPACE_DGL

class Application;
class TopLevelWidget;

// -----------------------------------------------------------------------

class Application;
class Widget;
class StandaloneWindow;
/**
DGL Window class.

This is the where all OS-related events initially happen, before being propagated to any widgets.

A Window MUST have an Application instance tied to it.
It is not possible to swap Application instances from within the lifetime of a Window.
But it is possible to completely change the Widgets that a Window contains during its lifetime.

Typically the event handling functions as following:
Application -> Window -> Top-Level-Widget -> SubWidgets

Please note that, unlike many other graphical toolkits out there,
DGL makes a clear distinction between a Window and a Widget.
You cannot directly draw in a Window, you need to create a Widget for that.

Also, a Window MUST have a single top-level Widget.
The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle.

...
*/
class Window
{
public:
#ifndef DGL_FILE_BROWSER_DISABLED
/**
File browser options.
@see Window::openFileBrowser
*/
struct FileBrowserOptions {
/**
File browser button state.
This allows to customize the behaviour of the file browse dialog buttons.
*/
enum ButtonState {
kButtonInvisible,
kButtonVisibleUnchecked,
kButtonVisibleChecked,
};

/** Start directory, uses current working directory if null */
const char* startDir;
/** File browser dialog window title, uses "FileBrowser" if null */
const char* title;
/** File browser dialog window width */
uint width;
/** File browser dialog window height */
uint height;
// TODO file filter

/**
File browser buttons.

0 means hidden.
1 means visible and unchecked.
2 means visible and checked.
/**
File browser buttons.
*/
struct Buttons {
uint listAllFiles;
uint showHidden;
uint showPlaces;
/** Whether to list all files vs only those with matching file extension */
ButtonState listAllFiles;
/** Whether to show hidden files */
ButtonState showHidden;
/** Whether to show list of places (bookmarks) */
ButtonState showPlaces;

/** Constuctor for default values */
Buttons()
: listAllFiles(2),
showHidden(1),
showPlaces(1) {}
: listAllFiles(kButtonVisibleChecked),
showHidden(kButtonVisibleUnchecked),
showPlaces(kButtonVisibleUnchecked) {}
} buttons;

/** Constuctor for default values */
@@ -75,91 +104,320 @@ public:
};
#endif // DGL_FILE_BROWSER_DISABLED

/**
Constructor for a regular, standalone window.
*/
explicit Window(Application& app);

/**
Constructor for a modal window, by having another window as its parent.
The Application instance must be the same between the 2 windows.
*/
explicit Window(Application& app, Window& parent);
explicit Window(Application& app, intptr_t parentId, double scaling, bool resizable);
virtual ~Window();

void show();
void hide();
void close();
void exec(bool lockWait = false);
/**
Constructor for an embed Window without known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
double scaleFactor,
bool resizable);

void focus();
void repaint() noexcept;
/**
Constructor for an embed Window with known size,
typically used in modules or plugins that run inside another host.
*/
explicit Window(Application& app,
uintptr_t parentWindowHandle,
uint width,
uint height,
double scaleFactor,
bool resizable);

#ifndef DGL_FILE_BROWSER_DISABLED
bool openFileBrowser(const FileBrowserOptions& options);
#endif
/**
Destructor.
*/
virtual ~Window();

/**
Whether this Window is embed into another (usually not DGL-controlled) Window.
*/
bool isEmbed() const noexcept;

/**
Check if this window is visible / mapped.
Invisible windows do not receive events except resize.
@see setVisible(bool)
*/
bool isVisible() const noexcept;
void setVisible(bool yesNo);

/**
Set windows visible (or not) according to @a visible.
Only valid for standalones, embed windows are always visible.
@see isVisible(), hide(), show()
*/
void setVisible(bool visible);

/**
Show window.
This is the same as calling setVisible(true).
@see isVisible(), setVisible(bool)
*/
void show();

/**
Hide window.
This is the same as calling setVisible(false).
@see isVisible(), setVisible(bool)
*/
void hide();

/**
Hide window and notify application of a window close event.
The application event-loop will stop when all windows have been closed.
For standalone windows only, has no effect if window is embed.
@see isEmbed()

@note It is possible to hide the window while not stopping the event-loop.
A closed window is always hidden, but the reverse is not always true.
*/
void close();

/**
Check if this window is resizable.
@see setResizable
*/
bool isResizable() const noexcept;
void setResizable(bool yesNo);

/**
Set window as resizable (by the user or window manager).
It is always possible to resize a window programmatically, which is not the same as the user being allowed to it.
@note This function does nothing for plugins, where the resizable state is set via macro.
@see DISTRHO_UI_USER_RESIZABLE
*/
void setResizable(bool resizable);

/**
Get width.
*/
uint getWidth() const noexcept;

/**
Get height.
*/
uint getHeight() const noexcept;

/**
Get size.
*/
Size<uint> getSize() const noexcept;

/**
Set width.
*/
void setWidth(uint width);

/**
Set height.
*/
void setHeight(uint height);

/**
Set size using @a width and @a height values.
*/
void setSize(uint width, uint height);
void setSize(Size<uint> size);

/**
Set size.
*/
void setSize(const Size<uint>& size);

/**
Get the title of the window previously set with setTitle().
*/
const char* getTitle() const noexcept;
void setTitle(const char* title);

void setGeometryConstraints(uint width, uint height, bool aspect);
void setTransientWinId(uintptr_t winId);
/**
Set the title of the window, typically displayed in the title bar or in window switchers.

This only makes sense for non-embedded windows.
*/
void setTitle(const char* title);

double getScaling() const noexcept;
/**
Check if key repeat events are ignored.
*/
bool isIgnoringKeyRepeat() const noexcept;

bool getIgnoringKeyRepeat() const noexcept;
/**
Set to ignore (or not) key repeat events according to @a ignore.
*/
void setIgnoringKeyRepeat(bool ignore) noexcept;

/**
Add a callback function to be triggered on every idle cycle or on a specific timer frequency.
You can add more than one, and remove them at anytime with removeIdleCallback().
This can be used to perform some action at a regular interval with relatively low frequency.

If providing a timer frequency, there are a few things to note:
1. There is a platform-specific limit to the number of supported timers, and overhead associated with each,
so you should create only a few timers and perform several tasks in one if necessary.
2. This timer frequency is not guaranteed to have a resolution better than 10ms
(the maximum timer resolution on Windows) and may be rounded up if it is too short.
On X11 and MacOS, a resolution of about 1ms can usually be relied on.
*/
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);

/**
Remove an idle callback previously added via addIdleCallback().
*/
bool removeIdleCallback(IdleCallback* callback);

/**
Get the application associated with this window.
*/
Application& getApp() const noexcept;
intptr_t getWindowId() const noexcept;

/**
Get the graphics context associated with this window.
GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
for example GraphicsContext.
@see CairoSubWidget, CairoTopLevelWidget
*/
const GraphicsContext& getGraphicsContext() const noexcept;

void addIdleCallback(IdleCallback* const callback);
void removeIdleCallback(IdleCallback* const callback);
/**
Get the "native" window handle.
Returned value depends on the platform:
- HaikuOS: This is a pointer to a `BView`.
- MacOS: This is a pointer to an `NSView*`.
- Windows: This is a `HWND`.
- Everything else: This is an [X11] `Window`.
*/
uintptr_t getNativeWindowHandle() const noexcept;

/**
Get the scale factor requested for this window.
This is purely informational, and up to developers to choose what to do with it.

If you do not want to deal with this yourself,
consider using setGeometryConstraints() where you can specify to automatically scale the window contents.
@see setGeometryConstraints
*/
double getScaleFactor() const noexcept;

/**
Grab the keyboard input focus.
*/
void focus();

#ifndef DGL_FILE_BROWSER_DISABLED
/**
Open a file browser dialog with this window as parent.
A few options can be specified to setup the dialog.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.

This function does not block the event loop.
*/
bool openFileBrowser(const FileBrowserOptions& options);
#endif

/**
Request repaint of this window, for the entire area.
*/
void repaint() noexcept;

/**
Request partial repaint of this window, with bounds according to @a rect.
*/
void repaint(const Rectangle<uint>& rect) noexcept;

/**
Run this window as a modal, blocking input events from the parent.
Only valid for windows that have been created with another window as parent (as passed in the constructor).
Can optionally block-wait, but such option is only available if the application is running as standalone.
*/
void runAsModal(bool blockWait = false);

/**
Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically.
*/
void setGeometryConstraints(uint minimumWidth,
uint minimumHeight,
bool keepAspectRatio = false,
bool automaticallyScale = false);

/** DEPRECATED Use isIgnoringKeyRepeat(). */
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()")
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); }

/** DEPRECATED Use getScaleFactor(). */
DISTRHO_DEPRECATED_BY("getScaleFactor()")
inline double getScaling() const noexcept { return getScaleFactor(); }

/** DEPRECATED Use runAsModal(bool). */
DISTRHO_DEPRECATED_BY("runAsModal(bool)")
inline void exec(bool blockWait = false) { runAsModal(blockWait); }

protected:
virtual void onDisplayBefore();
virtual void onDisplayAfter();
/**
A function called when the window is attempted to be closed.
Returning true closes the window, which is the default behaviour.
Override this method and return false to prevent the window from being closed by the user.
*/
virtual bool onClose();

/**
A function called when the window gains or loses the keyboard focus.
The default implementation does nothing.
*/
virtual void onFocus(bool focus, CrossingMode mode);

/**
A function called when the window is resized.
If there is a top-level widget associated with this window, its size will be set right after this function.
The default implementation sets up drawing context where necessary.
*/
virtual void onReshape(uint width, uint height);
virtual void onClose();

#ifndef DGL_FILE_BROWSER_DISABLED
virtual void fileBrowserSelected(const char* filename);
#endif
/**
A function called when a path is selected by the user, as triggered by openFileBrowser().
This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
The default implementation does nothing.
*/
virtual void onFileSelected(const char* filename);

// internal
void _setAutoScaling(double scaling) noexcept;
/** DEPRECATED Use onFileSelected(). */
DISTRHO_DEPRECATED_BY("onFileSelected(const char*)")
inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); }
#endif

private:
struct PrivateData;
PrivateData* const pData;
friend class Application;
friend class Widget;
friend class StandaloneWindow;
#ifdef DISTRHO_DEFINES_H_INCLUDED
friend class DISTRHO_NAMESPACE::UIExporter;
#endif

virtual void _addWidget(Widget* const widget);
virtual void _removeWidget(Widget* const widget);
void _idle();

bool handlePluginKeyboard(const bool press, const uint key);
bool handlePluginSpecial(const bool press, const Key key);
friend class TopLevelWidget;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window);
};

// -----------------------------------------------------------------------

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

+ 33
- 30
dgl/src/Application.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,14 +15,13 @@
*/

#include "ApplicationPrivateData.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

Application::Application()
: pData(new PrivateData()) {}
Application::Application(const bool isStandalone)
: pData(new PrivateData(isStandalone)) {}

Application::~Application()
{
@@ -31,44 +30,48 @@ Application::~Application()

void Application::idle()
{
for (std::list<Window*>::iterator it = pData->windows.begin(), ite = pData->windows.end(); it != ite; ++it)
{
Window* const window(*it);
window->_idle();
}

for (std::list<IdleCallback*>::iterator it = pData->idleCallbacks.begin(), ite = pData->idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
pData->idle(0);
}

void Application::exec(int idleTime)
void Application::exec(const uint idleTimeInMs)
{
for (; pData->doLoop;)
{
idle();
d_msleep(idleTime);
}
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,);

while (! pData->isQuitting)
pData->idle(idleTimeInMs);
}

void Application::quit()
{
pData->doLoop = false;
DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,);

for (std::list<Window*>::reverse_iterator rit = pData->windows.rbegin(), rite = pData->windows.rend(); rit != rite; ++rit)
{
Window* const window(*rit);
window->close();
}
pData->quit();
}

bool Application::isQuiting() const noexcept
{
return !pData->doLoop;
return pData->isQuitting;
}

void Application::addIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

pData->idleCallbacks.push_back(callback);
}

void Application::removeIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)

pData->idleCallbacks.remove(callback);
}

void Application::setClassName(const char* const name)
{
pData->setClassName(name);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 137
- 0
dgl/src/ApplicationPrivateData.cpp View File

@@ -0,0 +1,137 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "ApplicationPrivateData.hpp"
#include "../Window.hpp"

#include "pugl.hpp"

#include <ctime>

START_NAMESPACE_DGL

typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator;

// --------------------------------------------------------------------------------------------------------------------

Application::PrivateData::PrivateData(const bool standalone)
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE,
standalone ? PUGL_WORLD_THREADS : 0x0)),
isStandalone(standalone),
isQuitting(false),
isQuittingInNextCycle(false),
isStarting(true),
visibleWindows(0),
windows(),
idleCallbacks()
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

puglSetWorldHandle(world, this);
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));
#ifdef HAVE_X11
sofdFileDialogSetup(world);
#endif
}

Application::PrivateData::~PrivateData()
{
DISTRHO_SAFE_ASSERT(isStarting || isQuitting);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();

if (world != nullptr)
puglFreeWorld(world);
}

// --------------------------------------------------------------------------------------------------------------------

void Application::PrivateData::oneWindowShown() noexcept
{
if (++visibleWindows == 1)
{
isQuitting = false;
isStarting = false;
}
}

void Application::PrivateData::oneWindowClosed() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows != 0,);

if (--visibleWindows == 0)
isQuitting = true;
}

// --------------------------------------------------------------------------------------------------------------------

void Application::PrivateData::idle(const uint timeoutInMs)
{
if (isQuittingInNextCycle)
{
quit();
isQuittingInNextCycle = false;
}

if (world != nullptr)
{
const double timeoutInSeconds = timeoutInMs != 0
? static_cast<double>(timeoutInMs) / 1000.0
: 0.0;

puglUpdate(world, timeoutInSeconds);
}

for (std::list<IdleCallback*>::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it)
{
IdleCallback* const idleCallback(*it);
idleCallback->idleCallback();
}
}

void Application::PrivateData::quit()
{
DISTRHO_SAFE_ASSERT_RETURN(isStandalone,);

if (! isQuittingInNextCycle)
{
isQuittingInNextCycle = true;
return;
}

isQuitting = true;

#ifndef DPF_TEST_APPLICATION_CPP
for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit)
{
DGL_NAMESPACE::Window* const window(*rit);
window->close();
}
#endif
}

void Application::PrivateData::setClassName(const char* const name)
{
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);

puglSetClassName(world, name);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 56
- 38
dgl/src/ApplicationPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,53 +18,71 @@
#define DGL_APP_PRIVATE_DATA_HPP_INCLUDED

#include "../Application.hpp"
#include "../../distrho/extra/Sleep.hpp"

#include <list>

typedef struct PuglWorldImpl PuglWorld;

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
class Window;

// --------------------------------------------------------------------------------------------------------------------

struct Application::PrivateData {
bool doLoop;
/** Pugl world instance. */
PuglWorld* const world;

/** Whether the application is running as standalone, otherwise it is part of a plugin. */
const bool isStandalone;

/** Whether the applicating is about to quit, or already stopped. Defaults to false. */
bool isQuitting;

/** Helper for safely close everything from main thread. */
bool isQuittingInNextCycle;

/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */
bool isStarting;

/** Counter of visible windows, only used in standalone mode.
If 0->1, application is starting. If 1->0, application is quitting/stopping. */
uint visibleWindows;
std::list<Window*> windows;
std::list<IdleCallback*> idleCallbacks;

PrivateData()
: doLoop(true),
visibleWindows(0),
windows(),
idleCallbacks() {}

~PrivateData()
{
DISTRHO_SAFE_ASSERT(! doLoop);
DISTRHO_SAFE_ASSERT(visibleWindows == 0);

windows.clear();
idleCallbacks.clear();
}

void oneShown() noexcept
{
if (++visibleWindows == 1)
doLoop = true;
}

void oneHidden() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(visibleWindows > 0,);

if (--visibleWindows == 0)
doLoop = false;
}

DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)

/** List of windows for this application. Only used during `close`. */
std::list<DGL_NAMESPACE::Window*> windows;

/** List of idle callbacks for this application. */
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks;

/** Constructor and destructor */
explicit PrivateData(bool standalone);
~PrivateData();

/** Flag one window as shown, which increments @a visibleWindows.
Sets @a isQuitting and @a isStarting as false if this is the first window.
For standalone mode only. */
void oneWindowShown() noexcept;

/** Flag one window as closed, which decrements @a visibleWindows.
Sets @a isQuitting as true if this is the last window.
For standalone mode only. */
void oneWindowClosed() noexcept;

/** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */
void idle(uint timeoutInMs);

/** Set flag indicating application is quitting, and close all windows in reverse order of registration.
For standalone mode only. */
void quit();

/** Set pugl world class name. */
void setClassName(const char* name);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 728
- 36
dgl/src/Cairo.cpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,77 +15,143 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Geometry.hpp"
#include "../Cairo.hpp"
#include "../Color.hpp"
#include "../ImageBaseWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

static void notImplemented(const char *name)
static void notImplemented(const char* const name)
{
d_stderr2("cairo function not implemented: %s", name);
}

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

if (includeAlpha)
cairo_set_source_rgba(handle, red, green, blue, alpha);
else
cairo_set_source_rgb(handle, red, green, blue);
}

// -----------------------------------------------------------------------
// Line

template<typename T>
void Line<T>::draw(const GraphicsContext& context, const T width)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, width);
cairo_move_to(handle, posStart.getX(), posStart.getY());
cairo_line_to(handle, posEnd.getX(), posEnd.getY());
cairo_stroke(handle);
}

template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

template<typename T>
void Circle<T>::_draw(const bool outline)
static void drawCircle(cairo_t* const handle,
const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
notImplemented("Circle::draw");
}
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

// -----------------------------------------------------------------------
// Triangle
const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;

// TODO use arc
/*
cairo_arc(handle, origx, origy, size, sin, cos);
*/

cairo_move_to(handle, x + origx, y + origy);

for (uint i=1; i<numSegments; ++i)
{
cairo_line_to(handle, x + origx, y + origy);

t = x;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

cairo_line_to(handle, x + origx, y + origy);

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}

template<typename T>
void Triangle<T>::_draw(const bool outline)
void Circle<T>::draw(const GraphicsContext& context)
{
notImplemented("Triangle::draw");
}
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

// -----------------------------------------------------------------------
// Rectangle
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Rectangle<T>::_draw(const bool outline)
void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
notImplemented("Rectangle::draw");
}
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

// -----------------------------------------------------------------------
// Possible template data types
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

template class Point<double>;
template class Point<float>;
template class Point<int>;
template class Point<uint>;
template class Point<short>;
template class Point<ushort>;
cairo_set_line_width(handle, lineWidth);
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
}

template class Size<double>;
template class Size<float>;
template class Size<int>;
template class Size<uint>;
template class Size<short>;
template class Size<ushort>;
template<typename T>
void Circle<T>::draw()
{
notImplemented("Circle::draw");
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;
template<typename T>
void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}

template class Circle<double>;
template class Circle<float>;
@@ -93,6 +160,60 @@ template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

template<typename T>
static void drawTriangle(cairo_t* const handle,
const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

cairo_move_to(handle, pos1.getX(), pos1.getY());
cairo_line_to(handle, pos2.getX(), pos2.getY());
cairo_line_to(handle, pos3.getX(), pos3.getY());
cairo_line_to(handle, pos1.getX(), pos1.getY());

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}

template<typename T>
void Triangle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

drawTriangle<T>(handle, pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, lineWidth);
drawTriangle<T>(handle, pos1, pos2, pos3, true);
}

template<typename T>
void Triangle<T>::draw()
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
@@ -100,6 +221,54 @@ template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// -----------------------------------------------------------------------
// Rectangle

template<typename T>
static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline)
{
cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());

if (outline)
cairo_stroke(handle);
else
cairo_fill(handle);
}

template<typename T>
void Rectangle<T>::draw(const GraphicsContext& context)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

drawRectangle(handle, *this, false);
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_line_width(handle, lineWidth);
drawRectangle(handle, *this, true);
}

template<typename T>
void Rectangle<T>::draw()
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
@@ -107,6 +276,529 @@ template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// CairoImage

static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept
{
switch (format)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
return CAIRO_FORMAT_A8;
case kImageFormatBGR:
case kImageFormatRGB:
return CAIRO_FORMAT_RGB24;
case kImageFormatBGRA:
case kImageFormatRGBA:
return CAIRO_FORMAT_ARGB32;
}

return CAIRO_FORMAT_INVALID;
}

/*
static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept
{
switch (format)
{
case CAIRO_FORMAT_INVALID:
break;
case CAIRO_FORMAT_ARGB32:
break;
case CAIRO_FORMAT_RGB24:
break;
case CAIRO_FORMAT_A8:
break;
case CAIRO_FORMAT_A1:
break;
case CAIRO_FORMAT_RGB16_565:
break;
case CAIRO_FORMAT_RGB30:
break;
}

return kImageFormatNull;
}
*/

CairoImage::CairoImage()
: ImageBase(),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr) {}

CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr)
{
loadFromMemory(rdata, w, h, fmt);
}

CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt),
surface(nullptr),
surfacedata(nullptr),
datarefcount(nullptr)
{
loadFromMemory(rdata, s, fmt);
}

CairoImage::CairoImage(const CairoImage& image)
: ImageBase(image.rawData, image.size, image.format),
surface(cairo_surface_reference(image.surface)),
surfacedata(image.surfacedata),
datarefcount(image.datarefcount)
{
if (datarefcount != nullptr)
++(*datarefcount);
}

CairoImage::~CairoImage()
{
cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
{
std::free(surfacedata);
std::free(datarefcount);
}
}

void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
const cairo_format_t cairoformat = asCairoImageFormat(fmt);
const int width = static_cast<int>(s.getWidth());
const int height = static_cast<int>(s.getHeight());
const int stride = cairo_format_stride_for_width(cairoformat, width);

uchar* const newdata = (uchar*)std::malloc(static_cast<size_t>(width * height * stride * 4));
DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,);

cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride);
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),);
DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),);

cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));

surface = newsurface;
surfacedata = newdata;
*datarefcount = 1;

switch (fmt)
{
case kImageFormatNull:
break;
case kImageFormatGrayscale:
// Grayscale to A8
// TODO
break;
case kImageFormatBGR:
// BGR8 to CAIRO_FORMAT_RGB24
for (int h = 0; h < height; ++h)
{
for (int w = 0; w < width; ++w)
{
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*3+w*3+0]);
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*3+w*3+1]);
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*3+w*3+2]);
newdata[h*width*4+w*4+3] = 0;
}
}
break;
case kImageFormatBGRA:
// BGRA8 to CAIRO_FORMAT_ARGB32
// FIXME something is wrong here...
for (int h = 0, t; h < height; ++h)
{
for (int w = 0; w < width; ++w)
{
if ((t = rdata[h*width*4+w*4+3]) != 0)
{
newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*4+w*4+0]);
newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*4+w*4+1]);
newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*4+w*4+2]);
newdata[h*width*4+w*4+3] = static_cast<uchar>(t);
}
else
{
// make all pixels zero, cairo does not render full transparency otherwise
memset(&newdata[h*width*4+w*4], 0, 4);
}
}
}
break;
case kImageFormatRGB:
// RGB8 to CAIRO_FORMAT_RGB24
// TODO
break;
case kImageFormatRGBA:
// RGBA8 to CAIRO_FORMAT_ARGB32
// TODO
break;
}

ImageBase::loadFromMemory(rdata, s, fmt);
}

// const GraphicsContext& context
void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept
{
struct PngReaderData
{
const char* dataPtr;
uint sizeLeft;

static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept
{
PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure);

if (readerData.sizeLeft < length)
return CAIRO_STATUS_READ_ERROR;

std::memcpy(data, readerData.dataPtr, length);
readerData.dataPtr += length;
readerData.sizeLeft -= length;
return CAIRO_STATUS_SUCCESS;
}
};

PngReaderData readerData;
readerData.dataPtr = pngData;
readerData.sizeLeft = pngSize;

cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData);
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);

const int newwidth = cairo_image_surface_get_width(newsurface);
const int newheight = cairo_image_surface_get_height(newsurface);
DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,);
DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,);

cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));

surface = newsurface;
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
*datarefcount = 1;

rawData = nullptr;
format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface));
size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight));
}

void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
{
if (surface == nullptr)
return;

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;

cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
cairo_paint(handle);
}

CairoImage& CairoImage::operator=(const CairoImage& image) noexcept
{
cairo_surface_t* newsurface = cairo_surface_reference(image.surface);
cairo_surface_destroy(surface);

if (datarefcount != nullptr && --(*datarefcount) == 0)
{
std::free(surfacedata);
std::free(datarefcount);
}

surface = newsurface;
rawData = image.rawData;
size = image.size;
format = image.format;
surfacedata = image.surfacedata;
datarefcount = image.datarefcount;

if (datarefcount != nullptr)
++(*datarefcount);

return *this;
}

// -----------------------------------------------------------------------
// CairoSubWidget

template <>
CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent)
: SubWidget(parent) {}

template class CairoBaseWidget<SubWidget>;

// -----------------------------------------------------------------------
// CairoTopLevelWidget

template <>
CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo)
: TopLevelWidget(windowToMapTo) {}

template class CairoBaseWidget<TopLevelWidget>;

// -----------------------------------------------------------------------
// CairoStandaloneWindow

template <>
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app)
: StandaloneWindow(app) {}

template <>
CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow)
: StandaloneWindow(app, parentWindow) {}

template class CairoBaseWidget<StandaloneWindow>;

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<CairoImage>::onDisplay()
{
img.draw(getGraphicsContext());
}
#endif

template class ImageBaseAboutWindow<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<CairoImage>::PrivateData::init()
{
alwaysRepaint = true;
cairoSurface = nullptr;
}

template <>
void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
{
cairo_surface_destroy((cairo_surface_t*)cairoSurface);
cairoSurface = nullptr;
}

/**
Get the pixel size in bytes.
@return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes.
*/
static int getBytesPerPixel(const cairo_format_t format) noexcept
{
switch (format)
{
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_RGB30:
return 4;
case CAIRO_FORMAT_RGB16_565:
return 2;
case CAIRO_FORMAT_A8:
return 1;
case CAIRO_FORMAT_A1:
return 0;
default:
DISTRHO_SAFE_ASSERT(false);
return 0;
}
}

static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept
{
const cairo_format_t format = cairo_image_surface_get_format(origsurface);
const int bpp = getBytesPerPixel(format);

if (bpp == 0)
return nullptr;

const int fullWidth = cairo_image_surface_get_width(origsurface);
const int fullHeight = cairo_image_surface_get_height(origsurface);
const int stride = cairo_image_surface_get_stride(origsurface);
uchar* const fullData = cairo_image_surface_get_data(origsurface);

x = (x < fullWidth) ? x : fullWidth;
y = (y < fullHeight) ? y : fullHeight;
width = (x + width < fullWidth) ? width : (fullWidth - x);
height = (x + height < fullHeight) ? height : (fullHeight - x);

uchar* const data = fullData + (x * bpp + y * stride);
return cairo_image_surface_create_for_data(data, format, width, height, stride);
}

template <>
void ImageBaseKnob<CairoImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);

cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;

if (! pData->isReady)
{
const int layerW = static_cast<int>(pData->imgLayerWidth);
const int layerH = static_cast<int>(pData->imgLayerHeight);
int layerNum = 0;

if (pData->rotationAngle == 0)
layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5);

const int layerX = pData->isImgVertical ? 0 : layerNum * layerW;
const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH;

cairo_surface_t* newsurface;

if (pData->rotationAngle == 0)
{
newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH);
}
else
{
newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH);
cairo_t* const cr = cairo_create(newsurface);
cairo_translate(cr, 0.5 * layerW, 0.5 * layerH);
cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180));
cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH);
cairo_paint(cr);
cairo_destroy(cr);
}

DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);

cairo_surface_destroy(surface);
pData->cairoSurface = surface = newsurface;
pData->isReady = true;
}

if (surface != nullptr)
{
cairo_set_source_surface(handle, surface, 0, 0);
cairo_paint(handle);
}
}

template class ImageBaseKnob<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<CairoImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<CairoImage>;

// -----------------------------------------------------------------------

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;

bool needsResetClip = false;

cairo_matrix_t matrix;
cairo_get_matrix(handle, &matrix);

if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
{
// full viewport size
cairo_translate(handle, 0, 0);
}
else if (needsViewportScaling)
{
// limit viewport to widget bounds
// NOTE only used for nanovg for now, which is not relevant here
cairo_translate(handle, 0, 0);
}
else
{
// set viewport pos
cairo_translate(handle, absolutePos.getX(), absolutePos.getY());

// then cut the outer bounds
cairo_rectangle(handle,
0,
0,
std::round(self->getWidth() * autoScaleFactor),
std::round(self->getHeight() * autoScaleFactor));

cairo_clip(handle);
needsResetClip = true;
}

// display widget
self->onDisplay();

if (needsResetClip)
cairo_reset_clip(handle);

cairo_set_matrix(handle, &matrix);

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// FIXME anything needed here?
#if 0
// full viewport size
if (window.pData->autoScaling)
glViewport(0, -(height * autoScaleFactor - height), width * autoScaleFactor, height * autoScaleFactor);
else
glViewport(0, 0, width, height);
#endif

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
GraphicsContext& context((GraphicsContext&)graphicsContext);
((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
return context;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 34
- 37
dgl/src/Color.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -16,10 +16,6 @@

#include "../Color.hpp"

#ifndef HAVE_DCAIRO
#include "nanovg/nanovg.h"
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
@@ -61,27 +57,27 @@ static uchar getFixedRange2(const float& value)
return 0;
if (value2 >= 255.0f)
return 255;
return static_cast<uchar>(value2);
return static_cast<uchar>(value2 + 0.5f);
}

// -----------------------------------------------------------------------

Color::Color() noexcept
: red(1.0f),
green(1.0f),
blue(1.0f),
: red(0.0f),
green(0.0f),
blue(0.0f),
alpha(1.0f) {}

Color::Color(int r, int g, int b, int a) noexcept
Color::Color(const int r, const int g, const int b, const float a) noexcept
: red(static_cast<float>(r)/255.0f),
green(static_cast<float>(g)/255.0f),
blue(static_cast<float>(b)/255.0f),
alpha(static_cast<float>(a)/255.0f)
alpha(a)
{
fixBounds();
}

Color::Color(float r, float g, float b, float a) noexcept
Color::Color(const float r, const float g, const float b, const float a) noexcept
: red(r),
green(g),
blue(b),
@@ -109,7 +105,7 @@ Color& Color::operator=(const Color& color) noexcept
return *this;
}

Color::Color(const Color& color1, const Color& color2, float u) noexcept
Color::Color(const Color& color1, const Color& color2, const float u) noexcept
: red(color1.red),
green(color1.green),
blue(color1.blue),
@@ -136,65 +132,66 @@ Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
return col;
}

Color Color::fromHTML(const char* rgb, float alpha)
Color Color::fromHTML(const char* rgb, const float alpha) noexcept
{
Color fallback;
DISTRHO_SAFE_ASSERT_RETURN(rgb != nullptr && rgb[0] != '\0', fallback);

if (rgb[0] == '#') ++rgb;
if (rgb[0] == '#')
++rgb;
DISTRHO_SAFE_ASSERT_RETURN(rgb[0] != '\0', fallback);

std::size_t rgblen(std::strlen(rgb));
std::size_t rgblen = std::strlen(rgb);
DISTRHO_SAFE_ASSERT_RETURN(rgblen == 3 || rgblen == 6, fallback);

char rgbtmp[3] = { '\0', '\0', '\0' };
char rgbtmp[5] = { '0', 'x', '\0', '\0', '\0' };
int r, g, b;

if (rgblen == 3)
{
rgbtmp[0] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[0];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;

rgbtmp[0] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[1];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;

rgbtmp[0] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
rgbtmp[2] = rgb[2];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16)) * 17;
}
else
{
rgbtmp[0] = rgb[0];
rgbtmp[1] = rgb[1];
rgbtmp[2] = rgb[0];
rgbtmp[3] = rgb[1];
r = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));

rgbtmp[0] = rgb[2];
rgbtmp[1] = rgb[3];
rgbtmp[2] = rgb[2];
rgbtmp[3] = rgb[3];
g = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));

rgbtmp[0] = rgb[4];
rgbtmp[1] = rgb[5];
rgbtmp[2] = rgb[4];
rgbtmp[3] = rgb[5];
b = static_cast<int>(std::strtol(rgbtmp, nullptr, 16));
}

return Color(r, g, b, static_cast<int>(getFixedRange(alpha)*255.0f));
return Color(r, g, b, alpha);
}

void Color::interpolate(const Color& other, float u) noexcept
{
fixRange(u);
const float oneMinusU(1.0f - u);
const float oneMinusU = 1.0f - u;

red = red * oneMinusU + other.red * u;
green = green * oneMinusU + other.green * u;
blue = blue * oneMinusU + other.blue * u;
alpha = alpha * oneMinusU + other.alpha * u;
red = (red * oneMinusU) + (other.red * u);
green = (green * oneMinusU) + (other.green * u);
blue = (blue * oneMinusU) + (other.blue * u);
alpha = (alpha * oneMinusU) + (other.alpha * u);

fixBounds();
}

// -----------------------------------------------------------------------

bool Color::isEqual(const Color& color, bool withAlpha) noexcept
bool Color::isEqual(const Color& color, const bool withAlpha) noexcept
{
const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]);
@@ -212,7 +209,7 @@ bool Color::isEqual(const Color& color, bool withAlpha) noexcept
return (r1 == r2 && g1 == g2 && b1 == b2);
}

bool Color::isNotEqual(const Color& color, bool withAlpha) noexcept
bool Color::isNotEqual(const Color& color, const bool withAlpha) noexcept
{
const uchar r1 = getFixedRange2(rgba[0]);
const uchar g1 = getFixedRange2(rgba[1]);


+ 72
- 8
dgl/src/Common.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,12 +17,13 @@
#ifndef DGL_COMMON_HPP_INCLUDED
#define DGL_COMMON_HPP_INCLUDED

#include "../ImageWidgets.hpp"
#include "../ImageBaseWidgets.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

template <class ImageType>
struct ButtonImpl {
enum State {
kStateNormal = 0,
@@ -32,11 +33,11 @@ struct ButtonImpl {

int button;
int state;
Widget* self;
ImageBaseButton<ImageType>* const self;

ImageButton::Callback* callback_img;
typename ImageBaseButton<ImageType>::Callback* callback_img;

ButtonImpl(Widget* const s) noexcept
explicit ButtonImpl(ImageBaseButton<ImageType>* const s) noexcept
: button(-1),
state(kStateNormal),
self(s),
@@ -66,7 +67,7 @@ struct ButtonImpl {
self->repaint();

if (callback_img != nullptr)
callback_img->imageButtonClicked((ImageButton*)self, button2);
callback_img->imageButtonClicked(self, button2);

return true;
}
@@ -74,7 +75,7 @@ struct ButtonImpl {
// button was pressed, wait for release
if (ev.press && self->contains(ev.pos))
{
button = ev.button;
button = static_cast<int>(ev.button);
state = kStateDown;
self->repaint();
return true;
@@ -114,7 +115,70 @@ struct ButtonImpl {
}

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_STRUCT(ButtonImpl)
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)
};

// -----------------------------------------------------------------------


+ 210
- 182
dgl/src/Geometry.cpp View File

@@ -27,129 +27,129 @@ static const float M_2PIf = 3.14159265358979323846f*2.0f;

template<typename T>
Point<T>::Point() noexcept
: fX(0),
fY(0) {}
: x(0),
y(0) {}

template<typename T>
Point<T>::Point(const T& x, const T& y) noexcept
: fX(x),
fY(y) {}
Point<T>::Point(const T& x2, const T& y2) noexcept
: x(x2),
y(y2) {}

template<typename T>
Point<T>::Point(const Point<T>& pos) noexcept
: fX(pos.fX),
fY(pos.fY) {}
: x(pos.x),
y(pos.y) {}

template<typename T>
const T& Point<T>::getX() const noexcept
{
return fX;
return x;
}

template<typename T>
const T& Point<T>::getY() const noexcept
{
return fY;
return y;
}

template<typename T>
void Point<T>::setX(const T& x) noexcept
void Point<T>::setX(const T& x2) noexcept
{
fX = x;
x = x2;
}

template<typename T>
void Point<T>::setY(const T& y) noexcept
void Point<T>::setY(const T& y2) noexcept
{
fY = y;
y = y2;
}

template<typename T>
void Point<T>::setPos(const T& x, const T& y) noexcept
void Point<T>::setPos(const T& x2, const T& y2) noexcept
{
fX = x;
fY = y;
x = x2;
y = y2;
}

template<typename T>
void Point<T>::setPos(const Point<T>& pos) noexcept
{
fX = pos.fX;
fY = pos.fY;
x = pos.x;
y = pos.y;
}

template<typename T>
void Point<T>::moveBy(const T& x, const T& y) noexcept
void Point<T>::moveBy(const T& x2, const T& y2) noexcept
{
fX = static_cast<T>(fX+x);
fY = static_cast<T>(fY+y);
x = static_cast<T>(x+x2);
y = static_cast<T>(y+y2);
}

template<typename T>
void Point<T>::moveBy(const Point<T>& pos) noexcept
{
fX = static_cast<T>(fX+pos.fX);
fY = static_cast<T>(fY+pos.fY);
x = static_cast<T>(x+pos.x);
y = static_cast<T>(y+pos.y);
}

template<typename T>
bool Point<T>::isZero() const noexcept
{
return fX == 0 && fY == 0;
return x == 0 && y == 0;
}

template<typename T>
bool Point<T>::isNotZero() const noexcept
{
return fX != 0 || fY != 0;
return x != 0 || y != 0;
}

template<typename T>
Point<T> Point<T>::operator+(const Point<T>& pos) noexcept
{
return Point<T>(fX+pos.fX, fY+pos.fY);
return Point<T>(x+pos.x, y+pos.y);
}

template<typename T>
Point<T> Point<T>::operator-(const Point<T>& pos) noexcept
{
return Point<T>(fX-pos.fX, fY-pos.fY);
return Point<T>(x-pos.x, y-pos.y);
}

template<typename T>
Point<T>& Point<T>::operator=(const Point<T>& pos) noexcept
{
fX = pos.fX;
fY = pos.fY;
x = pos.x;
y = pos.y;
return *this;
}

template<typename T>
Point<T>& Point<T>::operator+=(const Point<T>& pos) noexcept
{
fX = static_cast<T>(fX+pos.fX);
fY = static_cast<T>(fY+pos.fY);
x = static_cast<T>(x+pos.x);
y = static_cast<T>(y+pos.y);
return *this;
}

template<typename T>
Point<T>& Point<T>::operator-=(const Point<T>& pos) noexcept
{
fX = static_cast<T>(fX-pos.fX);
fY = static_cast<T>(fY-pos.fY);
x = static_cast<T>(x-pos.x);
y = static_cast<T>(y-pos.y);
return *this;
}

template<typename T>
bool Point<T>::operator==(const Point<T>& pos) const noexcept
{
return (fX == pos.fX && fY == pos.fY);
return (x == pos.x && y == pos.y);
}

template<typename T>
bool Point<T>::operator!=(const Point<T>& pos) const noexcept
{
return (fX != pos.fX || fY != pos.fY);
return (x != pos.x || y != pos.y);
}

// -----------------------------------------------------------------------
@@ -246,6 +246,27 @@ bool Size<T>::isInvalid() const noexcept
return fWidth <= 0 || fHeight <= 0;
}

template<typename T>
Size<int> Size<T>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth),
static_cast<int>(fHeight));
}

template<>
Size<int> Size<double>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth + 0.5),
static_cast<int>(fHeight + 0.5));
}

template<>
Size<int> Size<float>::toInt() const noexcept
{
return Size<int>(static_cast<int>(fWidth + 0.5f),
static_cast<int>(fHeight + 0.5f));
}

template<typename T>
Size<T> Size<T>::operator+(const Size<T>& size) noexcept
{
@@ -315,162 +336,162 @@ bool Size<T>::operator!=(const Size<T>& size) const noexcept

template<typename T>
Line<T>::Line() noexcept
: fPosStart(0, 0),
fPosEnd(0, 0) {}
: posStart(0, 0),
posEnd(0, 0) {}

template<typename T>
Line<T>::Line(const T& startX, const T& startY, const T& endX, const T& endY) noexcept
: fPosStart(startX, startY),
fPosEnd(endX, endY) {}
: posStart(startX, startY),
posEnd(endX, endY) {}

template<typename T>
Line<T>::Line(const T& startX, const T& startY, const Point<T>& endPos) noexcept
: fPosStart(startX, startY),
fPosEnd(endPos) {}
: posStart(startX, startY),
posEnd(endPos) {}

template<typename T>
Line<T>::Line(const Point<T>& startPos, const T& endX, const T& endY) noexcept
: fPosStart(startPos),
fPosEnd(endX, endY) {}
: posStart(startPos),
posEnd(endX, endY) {}

template<typename T>
Line<T>::Line(const Point<T>& startPos, const Point<T>& endPos) noexcept
: fPosStart(startPos),
fPosEnd(endPos) {}
: posStart(startPos),
posEnd(endPos) {}

template<typename T>
Line<T>::Line(const Line<T>& line) noexcept
: fPosStart(line.fPosStart),
fPosEnd(line.fPosEnd) {}
: posStart(line.posStart),
posEnd(line.posEnd) {}

template<typename T>
const T& Line<T>::getStartX() const noexcept
{
return fPosStart.fX;
return posStart.x;
}

template<typename T>
const T& Line<T>::getStartY() const noexcept
{
return fPosStart.fY;
return posStart.y;
}

template<typename T>
const T& Line<T>::getEndX() const noexcept
{
return fPosEnd.fX;
return posEnd.x;
}

template<typename T>
const T& Line<T>::getEndY() const noexcept
{
return fPosEnd.fY;
return posEnd.y;
}

template<typename T>
const Point<T>& Line<T>::getStartPos() const noexcept
{
return fPosStart;
return posStart;
}

template<typename T>
const Point<T>& Line<T>::getEndPos() const noexcept
{
return fPosEnd;
return posEnd;
}

template<typename T>
void Line<T>::setStartX(const T& x) noexcept
{
fPosStart.fX = x;
posStart.x = x;
}

template<typename T>
void Line<T>::setStartY(const T& y) noexcept
{
fPosStart.fY = y;
posStart.y = y;
}

template<typename T>
void Line<T>::setStartPos(const T& x, const T& y) noexcept
{
fPosStart = Point<T>(x, y);
posStart = Point<T>(x, y);
}

template<typename T>
void Line<T>::setStartPos(const Point<T>& pos) noexcept
{
fPosStart = pos;
posStart = pos;
}

template<typename T>
void Line<T>::setEndX(const T& x) noexcept
{
fPosEnd.fX = x;
posEnd.x = x;
}

template<typename T>
void Line<T>::setEndY(const T& y) noexcept
{
fPosEnd.fY = y;
posEnd.y = y;
}

template<typename T>
void Line<T>::setEndPos(const T& x, const T& y) noexcept
{
fPosEnd = Point<T>(x, y);
posEnd = Point<T>(x, y);
}

template<typename T>
void Line<T>::setEndPos(const Point<T>& pos) noexcept
{
fPosEnd = pos;
posEnd = pos;
}

template<typename T>
void Line<T>::moveBy(const T& x, const T& y) noexcept
{
fPosStart.moveBy(x, y);
fPosEnd.moveBy(x, y);
posStart.moveBy(x, y);
posEnd.moveBy(x, y);
}

template<typename T>
void Line<T>::moveBy(const Point<T>& pos) noexcept
{
fPosStart.moveBy(pos);
fPosEnd.moveBy(pos);
posStart.moveBy(pos);
posEnd.moveBy(pos);
}

template<typename T>
bool Line<T>::isNull() const noexcept
{
return fPosStart == fPosEnd;
return posStart == posEnd;
}

template<typename T>
bool Line<T>::isNotNull() const noexcept
{
return fPosStart != fPosEnd;
return posStart != posEnd;
}

template<typename T>
Line<T>& Line<T>::operator=(const Line<T>& line) noexcept
{
fPosStart = line.fPosStart;
fPosEnd = line.fPosEnd;
posStart = line.posStart;
posEnd = line.posEnd;
return *this;
}

template<typename T>
bool Line<T>::operator==(const Line<T>& line) const noexcept
{
return (fPosStart == line.fPosStart && fPosEnd == line.fPosEnd);
return (posStart == line.posStart && posEnd == line.posEnd);
}

template<typename T>
bool Line<T>::operator!=(const Line<T>& line) const noexcept
{
return (fPosStart != line.fPosStart || fPosEnd != line.fPosEnd);
return (posStart != line.posStart || posEnd != line.posEnd);
}

// -----------------------------------------------------------------------
@@ -524,13 +545,13 @@ Circle<T>::Circle(const Circle<T>& cir) noexcept
template<typename T>
const T& Circle<T>::getX() const noexcept
{
return fPos.fX;
return fPos.x;
}

template<typename T>
const T& Circle<T>::getY() const noexcept
{
return fPos.fY;
return fPos.y;
}

template<typename T>
@@ -542,20 +563,20 @@ const Point<T>& Circle<T>::getPos() const noexcept
template<typename T>
void Circle<T>::setX(const T& x) noexcept
{
fPos.fX = x;
fPos.x = x;
}

template<typename T>
void Circle<T>::setY(const T& y) noexcept
{
fPos.fY = y;
fPos.y = y;
}

template<typename T>
void Circle<T>::setPos(const T& x, const T& y) noexcept
{
fPos.fX = x;
fPos.fY = y;
fPos.x = x;
fPos.y = y;
}

template<typename T>
@@ -599,18 +620,6 @@ void Circle<T>::setNumSegments(const uint num)
fSin = std::sin(fTheta);
}

template<typename T>
void Circle<T>::draw()
{
_draw(false);
}

template<typename T>
void Circle<T>::drawOutline()
{
_draw(true);
}

template<typename T>
Circle<T>& Circle<T>::operator=(const Circle<T>& cir) noexcept
{
@@ -640,83 +649,71 @@ bool Circle<T>::operator!=(const Circle<T>& cir) const noexcept

template<typename T>
Triangle<T>::Triangle() noexcept
: fPos1(0, 0),
fPos2(0, 0),
fPos3(0, 0) {}
: pos1(0, 0),
pos2(0, 0),
pos3(0, 0) {}

template<typename T>
Triangle<T>::Triangle(const T& x1, const T& y1, const T& x2, const T& y2, const T& x3, const T& y3) noexcept
: fPos1(x1, y1),
fPos2(x2, y2),
fPos3(x3, y3) {}
: pos1(x1, y1),
pos2(x2, y2),
pos3(x3, y3) {}

template<typename T>
Triangle<T>::Triangle(const Point<T>& pos1, const Point<T>& pos2, const Point<T>& pos3) noexcept
: fPos1(pos1),
fPos2(pos2),
fPos3(pos3) {}
Triangle<T>::Triangle(const Point<T>& p1, const Point<T>& p2, const Point<T>& p3) noexcept
: pos1(p1),
pos2(p2),
pos3(p3) {}

template<typename T>
Triangle<T>::Triangle(const Triangle<T>& tri) noexcept
: fPos1(tri.fPos1),
fPos2(tri.fPos2),
fPos3(tri.fPos3) {}
: pos1(tri.pos1),
pos2(tri.pos2),
pos3(tri.pos3) {}

template<typename T>
bool Triangle<T>::isNull() const noexcept
{
return fPos1 == fPos2 && fPos1 == fPos3;
return pos1 == pos2 && pos1 == pos3;
}

template<typename T>
bool Triangle<T>::isNotNull() const noexcept
{
return fPos1 != fPos2 || fPos1 != fPos3;
return pos1 != pos2 || pos1 != pos3;
}

template<typename T>
bool Triangle<T>::isValid() const noexcept
{
return fPos1 != fPos2 && fPos1 != fPos3;
return pos1 != pos2 && pos1 != pos3;
}

template<typename T>
bool Triangle<T>::isInvalid() const noexcept
{
return fPos1 == fPos2 || fPos1 == fPos3;
}

template<typename T>
void Triangle<T>::draw()
{
_draw(false);
}

template<typename T>
void Triangle<T>::drawOutline()
{
_draw(true);
return pos1 == pos2 || pos1 == pos3;
}

template<typename T>
Triangle<T>& Triangle<T>::operator=(const Triangle<T>& tri) noexcept
{
fPos1 = tri.fPos1;
fPos2 = tri.fPos2;
fPos3 = tri.fPos3;
pos1 = tri.pos1;
pos2 = tri.pos2;
pos3 = tri.pos3;
return *this;
}

template<typename T>
bool Triangle<T>::operator==(const Triangle<T>& tri) const noexcept
{
return (fPos1 == tri.fPos1 && fPos2 == tri.fPos2 && fPos3 == tri.fPos3);
return (pos1 == tri.pos1 && pos2 == tri.pos2 && pos3 == tri.pos3);
}

template<typename T>
bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept
{
return (fPos1 != tri.fPos1 || fPos2 != tri.fPos2 || fPos3 != tri.fPos3);
return (pos1 != tri.pos1 || pos2 != tri.pos2 || pos3 != tri.pos3);
}

// -----------------------------------------------------------------------
@@ -724,226 +721,257 @@ bool Triangle<T>::operator!=(const Triangle<T>& tri) const noexcept

template<typename T>
Rectangle<T>::Rectangle() noexcept
: fPos(0, 0),
fSize(0, 0) {}
: pos(0, 0),
size(0, 0) {}

template<typename T>
Rectangle<T>::Rectangle(const T& x, const T& y, const T& width, const T& height) noexcept
: fPos(x, y),
fSize(width, height) {}
Rectangle<T>::Rectangle(const T& x, const T& y, const T& w, const T& h) noexcept
: pos(x, y),
size(w, h) {}

template<typename T>
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& size) noexcept
: fPos(x, y),
fSize(size) {}
Rectangle<T>::Rectangle(const T& x, const T& y, const Size<T>& s) noexcept
: pos(x, y),
size(s) {}

template<typename T>
Rectangle<T>::Rectangle(const Point<T>& pos, const T& width, const T& height) noexcept
: fPos(pos),
fSize(width, height) {}
Rectangle<T>::Rectangle(const Point<T>& p, const T& w, const T& h) noexcept
: pos(p),
size(w, h) {}

template<typename T>
Rectangle<T>::Rectangle(const Point<T>& pos, const Size<T>& size) noexcept
: fPos(pos),
fSize(size) {}
Rectangle<T>::Rectangle(const Point<T>& p, const Size<T>& s) noexcept
: pos(p),
size(s) {}

template<typename T>
Rectangle<T>::Rectangle(const Rectangle<T>& rect) noexcept
: fPos(rect.fPos),
fSize(rect.fSize) {}
: pos(rect.pos),
size(rect.size) {}

template<typename T>
const T& Rectangle<T>::getX() const noexcept
{
return fPos.fX;
return pos.x;
}

template<typename T>
const T& Rectangle<T>::getY() const noexcept
{
return fPos.fY;
return pos.y;
}

template<typename T>
const T& Rectangle<T>::getWidth() const noexcept
{
return fSize.fWidth;
return size.fWidth;
}

template<typename T>
const T& Rectangle<T>::getHeight() const noexcept
{
return fSize.fHeight;
return size.fHeight;
}

template<typename T>
const Point<T>& Rectangle<T>::getPos() const noexcept
{
return fPos;
return pos;
}

template<typename T>
const Size<T>& Rectangle<T>::getSize() const noexcept
{
return fSize;
return size;
}

template<typename T>
void Rectangle<T>::setX(const T& x) noexcept
{
fPos.fX = x;
pos.x = x;
}

template<typename T>
void Rectangle<T>::setY(const T& y) noexcept
{
fPos.fY = y;
pos.y = y;
}

template<typename T>
void Rectangle<T>::setPos(const T& x, const T& y) noexcept
{
fPos.fX = x;
fPos.fY = y;
pos.x = x;
pos.y = y;
}

template<typename T>
void Rectangle<T>::setPos(const Point<T>& pos) noexcept
void Rectangle<T>::setPos(const Point<T>& pos2) noexcept
{
fPos = pos;
pos = pos2;
}

template<typename T>
void Rectangle<T>::moveBy(const T& x, const T& y) noexcept
{
fPos.moveBy(x, y);
pos.moveBy(x, y);
}

template<typename T>
void Rectangle<T>::moveBy(const Point<T>& pos) noexcept
void Rectangle<T>::moveBy(const Point<T>& pos2) noexcept
{
fPos.moveBy(pos);
pos.moveBy(pos2);
}

template<typename T>
void Rectangle<T>::setWidth(const T& width) noexcept
{
fSize.fWidth = width;
size.fWidth = width;
}

template<typename T>
void Rectangle<T>::setHeight(const T& height) noexcept
{
fSize.fHeight = height;
size.fHeight = height;
}

template<typename T>
void Rectangle<T>::setSize(const T& width, const T& height) noexcept
{
fSize.fWidth = width;
fSize.fHeight = height;
size.fWidth = width;
size.fHeight = height;
}

template<typename T>
void Rectangle<T>::setSize(const Size<T>& size) noexcept
void Rectangle<T>::setSize(const Size<T>& size2) noexcept
{
fSize = size;
size = size2;
}

template<typename T>
void Rectangle<T>::growBy(double multiplier) noexcept
{
fSize.growBy(multiplier);
size.growBy(multiplier);
}

template<typename T>
void Rectangle<T>::shrinkBy(double divider) noexcept
{
fSize.shrinkBy(divider);
size.shrinkBy(divider);
}

template<typename T>
void Rectangle<T>::setRectangle(const Point<T>& pos, const Size<T>& size) noexcept
void Rectangle<T>::setRectangle(const Point<T>& pos2, const Size<T>& size2) noexcept
{
fPos = pos;
fSize = size;
pos = pos2;
size = size2;
}

template<typename T>
void Rectangle<T>::setRectangle(const Rectangle<T>& rect) noexcept
{
fPos = rect.fPos;
fSize = rect.fSize;
pos = rect.pos;
size = rect.size;
}

template<typename T>
bool Rectangle<T>::contains(const T& x, const T& y) const noexcept
{
return (x >= fPos.fX && y >= fPos.fY && x <= fPos.fX+fSize.fWidth && y <= fPos.fY+fSize.fHeight);
return (x >= pos.x && y >= pos.y && x <= pos.x+size.fWidth && y <= pos.y+size.fHeight);
}

template<typename T>
bool Rectangle<T>::contains(const Point<T>& pos) const noexcept
bool Rectangle<T>::contains(const Point<T>& p) const noexcept
{
return contains(pos.fX, pos.fY);
return contains(p.x, p.y);
}

template<typename T>
template<typename T2>
bool Rectangle<T>::contains(const Point<T2>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
}

template<> template<>
bool Rectangle<int>::contains(const Point<double>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
}

template<> template<>
bool Rectangle<uint>::contains(const Point<double>& p) const noexcept
{
return (p.x >= pos.x && p.y >= pos.y && p.x <= pos.x+size.fWidth && p.y <= pos.y+size.fHeight);
}

template<typename T>
bool Rectangle<T>::containsX(const T& x) const noexcept
{
return (x >= fPos.fX && x <= fPos.fX + fSize.fWidth);
return (x >= pos.x && x <= pos.x + size.fWidth);
}

template<typename T>
bool Rectangle<T>::containsY(const T& y) const noexcept
{
return (y >= fPos.fY && y <= fPos.fY + fSize.fHeight);
return (y >= pos.y && y <= pos.y + size.fHeight);
}

template<typename T>
bool Rectangle<T>::isNull() const noexcept
{
return size.isNull();
}

template<typename T>
bool Rectangle<T>::isNotNull() const noexcept
{
return size.isNotNull();
}

template<typename T>
void Rectangle<T>::draw()
bool Rectangle<T>::isValid() const noexcept
{
_draw(false);
return size.isValid();
}

template<typename T>
void Rectangle<T>::drawOutline()
bool Rectangle<T>::isInvalid() const noexcept
{
_draw(true);
return size.isInvalid();
}

template<typename T>
Rectangle<T>& Rectangle<T>::operator=(const Rectangle<T>& rect) noexcept
{
fPos = rect.fPos;
fSize = rect.fSize;
pos = rect.pos;
size = rect.size;
return *this;
}

template<typename T>
Rectangle<T>& Rectangle<T>::operator*=(double m) noexcept
{
fSize *= m;
size *= m;
return *this;
}

template<typename T>
Rectangle<T>& Rectangle<T>::operator/=(double d) noexcept
{
fSize /= d;
size /= d;
return *this;
}

template<typename T>
bool Rectangle<T>::operator==(const Rectangle<T>& rect) const noexcept
{
return (fPos == rect.fPos && fSize == rect.fSize);
return (pos == rect.pos && size == rect.size);
}

template<typename T>
bool Rectangle<T>::operator!=(const Rectangle<T>& rect) const noexcept
{
return (fPos != rect.fPos || fSize != rect.fSize);
return (pos != rect.pos || size != rect.size);
}

// -----------------------------------------------------------------------


+ 0
- 150
dgl/src/Image.cpp View File

@@ -1,150 +0,0 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Image.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

Image::Image()
: ImageBase(),
fFormat(0),
fType(0),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const Image& image)
: ImageBase(image),
fFormat(image.fFormat),
fType(image.fType),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const uint width, const uint height, const GLenum format, const GLenum type)
: ImageBase(rawData, width, height),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::Image(const char* const rawData, const Size<uint>& size, const GLenum format, const GLenum type)
: ImageBase(rawData, size),
fFormat(format),
fType(type),
fTextureId(0),
fIsReady(false)
{
glGenTextures(1, &fTextureId);
}

Image::~Image()
{
if (fTextureId != 0)
{
#ifndef DISTRHO_OS_MAC // FIXME
glDeleteTextures(1, &fTextureId);
#endif
fTextureId = 0;
}
}

void Image::loadFromMemory(const char* const rawData,
const uint width,
const uint height,
const GLenum format,
const GLenum type) noexcept
{
loadFromMemory(rawData, Size<uint>(width, height), format, type);
}

void Image::loadFromMemory(const char* const rawData,
const Size<uint>& size,
const GLenum format,
const GLenum type) noexcept
{
fRawData = rawData;
fSize = size;
fFormat = format;
fType = type;
fIsReady = false;
}

GLenum Image::getFormat() const noexcept
{
return fFormat;
}

GLenum Image::getType() const noexcept
{
return fType;
}

Image& Image::operator=(const Image& image) noexcept
{
fRawData = image.fRawData;
fSize = image.fSize;
fFormat = image.fFormat;
fType = image.fType;
fIsReady = false;
return *this;
}

void Image::_drawAt(const Point<int>& pos)
{
if (fTextureId == 0 || ! isValid())
return;

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);

if (! fIsReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(fSize.getWidth()), static_cast<GLsizei>(fSize.getHeight()), 0,
fFormat, fType, fRawData);

fIsReady = true;
}

Rectangle<int>(pos, static_cast<int>(fSize.getWidth()), static_cast<int>(fSize.getHeight())).draw();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 56
- 31
dgl/src/ImageBase.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,82 +18,107 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// protected constructors

ImageBase::ImageBase()
: fRawData(nullptr),
fSize(0, 0) {}
: rawData(nullptr),
size(0, 0),
format(kImageFormatNull) {}

ImageBase::ImageBase(const char* const rawData, const uint width, const uint height)
: fRawData(rawData),
fSize(width, height) {}
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt)
: rawData(rdata),
size(width, height),
format(fmt) {}

ImageBase::ImageBase(const char* const rawData, const Size<uint>& size)
: fRawData(rawData),
fSize(size) {}
ImageBase::ImageBase(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: rawData(rdata),
size(s),
format(fmt) {}

ImageBase::ImageBase(const ImageBase& image)
: fRawData(image.fRawData),
fSize(image.fSize) {}
: rawData(image.rawData),
size(image.size),
format(image.format) {}

ImageBase::~ImageBase() {}
// --------------------------------------------------------------------------------------------------------------------
// public methods

// -----------------------------------------------------------------------
ImageBase::~ImageBase() {}

bool ImageBase::isValid() const noexcept
{
return (fRawData != nullptr && fSize.isValid());
return (rawData != nullptr && size.isValid());
}

bool ImageBase::isInvalid() const noexcept
{
return (rawData == nullptr || size.isInvalid());
}

uint ImageBase::getWidth() const noexcept
{
return fSize.getWidth();
return size.getWidth();
}

uint ImageBase::getHeight() const noexcept
{
return fSize.getHeight();
return size.getHeight();
}

const Size<uint>& ImageBase::getSize() const noexcept
{
return fSize;
return size;
}

const char* ImageBase::getRawData() const noexcept
{
return fRawData;
return rawData;
}

// -----------------------------------------------------------------------
ImageFormat ImageBase::getFormat() const noexcept
{
return format;
}

void ImageBase::loadFromMemory(const char* const rdata,
const uint width,
const uint height,
const ImageFormat fmt) noexcept
{
loadFromMemory(rdata, Size<uint>(width, height), fmt);
}

void ImageBase::draw()
void ImageBase::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
_drawAt(Point<int>());
rawData = rdata;
size = s;
format = fmt;
}

void ImageBase::drawAt(const int x, const int y)
void ImageBase::draw(const GraphicsContext& context)
{
_drawAt(Point<int>(x, y));
drawAt(context, Point<int>(0, 0));
}

void ImageBase::drawAt(const Point<int>& pos)
void ImageBase::drawAt(const GraphicsContext& context, const int x, const int y)
{
_drawAt(pos);
drawAt(context, Point<int>(x, y));
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// public operators

ImageBase& ImageBase::operator=(const ImageBase& image) noexcept
{
fRawData = image.fRawData;
fSize = image.fSize;
rawData = image.rawData;
size = image.size;
return *this;
}

bool ImageBase::operator==(const ImageBase& image) const noexcept
{
return (fRawData == image.fRawData && fSize == image.fSize);
return (rawData == image.rawData && size == image.size);
}

bool ImageBase::operator!=(const ImageBase& image) const noexcept
@@ -101,6 +126,6 @@ bool ImageBase::operator!=(const ImageBase& image) const noexcept
return !operator==(image);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 1061
- 0
dgl/src/ImageBaseWidgets.cpp
File diff suppressed because it is too large
View File


+ 0
- 1108
dgl/src/ImageWidgets.cpp
File diff suppressed because it is too large
View File


+ 39
- 68
dgl/src/NanoVG.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,7 +15,7 @@
*/

#include "../NanoVG.hpp"
#include "WidgetPrivateData.hpp"
#include "SubWidgetPrivateData.hpp"

#ifndef DGL_NO_SHARED_RESOURCES
# include "Resources.hpp"
@@ -257,11 +257,6 @@ NanoVG::NanoVG(int flags)
fInFrame(false),
fIsSubWidget(false) {}

NanoVG::NanoVG(NanoWidget* groupWidget)
: fContext(groupWidget->fContext),
fInFrame(false),
fIsSubWidget(true) {}

NanoVG::~NanoVG()
{
DISTRHO_SAFE_ASSERT(! fInFrame);
@@ -277,8 +272,8 @@ void NanoVG::beginFrame(const uint width, const uint height, const float scaleFa
if (fContext == nullptr) return;
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0f,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true;

nvgBeginFrame(fContext, static_cast<int>(width), static_cast<int>(height), scaleFactor);
}

@@ -286,14 +281,13 @@ void NanoVG::beginFrame(Widget* const widget)
{
DISTRHO_SAFE_ASSERT_RETURN(widget != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(! fInFrame,);

fInFrame = true;

if (fContext == nullptr)
return;

Window& window(widget->getParentWindow());
nvgBeginFrame(fContext, static_cast<int>(window.getWidth()), static_cast<int>(window.getHeight()), 1.0f);
if (TopLevelWidget* const tlw = widget->getTopLevelWidget())
nvgBeginFrame(fContext, static_cast<int>(tlw->getWidth()), static_cast<int>(tlw->getHeight()), 1.0f);
}

void NanoVG::cancelFrame()
@@ -789,26 +783,26 @@ void NanoVG::stroke()

NanoVG::FontId NanoVG::createFontFromFile(const char* name, const char* filename)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgCreateFont(fContext, name, filename);
}

NanoVG::FontId NanoVG::createFontFromMemory(const char* name, const uchar* data, uint dataSize, bool freeData)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgCreateFontMem(fContext, name, const_cast<uchar*>(data), static_cast<int>(dataSize), freeData);
}

NanoVG::FontId NanoVG::findFont(const char* name)
{
if (fContext == nullptr) return -1;
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', -1);
DISTRHO_SAFE_ASSERT_RETURN(fContext != nullptr, -1);

return nvgFindFont(fContext, name);
}
@@ -930,79 +924,56 @@ int NanoVG::textBreakLines(const char* string, const char* end, float breakRowWi
}

#ifndef DGL_NO_SHARED_RESOURCES
void NanoVG::loadSharedResources()
bool NanoVG::loadSharedResources()
{
if (fContext == nullptr) return;
if (fContext == nullptr) return false;

if (nvgFindFont(fContext, NANOVG_DEJAVU_SANS_TTF) >= 0)
return;
return true;

using namespace dpf_resources;

nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0;
}
#endif

// -----------------------------------------------------------------------
// NanoSubWidget

struct NanoWidget::PrivateData {
NanoWidget* const self;
std::vector<NanoWidget*> subWidgets;

PrivateData(NanoWidget* const s)
: self(s),
subWidgets() {}

~PrivateData()
{
subWidgets.clear();
}
};

NanoWidget::NanoWidget(Window& parent, int flags)
: Widget(parent),
NanoVG(flags),
nData(new PrivateData(this))
template <>
NanoBaseWidget<SubWidget>::NanoBaseWidget(Widget* const parent, int flags)
: SubWidget(parent),
NanoVG(flags)
{
pData->needsScaling = true;
pData->needsViewportScaling = true;
}

NanoWidget::NanoWidget(Widget* groupWidget, int flags)
: Widget(groupWidget, true),
NanoVG(flags),
nData(new PrivateData(this))
{
pData->needsScaling = true;
}
template class NanoBaseWidget<SubWidget>;

NanoWidget::NanoWidget(NanoWidget* groupWidget)
: Widget(groupWidget, false),
NanoVG(groupWidget),
nData(new PrivateData(this))
{
pData->needsScaling = true;
pData->skipDisplay = true;
groupWidget->nData->subWidgets.push_back(this);
}
// -----------------------------------------------------------------------
// NanoTopLevelWidget

NanoWidget::~NanoWidget()
{
delete nData;
}
template <>
NanoBaseWidget<TopLevelWidget>::NanoBaseWidget(Window& windowToMapTo, int flags)
: TopLevelWidget(windowToMapTo),
NanoVG(flags) {}

void NanoWidget::onDisplay()
{
NanoVG::beginFrame(getWidth(), getHeight());
onNanoDisplay();
template class NanoBaseWidget<TopLevelWidget>;

for (std::vector<NanoWidget*>::iterator it = nData->subWidgets.begin(); it != nData->subWidgets.end(); ++it)
{
NanoWidget* const widget(*it);
widget->onNanoDisplay();
}
// -----------------------------------------------------------------------
// NanoStandaloneWindow

NanoVG::endFrame();
}
template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, int flags)
: StandaloneWindow(app),
NanoVG(flags) {}

template <>
NanoBaseWidget<StandaloneWindow>::NanoBaseWidget(Application& app, Window& parentWindow, int flags)
: StandaloneWindow(app, parentWindow),
NanoVG(flags) {}

template class NanoBaseWidget<StandaloneWindow>;

// -----------------------------------------------------------------------



+ 558
- 58
dgl/src/OpenGL.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,136 +14,255 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Geometry.hpp"
#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
}

// -----------------------------------------------------------------------
// Line

template<typename T>
void Line<T>::draw()
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(fPosStart != fPosEnd,);
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(fPosStart.fX, fPosStart.fY);
glVertex2d(fPosEnd.fX, fPosEnd.fY);
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}

template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
}

// deprecated calls
template<typename T>
void Line<T>::draw()
{
drawLine<T>(posStart, posEnd);
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

template<typename T>
void Circle<T>::_draw(const bool outline)
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(fNumSegments >= 3 && fSize > 0.0f,);
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

double t, x = fSize, y = 0.0;
const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i=0; i<fNumSegments; ++i)
for (uint i=0; i<numSegments; ++i)
{
glVertex2d(x + fPos.fX, y + fPos.fY);
glVertex2d(x + origx, y + origy);

t = x;
x = fCos * x - fSin * y;
y = fSin * t + fCos * y;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

glEnd();
}

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
}

// deprecated calls
template<typename T>
void Circle<T>::draw()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

template<typename T>
void Triangle<T>::_draw(const bool outline)
static void drawTriangle(const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(fPos1 != fPos2 && fPos1 != fPos3,);
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(fPos1.fX, fPos1.fY);
glVertex2d(fPos2.fX, fPos2.fY);
glVertex2d(fPos3.fX, fPos3.fY);
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawTriangle<T>(pos1, pos2, pos3, true);
}

// deprecated calls
template<typename T>
void Triangle<T>::draw()
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline()
{
drawTriangle<T>(pos1, pos2, pos3, true);
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// -----------------------------------------------------------------------
// Rectangle

template<typename T>
void Rectangle<T>::_draw(const bool outline)
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(fSize.isValid(),);
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(fPos.fX, fPos.fY);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(fPos.fX+fSize.fWidth, fPos.fY+fSize.fHeight);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(fPos.fX, fPos.fY+fSize.fHeight);
glVertex2d(x, y+h);
}

glEnd();
}

// -----------------------------------------------------------------------
// Possible template data types

template class Point<double>;
template class Point<float>;
template class Point<int>;
template class Point<uint>;
template class Point<short>;
template class Point<ushort>;

template class Size<double>;
template class Size<float>;
template class Size<int>;
template class Size<uint>;
template class Size<short>;
template class Size<ushort>;
template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
drawRectangle<T>(*this, false);
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;
template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;
glLineWidth(static_cast<GLfloat>(lineWidth));
drawRectangle<T>(*this, true);
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;
// deprecated calls
template<typename T>
void Rectangle<T>::draw()
{
drawRectangle<T>(*this, false);
}

template<typename T>
void Rectangle<T>::drawOutline()
{
drawRectangle<T>(*this, true);
}

template class Rectangle<double>;
template class Rectangle<float>;
@@ -152,6 +271,387 @@ template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

OpenGLImage::OpenGLImage()
: ImageBase(),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const OpenGLImage& image)
: ImageBase(image),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::~OpenGLImage()
{
if (textureId != 0)
glDeleteTextures(1, &textureId);
}

void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
setupCalled = false;
ImageBase::loadFromMemory(rdata, s, fmt);
}

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
setupCalled = false;
return *this;
}

// deprecated calls
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt)
: ImageBase(rdata, s, asDISTRHOImageFormat(fmt)),
textureId(0),
setupCalled(false)
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
glPushMatrix();

const int w2 = w/2;
const int h2 = h/2;

glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);

Rectangle<int>(-w2, -h2, w, h).draw(context);

glPopMatrix();
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<OpenGLImage>;

// -----------------------------------------------------------------------

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
bool needsDisableScissor = false;

if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
{
// full viewport size
glViewport(0,
-static_cast<int>(height * autoScaleFactor - height + 0.5),
static_cast<int>(width * autoScaleFactor + 0.5),
static_cast<int>(height * autoScaleFactor + 0.5));
}
else if (needsViewportScaling)
{
// limit viewport to widget bounds
glViewport(absolutePos.getX(),
static_cast<int>(height - self->getHeight()) - absolutePos.getY(),
static_cast<int>(self->getWidth()),
static_cast<int>(self->getHeight()));
}
else
{
// set viewport pos
glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
-static_cast<int>(std::round((height * autoScaleFactor - height)
+ (absolutePos.getY() * autoScaleFactor))),
static_cast<int>(std::round(width * autoScaleFactor)),
static_cast<int>(std::round(height * autoScaleFactor)));

// then cut the outer bounds
glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY())
* autoScaleFactor)),
static_cast<int>(std::round(self->getWidth() * autoScaleFactor)),
static_cast<int>(std::round(self->getHeight() * autoScaleFactor)));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
}

// display widget
self->onDisplay();

if (needsDisableScissor)
glDisable(GL_SCISSOR_TEST);

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// full viewport size
if (window.pData->autoScaling)
{
glViewport(0,
-static_cast<int>(height * autoScaleFactor - height),
static_cast<int>(width * autoScaleFactor),
static_cast<int>(height * autoScaleFactor));
}
else
{
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
}

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 144
- 0
dgl/src/SubWidget.cpp View File

@@ -0,0 +1,144 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

SubWidget::SubWidget(Widget* const parentWidget)
: Widget(parentWidget),
pData(new PrivateData(this, parentWidget)) {}

SubWidget::~SubWidget()
{
delete pData;
}

template<typename T>
bool SubWidget::contains(T x, T y) const noexcept
{
return Rectangle<double>(0, 0, getWidth(), getHeight()).contains(x, y);
}

template<typename T>
bool SubWidget::contains(const Point<T>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

int SubWidget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int SubWidget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
}

Point<int> SubWidget::getAbsolutePos() const noexcept
{
return pData->absolutePos;
}

Rectangle<int> SubWidget::getAbsoluteArea() const noexcept
{
return Rectangle<int>(getAbsolutePos(), getSize().toInt());
}

Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept
{
return Rectangle<uint>(static_cast<uint>(std::max(0, getAbsoluteX())),
static_cast<uint>(std::max(0, getAbsoluteY())),
getSize());
}

void SubWidget::setAbsoluteX(int x) noexcept
{
setAbsolutePos(Point<int>(x, getAbsoluteY()));
}

void SubWidget::setAbsoluteY(int y) noexcept
{
setAbsolutePos(Point<int>(getAbsoluteX(), y));
}

void SubWidget::setAbsolutePos(int x, int y) noexcept
{
setAbsolutePos(Point<int>(x, y));
}

void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept
{
if (pData->absolutePos == pos)
return;

PositionChangedEvent ev;
ev.oldPos = pData->absolutePos;
ev.pos = pos;

pData->absolutePos = pos;
onPositionChanged(ev);

// repaint the bounds of parent
pData->parentWidget->repaint();
}

Widget* SubWidget::getParentWidget() const noexcept
{
return pData->parentWidget;
}

void SubWidget::repaint() noexcept
{
if (! isVisible())
return;

if (TopLevelWidget* const topw = getTopLevelWidget())
{
if (pData->needsFullViewportForDrawing)
topw->repaint();
else
topw->repaint(getConstrainedAbsoluteArea());
}
}

void SubWidget::setNeedsFullViewportDrawing(const bool needsFullViewportForDrawing)
{
pData->needsFullViewportForDrawing = needsFullViewportForDrawing;
}

void SubWidget::onPositionChanged(const PositionChangedEvent&)
{
}

// --------------------------------------------------------------------------------------------------------------------
// Possible template data types

template<>
bool SubWidget::contains(const Point<double>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
}

// float, int, uint, short, ushort

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 42
- 0
dgl/src/SubWidgetPrivateData.cpp View File

@@ -0,0 +1,42 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SubWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw)
: self(s),
selfw((Widget*)s),
parentWidget(pw),
absolutePos(),
needsFullViewportForDrawing(false),
needsViewportScaling(false)
{
parentWidget->pData->subWidgets.push_back(self);
}

SubWidget::PrivateData::~PrivateData()
{
parentWidget->pData->subWidgets.remove(self);
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 47
- 0
dgl/src/SubWidgetPrivateData.hpp View File

@@ -0,0 +1,47 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../SubWidget.hpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

struct SubWidget::PrivateData {
SubWidget* const self;
Widget* const selfw;
Widget* const parentWidget;
Point<int> absolutePos;
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds
bool needsViewportScaling; // needed for NanoVG

explicit PrivateData(SubWidget* const s, Widget* const pw);
~PrivateData();

// NOTE display function is different depending on build type, must call displaySubWidgets at the end
void display(uint width, uint height, double autoScaleFactor);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_SUBWIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 78
- 0
dgl/src/TopLevelWidget.cpp View File

@@ -0,0 +1,78 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

TopLevelWidget::TopLevelWidget(Window& windowToMapTo)
: Widget(this),
pData(new PrivateData(this, windowToMapTo)) {}

TopLevelWidget::~TopLevelWidget()
{
delete pData;
}

Application& TopLevelWidget::getApp() const noexcept
{
return pData->window.getApp();
}

Window& TopLevelWidget::getWindow() const noexcept
{
return pData->window;
}

bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
{
return pData->window.addIdleCallback(callback, timerFrequencyInMs);
}

bool TopLevelWidget::removeIdleCallback(IdleCallback* const callback)
{
return pData->window.removeIdleCallback(callback);
}

double TopLevelWidget::getScaleFactor() const noexcept
{
return pData->window.getScaleFactor();
}

void TopLevelWidget::repaint() noexcept
{
pData->window.repaint();
}

void TopLevelWidget::repaint(const Rectangle<uint>& rect) noexcept
{
pData->window.repaint(rect);
}

void TopLevelWidget::setGeometryConstraints(const uint minimumWidth,
const uint minimumHeight,
const bool keepAspectRatio,
const bool automaticallyScale)
{
pData->window.setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale);
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 163
- 0
dgl/src/TopLevelWidgetPrivateData.cpp View File

@@ -0,0 +1,163 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"
#include "pugl.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w)
: self(s),
selfw(s),
window(w)
{
window.pData->topLevelWidget = self;
}

TopLevelWidget::PrivateData::~PrivateData()
{
// FIXME?
window.pData->topLevelWidget = nullptr;
}

bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onKeyboard(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveKeyboardEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onSpecial(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveSpecialEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

// give top-level widget chance to catch this event first
if (self->onCharacterInput(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveCharacterInputEventForSubWidgets(ev);
}

bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MouseEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onMouse(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveMouseEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

MotionEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onMotion(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveMotionEventForSubWidgets(rev);
}

bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev)
{
// ignore event if we are not visible
if (! selfw->pData->visible)
return false;

ScrollEvent rev = ev;

if (window.pData->autoScaling)
{
const double autoScaleFactor = window.pData->autoScaleFactor;

rev.pos.setX(ev.pos.getX() / autoScaleFactor);
rev.pos.setY(ev.pos.getY() / autoScaleFactor);
rev.delta.setX(ev.delta.getX() / autoScaleFactor);
rev.delta.setY(ev.delta.getY() / autoScaleFactor);
}

// give top-level widget chance to catch this event first
if (self->onScroll(ev))
return true;

// propagate event to all subwidgets recursively
return selfw->pData->giveScrollEventForSubWidgets(rev);
}

void TopLevelWidget::PrivateData::fallbackOnResize()
{
puglFallbackOnResize(window.pData->view);
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 51
- 0
dgl/src/TopLevelWidgetPrivateData.hpp View File

@@ -0,0 +1,51 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED
#define DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../TopLevelWidget.hpp"

#include <list>

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

struct TopLevelWidget::PrivateData {
TopLevelWidget* const self;
Widget* const selfw;
Window& window;

explicit PrivateData(TopLevelWidget* const s, Window& w);
~PrivateData();
void display();
bool keyboardEvent(const KeyboardEvent& ev);
bool specialEvent(const SpecialEvent& ev);
bool characterInputEvent(const CharacterInputEvent& ev);
bool mouseEvent(const MouseEvent& ev);
bool motionEvent(const MotionEvent& ev);
bool scrollEvent(const ScrollEvent& ev);
void fallbackOnResize();

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_TOP_LEVEL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

+ 243
- 0
dgl/src/Vulkan.cpp View File

@@ -0,0 +1,243 @@
/*
* 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 "../Vulkan.hpp"
#include "../Color.hpp"

#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

static void notImplemented(const char* const name)
{
d_stderr2("vulkan function not implemented: %s", name);
}

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, bool)
{
notImplemented("Color::setFor");
}

// -----------------------------------------------------------------------
// Line

template<typename T>
void Line<T>::draw(const GraphicsContext&, T)
{
notImplemented("Line::draw");
}

template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
notImplemented("Circle::draw");
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Circle::drawOutline");
}

template<typename T>
void Circle<T>::draw()
{
notImplemented("Circle::draw");
}

template<typename T>
void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Triangle::drawOutline");
}

template<typename T>
void Triangle<T>::draw()
{
notImplemented("Triangle::draw");
}

template<typename T>
void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;


// -----------------------------------------------------------------------
// Rectangle

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, T)
{
notImplemented("Rectangle::drawOutline");
}

template<typename T>
void Rectangle<T>::draw()
{
notImplemented("Rectangle::draw");
}

template<typename T>
void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// VulkanImage

VulkanImage::VulkanImage()
: ImageBase() {}

VulkanImage::VulkanImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
: ImageBase(rdata, w, h, fmt) {}

VulkanImage::VulkanImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
: ImageBase(rdata, s, fmt) {}

VulkanImage::VulkanImage(const VulkanImage& image)
: ImageBase(image.rawData, image.size, image.format) {}

VulkanImage::~VulkanImage() {}

void VulkanImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
{
ImageBase::loadFromMemory(rdata, s, fmt);
}

void VulkanImage::drawAt(const GraphicsContext&, const Point<int>&)
{
}

VulkanImage& VulkanImage::operator=(const VulkanImage& image) noexcept
{
rawData = image.rawData;
size = image.size;
format = image.format;
return *this;
}

// -----------------------------------------------------------------------

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
// TODO

selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

void TopLevelWidget::PrivateData::display()
{
if (! selfw->pData->visible)
return;

const Size<uint> size(window.getSize());
const uint width = size.getWidth();
const uint height = size.getHeight();

const double autoScaleFactor = window.pData->autoScaleFactor;

// TODO

// main widget drawing
self->onDisplay();

// now draw subwidgets if there are any
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

// -----------------------------------------------------------------------

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

// -----------------------------------------------------------------------

+ 33
- 86
dgl/src/Widget.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,33 +15,22 @@
*/

#include "WidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"
#include "../Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Widget

Widget::Widget(Window& parent)
: pData(new PrivateData(this, parent, nullptr, false))
{
parent._addWidget(this);
}

Widget::Widget(Widget* groupWidget)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, true))
{
pData->parent._addWidget(this);
}
Widget::Widget(TopLevelWidget* const topLevelWidget)
: pData(new PrivateData(this, topLevelWidget)) {}

Widget::Widget(Widget* groupWidget, bool addToSubWidgets)
: pData(new PrivateData(this, groupWidget->getParentWindow(), groupWidget, addToSubWidgets))
{
pData->parent._addWidget(this);
}
Widget::Widget(Widget* const parentWidget)
: pData(new PrivateData(this, parentWidget)) {}

Widget::~Widget()
{
pData->parent._removeWidget(this);
delete pData;
}

@@ -50,13 +39,13 @@ bool Widget::isVisible() const noexcept
return pData->visible;
}

void Widget::setVisible(bool yesNo)
void Widget::setVisible(bool visible)
{
if (pData->visible == yesNo)
if (pData->visible == visible)
return;

pData->visible = yesNo;
pData->parent.repaint();
pData->visible = visible;
repaint();
}

void Widget::show()
@@ -79,7 +68,7 @@ uint Widget::getHeight() const noexcept
return pData->size.getHeight();
}

const Size<uint>& Widget::getSize() const noexcept
const Size<uint> Widget::getSize() const noexcept
{
return pData->size;
}
@@ -96,7 +85,7 @@ void Widget::setWidth(uint width) noexcept
pData->size.setWidth(width);
onResize(ev);

pData->parent.repaint();
repaint();
}

void Widget::setHeight(uint height) noexcept
@@ -111,7 +100,7 @@ void Widget::setHeight(uint height) noexcept
pData->size.setHeight(height);
onResize(ev);

pData->parent.repaint();
repaint();
}

void Widget::setSize(uint width, uint height) noexcept
@@ -131,77 +120,34 @@ void Widget::setSize(const Size<uint>& size) noexcept
pData->size = size;
onResize(ev);

pData->parent.repaint();
}

int Widget::getAbsoluteX() const noexcept
{
return pData->absolutePos.getX();
}

int Widget::getAbsoluteY() const noexcept
{
return pData->absolutePos.getY();
}

const Point<int>& Widget::getAbsolutePos() const noexcept
{
return pData->absolutePos;
repaint();
}

void Widget::setAbsoluteX(int x) noexcept
Application& Widget::getApp() const noexcept
{
setAbsolutePos(Point<int>(x, getAbsoluteY()));
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getApp();
}

void Widget::setAbsoluteY(int y) noexcept
Window& Widget::getWindow() const noexcept
{
setAbsolutePos(Point<int>(getAbsoluteX(), y));
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow();
}

void Widget::setAbsolutePos(int x, int y) noexcept
const GraphicsContext& Widget::getGraphicsContext() const noexcept
{
setAbsolutePos(Point<int>(x, y));
DISTRHO_SAFE_ASSERT(pData->topLevelWidget != nullptr);
return pData->topLevelWidget->getWindow().getGraphicsContext();
}

void Widget::setAbsolutePos(const Point<int>& pos) noexcept
TopLevelWidget* Widget::getTopLevelWidget() const noexcept
{
if (pData->absolutePos == pos)
return;

PositionChangedEvent ev;
ev.oldPos = pData->absolutePos;
ev.pos = pos;

pData->absolutePos = pos;
onPositionChanged(ev);

pData->parent.repaint();
}

Application& Widget::getParentApp() const noexcept
{
return pData->parent.getApp();
}

Window& Widget::getParentWindow() const noexcept
{
return pData->parent;
}

bool Widget::contains(int x, int y) const noexcept
{
return (x >= 0 && y >= 0 && static_cast<uint>(x) < pData->size.getWidth() && static_cast<uint>(y) < pData->size.getHeight());
}

bool Widget::contains(const Point<int>& pos) const noexcept
{
return contains(pos.getX(), pos.getY());
return pData->topLevelWidget;
}

void Widget::repaint() noexcept
{
pData->parent.repaint();
}

uint Widget::getId() const noexcept
@@ -224,6 +170,11 @@ bool Widget::onSpecial(const SpecialEvent&)
return false;
}

bool Widget::onCharacterInput(const CharacterInputEvent&)
{
return false;
}

bool Widget::onMouse(const MouseEvent&)
{
return false;
@@ -243,10 +194,6 @@ void Widget::onResize(const ResizeEvent&)
{
}

void Widget::onPositionChanged(const PositionChangedEvent&)
{
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

+ 176
- 63
dgl/src/WidgetPrivateData.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,91 +15,204 @@
*/

#include "WidgetPrivateData.hpp"

#ifdef DGL_CAIRO
# include "../Cairo.hpp"
#endif
#ifdef DGL_OPENGL
# include "../OpenGL.hpp"
#endif
#include "SubWidgetPrivateData.hpp"
#include "../TopLevelWidget.hpp"

START_NAMESPACE_DGL

#define FOR_EACH_SUBWIDGET(it) \
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)

#define FOR_EACH_SUBWIDGET_INV(rit) \
for (std::list<SubWidget*>::reverse_iterator rit = subWidgets.rbegin(); rit != subWidgets.rend(); ++rit)

// -----------------------------------------------------------------------

void Widget::PrivateData::display(const uint width,
const uint height,
const double scaling,
const bool renderingSubWidget)
Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw)
: self(s),
topLevelWidget(tlw),
parentWidget(nullptr),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
: self(s),
topLevelWidget(findTopLevelWidget(pw)),
parentWidget(pw),
id(0),
needsScaling(false),
visible(true),
size(0, 0),
subWidgets() {}

Widget::PrivateData::~PrivateData()
{
subWidgets.clear();
}

void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor)
{
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible)
if (subWidgets.size() == 0)
return;

#ifdef DGL_OPENGL
bool needsDisableScissor = false;
for (std::list<SubWidget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
SubWidget* const subwidget(*it);

// reset color
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
if (subwidget->isVisible())
subwidget->pData->display(width, height, autoScaleFactor);
}
}

// -----------------------------------------------------------------------

bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onKeyboard(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveSpecialEventForSubWidgets(const SpecialEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

FOR_EACH_SUBWIDGET_INV(rit)
{
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onSpecial(ev))
return true;
}

return false;
}

bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

if (needsFullViewport || (absolutePos.isZero() && size == Size<uint>(width, height)))
FOR_EACH_SUBWIDGET_INV(rit)
{
// full viewport size
glViewport(0,
-(height * scaling - height),
width * scaling,
height * scaling);
SubWidget* const widget(*rit);

if (widget->isVisible() && widget->onCharacterInput(ev))
return true;
}
else if (needsScaling)

return false;
}

bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

const double x = ev.pos.getX();
const double y = ev.pos.getY();

FOR_EACH_SUBWIDGET_INV(rit)
{
// limit viewport to widget bounds
glViewport(absolutePos.getX(),
height - self->getHeight() - absolutePos.getY(),
self->getWidth(),
self->getHeight());
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onMouse(ev))
return true;
}
else

return false;
}

bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

const double x = ev.pos.getX();
const double y = ev.pos.getY();

FOR_EACH_SUBWIDGET_INV(rit)
{
// only set viewport pos
glViewport(absolutePos.getX() * scaling,
-std::round((height * scaling - height) + (absolutePos.getY() * scaling)),
std::round(width * scaling),
std::round(height * scaling));

// then cut the outer bounds
glScissor(absolutePos.getX() * scaling,
height - std::round((self->getHeight() + absolutePos.getY()) * scaling),
std::round(self->getWidth() * scaling),
std::round(self->getHeight() * scaling));

glEnable(GL_SCISSOR_TEST);
needsDisableScissor = true;
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onMotion(ev))
return true;
}
#endif

#ifdef DGL_CAIRO
cairo_t* cr = parent.getGraphicsContext().cairo;
cairo_matrix_t matrix;
cairo_get_matrix(cr, &matrix);
cairo_translate(cr, absolutePos.getX(), absolutePos.getY());
// TODO: scaling and cropping
#endif
return false;
}

// display widget
self->onDisplay();
bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev)
{
if (! visible)
return false;
if (subWidgets.size() == 0)
return false;

#ifdef DGL_CAIRO
cairo_set_matrix(cr, &matrix);
#endif
const double x = ev.pos.getX();
const double y = ev.pos.getY();

#ifdef DGL_OPENGL
if (needsDisableScissor)
FOR_EACH_SUBWIDGET_INV(rit)
{
glDisable(GL_SCISSOR_TEST);
needsDisableScissor = false;
SubWidget* const widget(*rit);

if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());

if (widget->onScroll(ev))
return true;
}
#endif

displaySubWidgets(width, height, scaling);
return false;
}

// -----------------------------------------------------------------------

TopLevelWidget* Widget::PrivateData::findTopLevelWidget(Widget* const pw)
{
if (pw->pData->topLevelWidget != nullptr)
return pw->pData->topLevelWidget;
if (pw->pData->parentWidget != nullptr)
return findTopLevelWidget(pw->pData->parentWidget);
return nullptr;
}

// -----------------------------------------------------------------------


+ 22
- 47
dgl/src/WidgetPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,69 +18,44 @@
#define DGL_WIDGET_PRIVATE_DATA_HPP_INCLUDED

#include "../Widget.hpp"
#include "../Window.hpp"

#include <vector>
#include <list>

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

struct Widget::PrivateData {
Widget* const self;
Window& parent;
Point<int> absolutePos;
Size<uint> size;
std::vector<Widget*> subWidgets;

TopLevelWidget* const topLevelWidget;
Widget* const parentWidget;
uint id;
bool needsFullViewport;
bool needsScaling;
bool skipDisplay;
bool visible;
Size<uint> size;
std::list<SubWidget*> subWidgets;

PrivateData(Widget* const s, Window& p, Widget* groupWidget, bool addToSubWidgets)
: self(s),
parent(p),
absolutePos(0, 0),
size(0, 0),
subWidgets(),
id(0),
needsFullViewport(false),
needsScaling(false),
skipDisplay(false),
visible(true)
{
if (addToSubWidgets && groupWidget != nullptr)
{
skipDisplay = true;
groupWidget->pData->subWidgets.push_back(self);
}
}

~PrivateData()
{
subWidgets.clear();
}
// called via TopLevelWidget
explicit PrivateData(Widget* const s, TopLevelWidget* const tlw);
// called via SubWidget
explicit PrivateData(Widget* const s, Widget* const pw);
~PrivateData();

// display function is different depending on build type
void display(const uint width, const uint height, const double scaling, const bool renderingSubWidget);
void displaySubWidgets(uint width, uint height, double autoScaleFactor);

void displaySubWidgets(const uint width, const uint height, const double scaling)
{
for (std::vector<Widget*>::iterator it = subWidgets.begin(); it != subWidgets.end(); ++it)
{
Widget* const widget(*it);
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this);
bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev);
bool giveSpecialEventForSubWidgets(const SpecialEvent& ev);
bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev);
bool giveMouseEventForSubWidgets(MouseEvent& ev);
bool giveMotionEventForSubWidgets(MotionEvent& ev);
bool giveScrollEventForSubWidgets(ScrollEvent& ev);

widget->pData->display(width, height, scaling, true);
}
}
static TopLevelWidget* findTopLevelWidget(Widget* const w);

DISTRHO_DECLARE_NON_COPY_STRUCT(PrivateData)
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL



+ 150
- 1664
dgl/src/Window.cpp
File diff suppressed because it is too large
View File


+ 223
- 0
dgl/src/WindowFileBrowser.cpp View File

@@ -0,0 +1,223 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru>
* Copyright (C) 2019 Robin Gareus <robin@gareus.org>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../StandaloneWindow.hpp"

// static int fib_filter_filename_filter(const char* const name)
// {
// return 1;
// (void)name;
// }

// TODO use DGL_NAMESPACE for class names

#ifdef DISTRHO_OS_MAC
@interface FilePanelDelegate : NSObject
{
void (*fCallback)(NSOpenPanel*, int, void*);
void* fUserData;
}
-(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData;
-(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
@end

@implementation FilePanelDelegate
-(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData
{
[super init];
self->fCallback = callback;
self->fUserData = userData;
return self;
}

-(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
self->fCallback(sheet, returnCode, self->fUserData);
(void)contextInfo;
}
@end
#endif

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

bool Window::openFileBrowser(const FileBrowserOptions& options)
{
#if defined(DISTRHO_OS_WINDOWS)
// the old and compatible dialog API
OPENFILENAMEW ofn;
memset(&ofn, 0, sizeof(ofn));

ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = pData->hwnd;

// set initial directory in UTF-16 coding
std::vector<WCHAR> startDirW;
if (options.startDir)
{
startDirW.resize(strlen(options.startDir) + 1);
if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size()))
ofn.lpstrInitialDir = startDirW.data();
}

// set title in UTF-16 coding
std::vector<WCHAR> titleW;
if (options.title)
{
titleW.resize(strlen(options.title) + 1);
if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size()))
ofn.lpstrTitle = titleW.data();
}

// prepare a buffer to receive the result
std::vector<WCHAR> fileNameW(32768); // the Unicode maximum
ofn.lpstrFile = fileNameW.data();
ofn.nMaxFile = (DWORD)fileNameW.size();

// TODO synchronous only, can't do better with WinAPI native dialogs.
// threading might work, if someone is motivated to risk it.
if (GetOpenFileNameW(&ofn))
{
// back to UTF-8
std::vector<char> fileNameA(4 * 32768);
if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr))
{
// handle it during the next idle cycle (fake async)
pData->fSelectedFile = fileNameA.data();
}
}

return true;

#elif defined(DISTRHO_OS_MAC)
if (pData->fOpenFilePanel) // permit one dialog at most
{
[pData->fOpenFilePanel makeKeyAndOrderFront:nil];
return false;
}

NSOpenPanel* panel = [NSOpenPanel openPanel];
pData->fOpenFilePanel = [panel retain];

[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:NO];
[panel setAllowsMultipleSelection:NO];

if (options.startDir)
[panel setDirectory:[NSString stringWithUTF8String:options.startDir]];

if (options.title)
{
NSString* titleString = [[NSString alloc]
initWithBytes:options.title
length:strlen(options.title)
encoding:NSUTF8StringEncoding];
[panel setTitle:titleString];
}

id delegate = pData->fFilePanelDelegate;
if (!delegate)
{
delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd
userData:pData];
pData->fFilePanelDelegate = [delegate retain];
}

[panel beginSheetForDirectory:nullptr
file:nullptr
modalForWindow:nullptr
modalDelegate:delegate
didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
contextInfo:nullptr];

return true;

#elif defined(SOFD_HAVE_X11)
using DISTRHO_NAMESPACE::String;

// --------------------------------------------------------------------------
// configure start dir

// TODO: get abspath if needed
// TODO: cross-platform

String startDir(options.startDir);

# ifdef DISTRHO_OS_LINUX
if (startDir.isEmpty())
{
if (char* const dir_name = get_current_dir_name())
{
startDir = dir_name;
std::free(dir_name);
}
}
# endif

DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);

if (! startDir.endsWith('/'))
startDir += "/";

DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);

// --------------------------------------------------------------------------
// configure title

String title(options.title);

if (title.isEmpty())
{
title = pData->getTitle();

if (title.isEmpty())
title = "FileBrowser";
}

DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);

// --------------------------------------------------------------------------
// configure filters

x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);

// --------------------------------------------------------------------------
// configure buttons

x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
x_fib_cfg_buttons(1, options.buttons.showHidden-1);
x_fib_cfg_buttons(2, options.buttons.showPlaces-1);

// --------------------------------------------------------------------------
// show

return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);

#else
// not implemented
return false;

// unused
(void)options;
#endif
}

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

+ 1090
- 0
dgl/src/WindowPrivateData.cpp
File diff suppressed because it is too large
View File


+ 296
- 0
dgl/src/WindowPrivateData.hpp View File

@@ -0,0 +1,296 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED
#define DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

#include "../Window.hpp"
#include "../Widget.hpp"
#include "ApplicationPrivateData.hpp"

#include "pugl.hpp"

START_NAMESPACE_DGL

class TopLevelWidget;

// -----------------------------------------------------------------------

struct Window::PrivateData : IdleCallback {
/** Reference to the DGL Application class this (private data) window associates with. */
Application& app;

/** Direct access to the DGL Application private data where we registers ourselves in. */
Application::PrivateData* const appData;

/** Pointer to the the DGL Window class that this private data belongs to. */
Window* const self;

/** Pugl view instance. */
PuglView* const view;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];

/** The top-level widget associated with this Window. */
TopLevelWidget* topLevelWidget;

/** Whether this Window is closed (not visible or counted in the Application it is tied to).
Defaults to true unless embed (embed windows are never closed). */
bool isClosed;

/** Whether this Window is currently visible/mapped. Defaults to false. */
bool isVisible;

/** Whether this Window is embed into another (usually not DGL-controlled) Window. */
const bool isEmbed;

/** Scale factor to report to widgets on request, purely informational. */
double scaleFactor;

/** Automatic scaling to apply on widgets, implemented internally. */
bool autoScaling;
double autoScaleFactor;

/** Pugl minWidth, minHeight access. */
uint minWidth, minHeight;

#ifdef DISTRHO_OS_WINDOWS
/** Selected file for openFileBrowser on windows, stored for fake async operation. */
const char* win32SelectedFile;
#endif

/** Modal window setup. */
struct Modal {
PrivateData* parent; // parent of this window (so we can become modal)
PrivateData* child; // child window to give focus to when modal mode is enabled
bool enabled; // wherever modal mode is enabled (only possible if parent != null)

/** Constructor for a non-modal window. */
Modal() noexcept
: parent(nullptr),
child(nullptr),
enabled(false) {}

/** Constructor for a modal window (with a parent). */
Modal(PrivateData* const p) noexcept
: parent(p),
child(nullptr),
enabled(false) {}

/** Destructor. */
~Modal() noexcept
{
DISTRHO_SAFE_ASSERT(! enabled);
}

DISTRHO_DECLARE_NON_COPYABLE(Modal)
DISTRHO_PREVENT_HEAP_ALLOCATION
} modal;

/** Constructor for a regular, standalone window. */
explicit PrivateData(Application& app, Window* self);

/** Constructor for a modal window. */
explicit PrivateData(Application& app, Window* self, PrivateData* ppData);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable);

/** Constructor for an embed Window, with a few extra hints from the host side. */
explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle,
uint width, uint height, double scaling, bool resizable);

/** Destructor. */
~PrivateData() override;

/** Helper initialization function called at the end of all this class constructors. */
void init(uint width, uint height, bool resizable);

/** Hide window and notify application of a window close event.
* Does nothing if window is embed (that is, not standalone).
* The application event-loop will stop when all windows have been closed.
*
* @note It is possible to hide the window while not stopping the event-loop.
* A closed window is always hidden, but the reverse is not always true.
*/
void close();

void show();
void hide();

void focus();

void setResizable(bool resizable);

const GraphicsContext& getGraphicsContext() const noexcept;

// idle callback stuff
void idleCallback() override;
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs);
bool removeIdleCallback(IdleCallback* callback);

// file handling
bool openFileBrowser(const Window::FileBrowserOptions& options);

// modal handling
void startModal();
void stopModal();
void runAsModal(bool blockWait);

// pugl events
void onPuglConfigure(double width, double height);
void onPuglExpose();
void onPuglClose();
void onPuglFocus(bool focus, CrossingMode mode);
void onPuglKey(const Widget::KeyboardEvent& ev);
void onPuglSpecial(const Widget::SpecialEvent& ev);
void onPuglText(const Widget::CharacterInputEvent& ev);
void onPuglMouse(const Widget::MouseEvent& ev);
void onPuglMotion(const Widget::MotionEvent& ev);
void onPuglScroll(const Widget::ScrollEvent& ev);

// Pugl event handling entry point
static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DGL

#if 0
// #if defined(DISTRHO_OS_HAIKU)
// BApplication* bApplication;
// BView* bView;
// BWindow* bWindow;
#if defined(DISTRHO_OS_MAC)
// NSView<PuglGenericView>* mView;
// id mWindow;
// id mParentWindow;
# ifndef DGL_FILE_BROWSER_DISABLED
NSOpenPanel* fOpenFilePanel;
id fFilePanelDelegate;
# endif
#elif defined(DISTRHO_OS_WINDOWS)
// HWND hwnd;
// HWND hwndParent;
# ifndef DGL_FILE_BROWSER_DISABLED
String fSelectedFile;
# endif
#endif
#endif

#if 0
// -----------------------------------------------------------------------
// Window Private

struct Window::PrivateData {
// -------------------------------------------------------------------

bool handlePluginSpecial(const bool press, const Key key)
{
DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);

if (fModal.childFocus != nullptr)
{
fModal.childFocus->focus();
return true;
}

int mods = 0x0;

switch (key)
{
case kKeyShift:
mods |= kModifierShift;
break;
case kKeyControl:
mods |= kModifierControl;
break;
case kKeyAlt:
mods |= kModifierAlt;
break;
default:
break;
}

if (mods != 0x0)
{
if (press)
fView->mods |= mods;
else
fView->mods &= ~(mods);
}

Widget::SpecialEvent ev;
ev.press = press;
ev.key = key;
ev.mod = static_cast<Modifier>(fView->mods);
ev.time = 0;

FOR_EACH_WIDGET_INV(rit)
{
Widget* const widget(*rit);

if (widget->isVisible() && widget->onSpecial(ev))
return true;
}

return false;
}

#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED)
static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData)
{
PrivateData* pData = (PrivateData*)userData;

if (returnCode == NSOKButton)
{
NSArray* urls = [panel URLs];
NSURL* fileUrl = nullptr;

for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i)
{
NSURL* url = (NSURL*)[urls objectAtIndex:i];
if ([url isFileURL])
fileUrl = url;
}

if (fileUrl)
{
PuglView* view = pData->fView;
if (view->fileSelectedFunc)
{
const char* fileName = [fileUrl.path UTF8String];
view->fileSelectedFunc(view, fileName);
}
}
}

[pData->fOpenFilePanel release];
pData->fOpenFilePanel = nullptr;
}
#endif

// -------------------------------------------------------------------

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};
#endif

#endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED

dgl/src/pugl/pugl.h → dgl/src/pugl-custom/pugl.h View File


dgl/src/pugl/pugl_haiku.cpp → dgl/src/pugl-custom/pugl_haiku.cpp View File


dgl/src/pugl/pugl_internal.h → dgl/src/pugl-custom/pugl_internal.h View File


dgl/src/pugl/pugl_osx.m → dgl/src/pugl-custom/pugl_osx.m View File


dgl/src/pugl/pugl_win.cpp → dgl/src/pugl-custom/pugl_win.cpp View File


dgl/src/pugl/pugl_x11.c → dgl/src/pugl-custom/pugl_x11.c View File


+ 29
- 0
dgl/src/pugl-extra/extras.c View File

@@ -0,0 +1,29 @@
/*
Copyright (C) 2012-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file extras.c pugl extra implementations for DPF.
*/

#include "extras.h"

#include "../pugl-upstream/src/implementation.h"

const char*
puglGetWindowTitle(const PuglView* view)
{
return view->title;
}

+ 50
- 0
dgl/src/pugl-extra/extras.h View File

@@ -0,0 +1,50 @@
/*
Copyright (C) 2012-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file pugl.h pugl extra API for DPF.
*/

#ifndef PUGL_EXTRAS_PUGL_H
#define PUGL_EXTRAS_PUGL_H

#include "../pugl-upstream/include/pugl/pugl.h"

PUGL_BEGIN_DECLS

PUGL_API const char*
puglGetWindowTitle(const PuglView* view);

PUGL_API int
puglGetViewHint(const PuglView* view, PuglViewHint hint);

PUGL_API void
puglRaiseWindow(PuglView* view);

PUGL_API void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height);

PUGL_API void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect);

#ifdef DISTRHO_OS_WINDOWS
PUGL_API void
puglWin32SetWindowResizable(PuglView* view, bool resizable);
#endif

PUGL_END_DECLS

#endif // PUGL_EXTRAS_PUGL_H

+ 81
- 0
dgl/src/pugl-extra/haiku.cpp View File

@@ -0,0 +1,81 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file mac.cpp HaikuOS implementation.
*/

#include "haiku.h"

#include "pugl/detail/implementation.h"

PuglStatus
puglGrabFocus(PuglView* view)
{
view->impl->bView->MakeFocus(true);
return PUGL_SUCCESS;
}

// extras follow

void
puglRaiseWindow(PuglView* view)
{
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
bView->ResizeTo(width, height);

if (bWindow != nullptr && bWindow->LockLooper())
{
bWindow->MoveTo(50, 50);
bWindow->ResizeTo(width, height);

if (! forced)
bWindow->Flush();

bWindow->UnlockLooper();
}
// TODO resizable
}

void setVisible(const bool yesNo)
{
if (bWindow != nullptr)
{
if (bWindow->LockLooper())
{
if (yesNo)
bWindow->Show();
else
bWindow->Hide();

// TODO use flush?
bWindow->Sync();
bWindow->UnlockLooper();
}
}
else
{
if (yesNo)
bView->Show();
else
bView->Hide();
}
}

+ 35
- 0
dgl/src/pugl-extra/haiku.h View File

@@ -0,0 +1,35 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file haiku.h Shared definitions for HaikuOS implementation.
*/

#include "pugl/pugl.h"

#include <Application.h>
#include <Window.h>
// using? interface/

struct PuglWorldInternalsImpl {
BApplication* app;
};

struct PuglInternalsImpl {
BViewType* view;
BWindow* window;
};

+ 48
- 0
dgl/src/pugl-extra/mac.m View File

@@ -0,0 +1,48 @@
/*
Copyright (C) 2012-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file mac.m MacOS extra implementation for DPF.
*/

#include "pugl/detail/mac.h"

void
puglRaiseWindow(PuglView* view)
{
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
// NOTE: pugl mac code does nothing with x and y
const PuglRect frame = { 0.0, 0.0, (double)width, (double)height };
puglSetFrame(view, frame);
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio
view->minWidth = width;
view->minHeight = height;

[view->impl->window setContentMinSize:sizePoints(view, width, height)];

if (aspect) {
[view->impl->window setContentAspectRatio:sizePoints(view, width, height)];
}
}

+ 118
- 0
dgl/src/pugl-extra/win.c View File

@@ -0,0 +1,118 @@
/*
Copyright (C) 2012-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file win.c Windows extra implementation for DPF.
*/

#include "pugl/detail/win.h"

#include "pugl/detail/implementation.h"

void
puglRaiseWindow(PuglView* view)
{
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
return PUGL_SUCCESS;
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
view->frame.width = width;
view->frame.height = height;

// NOTE the following code matches upstream pugl, except we add SWP_NOMOVE flag
if (view->impl->hwnd) {
RECT rect = { (long)frame.x,
(long)frame.y,
(long)frame.x + (long)frame.width,
(long)frame.y + (long)frame.height };

AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view),
FALSE,
puglWinGetWindowExFlags(view));

SetWindowPos(view->impl->hwnd,
HWND_TOP,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio, but stilL TODO on pugl
Display* display = view->world->impl->display;

view->minWidth = width;
view->minHeight = height;

if (aspect) {
view->minAspectX = width;
view->minAspectY = height;
view->maxAspectX = width;
view->maxAspectY = height;
}
}

void
puglWin32RestoreWindow(PuglView* view)
{
PuglInternals* impl = view->impl;

ShowWindow(impl->hwnd, SW_RESTORE);
SetFocus(impl->hwnd);
}

void
puglWin32ShowWindowCentered(PuglView* view)
{
PuglInternals* impl = view->impl;

RECT rectChild, rectParent;

if (impl->transientParent != 0 &&
GetWindowRect(impl->hwnd, &rectChild) &&
GetWindowRect(impl->transientParent, &rectParent))
{
SetWindowPos(impl->hwnd, (HWND)impl->transientParent,
rectParent.left + (rectChild.right-rectChild.left)/2,
rectParent.top + (rectChild.bottom-rectChild.top)/2,
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
}
else
{
ShowWindow(hwnd, SW_SHOWNORMAL);
}

SetFocus(impl->hwnd);
}

void
puglWin32SetWindowResizable(PuglView* view, bool resizable)
{
PuglInternals* impl = view->impl;

const int winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
: GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
SetWindowLong(impl->hwnd, GWL_STYLE, winFlags);
}

+ 111
- 0
dgl/src/pugl-extra/x11.c View File

@@ -0,0 +1,111 @@
/*
Copyright (C) 2012-2020 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.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file x11.c X11 extra implementation for DPF.
*/

// NOTE can't import this file twice!
#ifndef PUGL_DETAIL_X11_H_INCLUDED
#include "../pugl-upstream/src/x11.h"
#endif

#include "../pugl-upstream/src/implementation.h"

#include <sys/types.h>
#include <unistd.h>

void
puglRaiseWindow(PuglView* view)
{
XRaiseWindow(view->impl->display, view->impl->win);
}

void
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height)
{
view->frame.width = width;
view->frame.height = height;

if (view->impl->win) {
#if 0
if (! fResizable)
{
XSizeHints sizeHints;
memset(&sizeHints, 0, sizeof(sizeHints));

sizeHints.flags = PSize|PMinSize|PMaxSize;
sizeHints.width = static_cast<int>(width);
sizeHints.height = static_cast<int>(height);
sizeHints.min_width = static_cast<int>(width);
sizeHints.min_height = static_cast<int>(height);
sizeHints.max_width = static_cast<int>(width);
sizeHints.max_height = static_cast<int>(height);

XSetWMNormalHints(xDisplay, xWindow, &sizeHints);
}
#endif

XResizeWindow(view->world->impl->display, view->impl->win, width, height);
}
}

void
puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect)
{
// NOTE this is a combination of puglSetMinSize and puglSetAspectRatio
Display* display = view->world->impl->display;

view->minWidth = width;
view->minHeight = height;

if (aspect) {
view->minAspectX = width;
view->minAspectY = height;
view->maxAspectX = width;
view->maxAspectY = height;
}

#if 0
if (view->impl->win) {
XSizeHints sizeHints = getSizeHints(view);
XSetNormalHints(display, view->impl->win, &sizeHints);
// NOTE old code used this instead
// XSetWMNormalHints(display, view->impl->win, &sizeHints);
}
#endif
}

void
puglExtraSetWindowTypeAndPID(PuglView* view)
{
PuglInternals* const impl = view->impl;

const pid_t pid = getpid();
const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False);
XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);

const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False);

// Setting the window to both dialog and normal will produce a decorated floating dialog
// Order is important: DIALOG needs to come before NORMAL
const Atom _wts[2] = {
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False),
XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False)
};

XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
}

+ 1
- 0
dgl/src/pugl-upstream

@@ -0,0 +1 @@
Subproject commit c5a5eb4cdcb12dc8d74be4f5fbb335e8b3eac711

+ 458
- 0
dgl/src/pugl.cpp View File

@@ -0,0 +1,458 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "pugl.hpp"

/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
# import <Cocoa/Cocoa.h>
# include <dlfcn.h>
# include <mach/mach_time.h>
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-quartz.h>
# endif
# ifdef DGL_OPENGL
# include <OpenGL/gl.h>
# endif
# ifdef DGL_VULKAN
# import <QuartzCore/CAMetalLayer.h>
# include <vulkan/vulkan_core.h>
# include <vulkan/vulkan_macos.h>
# endif
#elif defined(DISTRHO_OS_WINDOWS)
# include <wctype.h>
# include <windows.h>
# include <windowsx.h>
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-win32.h>
# endif
# ifdef DGL_OPENGL
# include <GL/gl.h>
# endif
# ifdef DGL_VULKAN
# include <vulkan/vulkan.h>
# include <vulkan/vulkan_win32.h>
# endif
#else
# include <dlfcn.h>
# include <sys/select.h>
# include <sys/time.h>
# include <X11/X.h>
# include <X11/Xatom.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/keysym.h>
# ifdef HAVE_XCURSOR
# include <X11/Xcursor/Xcursor.h>
# include <X11/cursorfont.h>
# endif
# ifdef HAVE_XRANDR
# include <X11/extensions/Xrandr.h>
# endif
# ifdef HAVE_XSYNC
# include <X11/extensions/sync.h>
# include <X11/extensions/syncconst.h>
# endif
# ifdef DGL_CAIRO
# include <cairo.h>
# include <cairo-xlib.h>
# endif
# ifdef DGL_OPENGL
# include <GL/gl.h>
# include <GL/glx.h>
# endif
# ifdef DGL_VULKAN
# include <vulkan/vulkan_core.h>
# include <vulkan/vulkan_xlib.h>
# endif
#endif

#ifdef HAVE_X11
# define DBLCLKTME 400
# include "sofd/libsofd.h"
# include "sofd/libsofd.c"
#endif

#ifndef DISTRHO_OS_MAC
START_NAMESPACE_DGL
#endif

// --------------------------------------------------------------------------------------------------------------------

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
# ifndef DISTRHO_MACOS_NAMESPACE_MACRO
# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE
# define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE)
# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView)
# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView)
# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow)
# endif
# import "pugl-upstream/src/mac.m"
# import "pugl-upstream/src/mac_stub.m"
# ifdef DGL_CAIRO
# import "pugl-upstream/src/mac_cairo.m"
# endif
# ifdef DGL_OPENGL
# import "pugl-upstream/src/mac_gl.m"
# endif
# ifdef DGL_VULKAN
# import "pugl-upstream/src/mac_vulkan.m"
# endif
#elif defined(DISTRHO_OS_WINDOWS)
# include "pugl-upstream/src/win.c"
# include "pugl-upstream/src/win_stub.c"
# ifdef DGL_CAIRO
# include "pugl-upstream/src/win_cairo.c"
# endif
# ifdef DGL_OPENGL
# include "pugl-upstream/src/win_gl.c"
# endif
# ifdef DGL_VULKAN
# include "pugl-upstream/src/win_vulkan.c"
# endif
#else
# include "pugl-upstream/src/x11.c"
# include "pugl-upstream/src/x11_stub.c"
# ifdef DGL_CAIRO
# include "pugl-upstream/src/x11_cairo.c"
# endif
# ifdef DGL_OPENGL
# include "pugl-upstream/src/x11_gl.c"
# endif
# ifdef DGL_VULKAN
# include "pugl-upstream/src/x11_vulkan.c"
# endif
#endif

#include "pugl-upstream/src/implementation.c"

// --------------------------------------------------------------------------------------------------------------------
// expose backend enter

void puglBackendEnter(PuglView* view)
{
view->backend->enter(view, NULL);
}

// --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns title char* pointer

const char* puglGetWindowTitle(const PuglView* view)
{
return view->title;
}

// --------------------------------------------------------------------------------------------------------------------
// bring view window into the foreground, aka "raise" window

void puglRaiseWindow(PuglView* view)
{
#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC)
// nothing here yet
#elif defined(DISTRHO_OS_WINDOWS)
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
#else
XRaiseWindow(view->impl->display, view->impl->win);
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// set backend that matches current build

void puglSetMatchingBackendForCurrentBuild(PuglView* const view)
{
#ifdef DGL_CAIRO
puglSetBackend(view, puglCairoBackend());
#endif
#ifdef DGL_OPENGL
puglSetBackend(view, puglGlBackend());
#endif
#ifdef DGL_VULKAN
puglSetBackend(view, puglVulkanBackend());
#endif
if (view->backend == nullptr)
puglSetBackend(view, puglStubBackend());
}

// --------------------------------------------------------------------------------------------------------------------
// Combine puglSetMinSize and puglSetAspectRatio

PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect)
{
view->minWidth = (int)width;
view->minHeight = (int)height;

if (aspect) {
view->minAspectX = (int)width;
view->minAspectY = (int)height;
view->maxAspectX = (int)width;
view->maxAspectY = (int)height;
}

#if defined(DISTRHO_OS_HAIKU)
// nothing?
#elif defined(DISTRHO_OS_MAC)
/*
if (view->impl->window)
{
[view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)];

if (aspect)
[view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)];
}
*/
puglSetMinSize(view, width, height);
puglSetAspectRatio(view, width, height, width, height);
#elif defined(DISTRHO_OS_WINDOWS)
// nothing
#else
return updateSizeHints(view);
#endif

return PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// set window size without changing frame x/y position

PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height)
{
#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC)
// keep upstream behaviour
const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height };
return puglSetFrame(view, frame);
#elif defined(DISTRHO_OS_WINDOWS)
// matches upstream pugl, except we add SWP_NOMOVE flag
if (view->impl->hwnd)
{
const PuglRect frame = view->frame;

RECT rect = { (long)frame.x,
(long)frame.y,
(long)frame.x + (long)frame.width,
(long)frame.y + (long)frame.height };

AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view));

if (! SetWindowPos(view->impl->hwnd,
HWND_TOP,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER))
return PUGL_UNKNOWN_ERROR;
}
#else
// matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow
if (view->impl->win)
{
if (! XResizeWindow(view->world->impl->display, view->impl->win, width, height))
return PUGL_UNKNOWN_ERROR;
}
#endif

view->frame.width = width;
view->frame.height = height;
return PUGL_SUCCESS;
}

// --------------------------------------------------------------------------------------------------------------------
// DGL specific, build-specific drawing prepare

void puglOnDisplayPrepare(PuglView*)
{
#ifdef DGL_OPENGL
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// DGL specific, build-specific fallback resize

void puglFallbackOnResize(PuglView* const view)
{
#ifdef DGL_OPENGL
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(view->frame.width), static_cast<GLdouble>(view->frame.height), 0.0, 0.0, 1.0);
glViewport(0, 0, static_cast<GLsizei>(view->frame.width), static_cast<GLsizei>(view->frame.height));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#endif
}

#ifdef DISTRHO_OS_WINDOWS
// --------------------------------------------------------------------------------------------------------------------
// win32 specific, call ShowWindow with SW_RESTORE

void puglWin32RestoreWindow(PuglView* const view)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

ShowWindow(impl->hwnd, SW_RESTORE);
SetFocus(impl->hwnd);
}

// --------------------------------------------------------------------------------------------------------------------
// win32 specific, center view based on parent coordinates (if there is one)

void puglWin32ShowWindowCentered(PuglView* const view)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

RECT rectChild, rectParent;

if (view->transientParent != 0 &&
GetWindowRect(impl->hwnd, &rectChild) &&
GetWindowRect((HWND)view->transientParent, &rectParent))
{
SetWindowPos(impl->hwnd, (HWND)view->transientParent,
rectParent.left + (rectChild.right-rectChild.left)/2,
rectParent.top + (rectChild.bottom-rectChild.top)/2,
0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
}
else
{
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
}

SetFocus(impl->hwnd);
}

// --------------------------------------------------------------------------------------------------------------------
// win32 specific, set or unset WS_SIZEBOX style flag

void puglWin32SetWindowResizable(PuglView* const view, const bool resizable)
{
PuglInternals* impl = view->impl;
DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,);

const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX
: GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX;
SetWindowLong(impl->hwnd, GWL_STYLE, winFlags);
}

// --------------------------------------------------------------------------------------------------------------------
#endif

#ifdef HAVE_X11
// --------------------------------------------------------------------------------------------------------------------
// X11 specific, setup event loop filter for sofd file dialog

static bool sofd_has_action;
static char* sofd_filename;

static bool sofd_event_filter(Display* const display, XEvent* const xevent)
{
if (x_fib_handle_events(display, xevent) == 0)
return false;

if (sofd_filename != nullptr)
std::free(sofd_filename);

if (x_fib_status() > 0)
sofd_filename = x_fib_filename();
else
sofd_filename = nullptr;

x_fib_close(display);
sofd_has_action = true;
return true;
}

void sofdFileDialogSetup(PuglWorld* const world)
{
puglX11SetEventFilter(world, sofd_event_filter);
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, show file dialog via sofd

bool sofdFileDialogShow(PuglView* const view,
const char* const startDir, const char* const title,
const uint flags, const uint width, const uint height)
{
DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);

/*
x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
x_fib_cfg_buttons(1, options.buttons.showHidden-1);
x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
*/

PuglInternals* const impl = view->impl;
return (x_fib_show(impl->display, impl->win, width, height) == 0);
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, close sofd file dialog

void sofdFileDialogClose(PuglView* const view)
{
PuglInternals* const impl = view->impl;
x_fib_close(impl->display);
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, get path chosen via sofd file dialog

bool sofdFileDialogGetPath(char** path)
{
if (! sofd_has_action)
return false;

sofd_has_action = false;
*path = sofd_filename;
return true;
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, free path of sofd file dialog, no longer needed

void sofdFileDialogFree(char* const path)
{
DISTRHO_SAFE_ASSERT_RETURN(path == nullptr || path == sofd_filename,);

std::free(sofd_filename);
sofd_filename = nullptr;
}

// --------------------------------------------------------------------------------------------------------------------
#endif

// --------------------------------------------------------------------------------------------------------------------

#ifndef DISTRHO_OS_MAC
END_NAMESPACE_DGL
#endif

+ 121
- 0
dgl/src/pugl.hpp View File

@@ -0,0 +1,121 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_PUGL_HPP_INCLUDED
#define DGL_PUGL_HPP_INCLUDED

#include "../Base.hpp"

/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */
#include <cstdbool>
#include <cstddef>
#include <cstdint>

#define PUGL_API
#define PUGL_DISABLE_DEPRECATED
#define PUGL_NO_INCLUDE_GLU_H

// --------------------------------------------------------------------------------------------------------------------

#ifndef DISTRHO_OS_MAC
START_NAMESPACE_DGL
#else
USE_NAMESPACE_DGL
#endif

#include "pugl-upstream/include/pugl/pugl.h"

// --------------------------------------------------------------------------------------------------------------------

PUGL_BEGIN_DECLS

// expose backend enter
PUGL_API void
puglBackendEnter(PuglView* view);

// missing in pugl, directly returns title char* pointer
PUGL_API const char*
puglGetWindowTitle(const PuglView* view);

// bring view window into the foreground, aka "raise" window
PUGL_API void
puglRaiseWindow(PuglView* view);

// DGL specific, assigns backend that matches current DGL build
PUGL_API void
puglSetMatchingBackendForCurrentBuild(PuglView* view);

// Combine puglSetMinSize and puglSetAspectRatio
PUGL_API PuglStatus
puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect);

// set window size without changing frame x/y position
PUGL_API PuglStatus
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height);

// DGL specific, build-specific drawing prepare
PUGL_API void
puglOnDisplayPrepare(PuglView* view);

// DGL specific, build-specific fallback resize
PUGL_API void
puglFallbackOnResize(PuglView* view);

#ifdef DISTRHO_OS_WINDOWS
// win32 specific, call ShowWindow with SW_RESTORE
PUGL_API void
puglWin32RestoreWindow(PuglView* view);

// win32 specific, center view based on parent coordinates (if there is one)
PUGL_API void
puglWin32ShowWindowCentered(PuglView* view);

// win32 specific, set or unset WS_SIZEBOX style flag
PUGL_API void
puglWin32SetWindowResizable(PuglView* view, bool resizable);
#endif

#ifdef HAVE_X11
// X11 specific, setup event loop filter for sofd file dialog
PUGL_API void
sofdFileDialogSetup(PuglWorld* world);

// X11 specific, show file dialog via sofd
PUGL_API bool
sofdFileDialogShow(PuglView* view, const char* startDir, const char* title, uint flags, uint width, uint height);

// X11 specific, close sofd file dialog
PUGL_API void
sofdFileDialogClose(PuglView* view);

// X11 specific, get path chosen via sofd file dialog
PUGL_API bool
sofdFileDialogGetPath(char** path);

// X11 specific, free path of sofd file dialog, no longer needed
PUGL_API void
sofdFileDialogFree(char* path);
#endif

PUGL_END_DECLS

// --------------------------------------------------------------------------------------------------------------------

#ifndef DISTRHO_OS_MAC
END_NAMESPACE_DGL
#endif

#endif // DGL_PUGL_HPP_INCLUDED

+ 1
- 0
dgl/src/pugl.mm View File

@@ -0,0 +1 @@
pugl.cpp

+ 20
- 15
dgl/src/sofd/libsofd.c View File

@@ -32,7 +32,7 @@
*/

#ifdef SOFD_TEST
#define SOFD_HAVE_X11
#define HAVE_X11
#include "libsofd.h"
#endif

@@ -337,7 +337,7 @@ const char *x_fib_recent_file(const char *appname) {
return NULL;
}

#ifdef SOFD_HAVE_X11
#ifdef HAVE_X11
#include <mntent.h>
#include <dirent.h>

@@ -475,7 +475,10 @@ static int (*_fib_filter_function)(const char *filename);
#define FAREATEXTL (FAREAMRGL + TEXTSEP) //px; filename text-left FAREAMRGL + TEXTSEP
#define SORTBTNOFF -10 //px;

#define DBLCLKTME 400 //msec; double click time
#ifndef DBLCLKTME
#define DBLCLKTME 200 //msec; double click time
#endif

#define DRAW_OUTLINE
#define DOUBLE_BUFFER

@@ -1208,7 +1211,7 @@ static int fib_dirlistadd (Display *dpy, const int i, const char* path, const ch

static int fib_openrecent (Display *dpy, const char *sel) {
int i;
unsigned int j;
unsigned int j;
assert (_recentcnt > 0);
fib_pre_opendir (dpy);
query_font_geometry (dpy, _fib_gc, "Last Used", &_fib_font_time_width, NULL, NULL, NULL);
@@ -1332,8 +1335,8 @@ static int fib_open (Display *dpy, int item) {
static void cb_cancel (Display *dpy) {
_status = -1;

// unused
return; (void)dpy;
// unused
return; (void)dpy;
}

static void cb_open (Display *dpy) {
@@ -1478,8 +1481,8 @@ static int fib_widget_at_pos (Display *dpy, int x, int y, int *it) {

return 0;

// unused
(void)dpy;
// unused
(void)dpy;
}

static void fib_update_hover (Display *dpy, int need_expose, const int type, const int item) {
@@ -1592,9 +1595,11 @@ static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long ti
fib_select (dpy, it);
_dblclk = time;
}
/*if (_fsel >= 0) {
/*
if (_fsel >= 0) {
if (!(_dirlist[_fsel].flags & 4));
}*/
}
*/
}
break;
case 1: // paths
@@ -1656,8 +1661,8 @@ static void fib_mousedown (Display *dpy, int x, int y, int btn, unsigned long ti
static void fib_mouseup (Display *dpy, int x, int y, int btn, unsigned long time) {
_scrl_my = -1;

// unused
return; (void)dpy; (void)x; (void)y; (void)btn; (void)time;
// unused
return; (void)dpy; (void)x; (void)y; (void)btn; (void)time;
}

static void add_place_raw (Display *dpy, const char *name, const char *path) {
@@ -1885,8 +1890,8 @@ static int x_error_handler (Display *d, XErrorEvent *e) {
font_err = 1;
return 0;

// unused
(void)d; (void)e;
// unused
(void)d; (void)e;
}

int x_fib_show (Display *dpy, Window parent, int x, int y) {
@@ -2340,7 +2345,7 @@ char *x_fib_filename () {
else
return NULL;
}
#endif // SOFD_HAVE_X11
#endif // HAVE_X11

#if defined(__clang__)
# pragma clang diagnostic pop


+ 21
- 2
dgl/src/sofd/libsofd.h View File

@@ -22,10 +22,15 @@
*/

#ifndef LIBSOFD_H
#define LIBSOFD_H
#define LIBSOFD_H 1

#ifdef HAVE_X11
#include <X11/Xlib.h>

#ifdef __cplusplus
extern "C" {
#endif

///////////////////////////////////////////////////////////////////////////////
/* public API */

@@ -110,6 +115,16 @@ int x_fib_cfg_buttons (int k, int v);
*/
int x_fib_cfg_filter_callback (int (*cb)(const char*));

#ifdef __cplusplus
}
#endif

#endif /* END X11 specific functions */

#ifdef __cplusplus
extern "C" {
#endif

/* 'recently used' API. x-platform
* NOTE: all functions use a static cache and are not reentrant.
* It is expected that none of these functions are called in
@@ -172,4 +187,8 @@ unsigned int x_fib_recent_count ();
*/
const char *x_fib_recent_at (unsigned int i);

#endif // LIBSOFD_H
#ifdef __cplusplus
}
#endif

#endif // header guard

+ 22
- 0
distrho/DistrhoInfo.hpp View File

@@ -563,6 +563,28 @@ START_NAMESPACE_DISTRHO
*/
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1

/**
Wherever the %UI uses a custom toolkit implementation based on OpenGL.@n
When enabled, the macros @ref DISTRHO_UI_CUSTOM_INCLUDE_PATH and @ref DISTRHO_UI_CUSTOM_WIDGET_TYPE are required.
*/
#define DISTRHO_UI_USE_CUSTOM 1

/**
The include path to the header file used by the custom toolkit implementation.
This path must be relative to dpf/distrho/DistrhoUI.hpp
@see DISTRHO_UI_USE_CUSTOM
*/
#define DISTRHO_UI_CUSTOM_INCLUDE_PATH

/**
The top-level-widget typedef to use for the custom toolkit.
This widget class MUST be a subclass of DGL TopLevelWindow class.
It is recommended that you keep this widget class inside the DGL namespace,
and define widget type as e.g. DGL_NAMESPACE::MyCustomTopLevelWidget.
@see DISTRHO_UI_USE_CUSTOM
*/
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE

/**
Wherever the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n
When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget.


+ 1
- 1
distrho/DistrhoPlugin.hpp View File

@@ -369,7 +369,7 @@ struct ParameterEnumerationValues {
}
}

DISTRHO_DECLARE_NON_COPY_STRUCT(ParameterEnumerationValues)
DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues)
};

/**


+ 36
- 30
distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -20,23 +20,32 @@
#include "extra/LeakDetector.hpp"
#include "src/DistrhoPluginChecks.h"

#ifdef DGL_CAIRO
# include "Cairo.hpp"
#endif
#ifdef DGL_OPENGL
# include "OpenGL.hpp"
#endif
#ifdef DGL_VULKAN
# include "Vulkan.hpp"
#endif

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../dgl/Base.hpp"
# include "extra/ExternalWindow.hpp"
typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget;
#elif DISTRHO_UI_USE_CUSTOM
# include DISTRHO_UI_CUSTOM_INCLUDE_PATH
typedef DISTRHO_UI_CUSTOM_WIDGET_TYPE UIWidget;
#elif DISTRHO_UI_USE_CAIRO
# include "../dgl/Cairo.hpp"
typedef DGL_NAMESPACE::CairoTopLevelWidget UIWidget;
#elif DISTRHO_UI_USE_NANOVG
# include "../dgl/NanoVG.hpp"
typedef DGL_NAMESPACE::NanoWidget UIWidget;
typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget;
#else
# include "../dgl/Widget.hpp"
typedef DGL_NAMESPACE::Widget UIWidget;
#endif

#ifdef DGL_CAIRO
# include "Cairo.hpp"
#endif
#ifdef DGL_OPENGL
# include "OpenGL.hpp"
# include "../dgl/TopLevelWidget.hpp"
typedef DGL_NAMESPACE::TopLevelWidget UIWidget;
#endif

START_NAMESPACE_DISTRHO
@@ -69,27 +78,32 @@ public:
*/
virtual ~UI();

#if DISTRHO_UI_USER_RESIZABLE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/**
Set geometry constraints for the UI when resized by the user, and optionally scale UI automatically.
@see Window::setGeometryConstraints(uint,uint,bool)
@see Window::setScaling(double)
*/
void setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale = false);
#endif

/* --------------------------------------------------------------------------------------------------------
* Host state */

/**
Get the color used for UI background (i.e. window color) in RGBA format.
Returns 0 by default, in case of error or lack of host support.

The following example code can be use to extract individual colors:
```
const int red = (bgColor >> 24) & 0xff;
const int green = (bgColor >> 16) & 0xff;
const int blue = (bgColor >> 8) & 0xff;
```
*/
uint getBackgroundColor() const noexcept;

/**
Get the color used for UI foreground (i.e. text color) in RGBA format.
Returns 0xffffffff by default, in case of error or lack of host support.

The following example code can be use to extract individual colors:
```
const int red = (fgColor >> 24) & 0xff;
const int green = (fgColor >> 16) & 0xff;
const int blue = (fgColor >> 8) & 0xff;
```
*/
uint getForegroundColor() const noexcept;

@@ -232,7 +246,7 @@ protected:
# ifndef DGL_FILE_BROWSER_DISABLED
/**
File browser selected function.
@see Window::fileBrowserSelected(const char*)
@see Window::onFileSelected(const char*)
*/
virtual void uiFileBrowserSelected(const char* filename);
# endif
@@ -259,18 +273,10 @@ protected:

private:
struct PrivateData;
PrivateData* const pData;
PrivateData* const uiData;
friend class UIExporter;
friend class UIExporterWindow;

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}
void setAbsolutePos(int, int) const noexcept {}
void setAbsolutePos(const DGL_NAMESPACE::Point<int>&) const noexcept {}
#endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI)
};



+ 33
- 0
distrho/DistrhoUI_macOS.mm View File

@@ -0,0 +1,33 @@
/*
* 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 PUGL_NAMESPACE
# error PUGL_NAMESPACE must be set when compiling this file
#endif

#include "src/DistrhoPluginChecks.h"
#include "../dgl/Base.hpp"

#define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE
#define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE)

#define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView)
#define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView)
#define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window)
#define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate)
#define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView)

#import "src/pugl.mm"

+ 14
- 1
distrho/DistrhoUtils.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -50,6 +50,9 @@ inline float round(float __x)
# define M_PI 3.14159265358979323846
#endif

#define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO
#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO)

// -----------------------------------------------------------------------
// misc functions

@@ -196,6 +199,16 @@ void d_safe_assert_uint2(const char* const assertion, const char* const file,
d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %u, v2 %u", assertion, file, line, v1, v2);
}

/*
* Print a safe assertion error message, with a custom error message.
*/
static inline
void d_custom_safe_assert(const char* const message, const char* const assertion, const char* const file,
const int line) noexcept
{
d_stderr2("assertion failure: %s, condition \"%s\" in file %s, line %i", message, assertion, file, line);
}

/*
* Print a safe exception error message.
*/


+ 1
- 1
distrho/extra/ExternalWindow.hpp View File

@@ -192,7 +192,7 @@ private:

friend class UIExporter;

DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow)
DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow)
};

// -----------------------------------------------------------------------


+ 2
- 2
distrho/extra/LeakDetector.hpp View File

@@ -54,13 +54,13 @@ START_NAMESPACE_DISTRHO
DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName> DISTRHO_JOIN_MACRO(leakDetector_, ClassName);

# define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
DISTRHO_LEAK_DETECTOR(ClassName)
#else
/** Don't use leak detection on release builds. */
# define DISTRHO_LEAK_DETECTOR(ClassName)
# define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
DISTRHO_DECLARE_NON_COPY_CLASS(ClassName)
DISTRHO_DECLARE_NON_COPYABLE(ClassName)
#endif

//==============================================================================


+ 17
- 14
distrho/extra/Mutex.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -39,7 +39,7 @@ public:
/*
* Constructor.
*/
Mutex(bool inheritPriority = true) noexcept
Mutex(const bool inheritPriority = true) noexcept
: fMutex()
{
pthread_mutexattr_t attr;
@@ -61,9 +61,9 @@ public:
/*
* Lock the mutex.
*/
void lock() const noexcept
bool lock() const noexcept
{
pthread_mutex_lock(&fMutex);
return (pthread_mutex_lock(&fMutex) == 0);
}

/*
@@ -86,8 +86,7 @@ public:
private:
mutable pthread_mutex_t fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(Mutex)
DISTRHO_DECLARE_NON_COPYABLE(Mutex)
};

// -----------------------------------------------------------------------
@@ -133,12 +132,13 @@ public:
/*
* Lock the mutex.
*/
void lock() const noexcept
bool lock() const noexcept
{
#ifdef DISTRHO_OS_WINDOWS
EnterCriticalSection(&fSection);
return true;
#else
pthread_mutex_lock(&fMutex);
return (pthread_mutex_lock(&fMutex) == 0);
#endif
}

@@ -174,8 +174,7 @@ private:
mutable pthread_mutex_t fMutex;
#endif

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(RecursiveMutex)
DISTRHO_DECLARE_NON_COPYABLE(RecursiveMutex)
};

// -----------------------------------------------------------------------
@@ -256,7 +255,7 @@ private:
volatile bool fTriggered;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(Signal)
DISTRHO_DECLARE_NON_COPYABLE(Signal)
};

// -----------------------------------------------------------------------
@@ -281,7 +280,7 @@ private:
const Mutex& fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeLocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeLocker)
};

// -----------------------------------------------------------------------
@@ -295,6 +294,10 @@ public:
: fMutex(mutex),
fLocked(mutex.tryLock()) {}

ScopeTryLocker(const Mutex& mutex, const bool forceLock) noexcept
: fMutex(mutex),
fLocked(forceLock ? mutex.lock() : mutex.tryLock()) {}

~ScopeTryLocker() noexcept
{
if (fLocked)
@@ -316,7 +319,7 @@ private:
const bool fLocked;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeTryLocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeTryLocker)
};

// -----------------------------------------------------------------------
@@ -341,7 +344,7 @@ private:
const Mutex& fMutex;

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPY_CLASS(ScopeUnlocker)
DISTRHO_DECLARE_NON_COPYABLE(ScopeUnlocker)
};

// -----------------------------------------------------------------------


+ 136
- 0
distrho/extra/ScopedSafeLocale.hpp View File

@@ -0,0 +1,136 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED
#define DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED

#include "../DistrhoUtils.hpp"

#include <clocale>

#if ! (defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
# define DISTRHO_USE_NEWLOCALE
#endif

#if defined(DISTRHO_OS_WINDOWS) && __MINGW64_VERSION_MAJOR >= 5
# define DISTRHO_USE_CONFIGTHREADLOCALE
#endif

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// ScopedSafeLocale class definition

/**
ScopedSafeLocale is a handy class for setting current locale to C on constructor, and revert back on destructor.
It tries to be thread-safe, but it is not always possible.

Put it inside a scope of code where string conversions happen to ensure they are consistent across many systems.
For example:

```
// stack buffer to put converted float value in
char strbuf[0xff];

{
// safe locale operations during this scope
const ScopedSafeLocale sl;
snprintf(strbuf, 0xff, "%f", value);
}

// do something with `strbuf` now, locale is reverted and left just as it was before
```
*/
class ScopedSafeLocale {
public:
/*
* Constructor.
* Current system locale will saved, while "C" is set as the next one to use.
*/
inline ScopedSafeLocale() noexcept;

/*
* Destructor.
* System locale will revert back to the one saved during constructor.
*/
inline ~ScopedSafeLocale() noexcept;

private:
#ifdef DISTRHO_USE_NEWLOCALE
locale_t newloc, oldloc;
#else
# ifdef DISTRHO_USE_CONFIGTHREADLOCALE
const int oldthreadloc;
# endif
char* const oldloc;
#endif

DISTRHO_DECLARE_NON_COPYABLE(ScopedSafeLocale)
DISTRHO_PREVENT_HEAP_ALLOCATION
};

// -----------------------------------------------------------------------
// ScopedSafeLocale class implementation

#ifdef DISTRHO_USE_NEWLOCALE
static constexpr const locale_t kNullLocale = (locale_t)nullptr;
#endif

inline ScopedSafeLocale::ScopedSafeLocale() noexcept
#ifdef DISTRHO_USE_NEWLOCALE
: newloc(::newlocale(LC_NUMERIC_MASK, "C", kNullLocale)),
oldloc(newloc != kNullLocale ? ::uselocale(newloc) : kNullLocale) {}
#else
# ifdef DISTRHO_USE_CONFIGTHREADLOCALE
: oldthreadloc(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)),
# else
:
# endif
oldloc(strdup(::setlocale(LC_NUMERIC, nullptr)))
{
::setlocale(LC_NUMERIC, "C");
}
#endif

inline ScopedSafeLocale::~ScopedSafeLocale() noexcept
{
#ifdef DISTRHO_USE_NEWLOCALE
if (oldloc != kNullLocale)
::uselocale(oldloc);
if (newloc != kNullLocale)
::freelocale(newloc);
#else // DISTRHO_USE_NEWLOCALE
if (oldloc != nullptr)
{
::setlocale(LC_NUMERIC, oldloc);
std::free(oldloc);
}

# ifdef DISTRHO_USE_CONFIGTHREADLOCALE
if (oldthreadloc != -1)
_configthreadlocale(oldthreadloc);
# endif
#endif // DISTRHO_USE_NEWLOCALE
}

// -----------------------------------------------------------------------

#undef DISTRHO_USE_CONFIGTHREADLOCALE
#undef DISTRHO_USE_NEWLOCALE

END_NAMESPACE_DISTRHO

#endif // DISTRHO_SCOPED_SAFE_LOCALE_HPP_INCLUDED

+ 158
- 65
distrho/extra/String.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,7 @@
#define DISTRHO_STRING_HPP_INCLUDED

#include "../DistrhoUtils.hpp"
#include "../extra/ScopedSafeLocale.hpp"

#include <algorithm>

@@ -37,14 +38,16 @@ public:
*/
explicit String() noexcept
: fBuffer(_null()),
fBufferLen(0) {}
fBufferLen(0),
fBufferAlloc(false) {}

/*
* Simple character.
*/
explicit String(const char c) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char ch[2];
ch[0] = c;
@@ -58,7 +61,8 @@ public:
*/
explicit String(char* const strBuf, const bool copyData = true) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
if (copyData || strBuf == nullptr)
{
@@ -66,10 +70,10 @@ public:
}
else
{
fBuffer = strBuf;
fBufferLen = std::strlen(strBuf);
fBuffer = strBuf;
fBufferLen = std::strlen(strBuf);
fBufferAlloc = true;
}

}

/*
@@ -77,7 +81,8 @@ public:
*/
explicit String(const char* const strBuf) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
_dup(strBuf);
}
@@ -87,7 +92,8 @@ public:
*/
explicit String(const int value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%d", value);
@@ -101,7 +107,8 @@ public:
*/
explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
@@ -115,7 +122,8 @@ public:
*/
explicit String(const long value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%ld", value);
@@ -129,7 +137,8 @@ public:
*/
explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
@@ -143,7 +152,8 @@ public:
*/
explicit String(const long long value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%lld", value);
@@ -157,7 +167,8 @@ public:
*/
explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
@@ -171,10 +182,16 @@ public:
*/
explicit String(const float value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));

{
const ScopedSafeLocale ssl;
std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
}

strBuf[0xff] = '\0';

_dup(strBuf);
@@ -185,10 +202,16 @@ public:
*/
explicit String(const double value) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
char strBuf[0xff+1];
std::snprintf(strBuf, 0xff, "%.24g", value);

{
const ScopedSafeLocale ssl;
std::snprintf(strBuf, 0xff, "%.24g", value);
}

strBuf[0xff] = '\0';

_dup(strBuf);
@@ -202,7 +225,8 @@ public:
*/
String(const String& str) noexcept
: fBuffer(_null()),
fBufferLen(0)
fBufferLen(0),
fBufferAlloc(false)
{
_dup(str.fBuffer);
}
@@ -217,13 +241,12 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);

if (fBuffer == _null())
return;

std::free(fBuffer);
if (fBufferAlloc)
std::free(fBuffer);

fBuffer = nullptr;
fBufferLen = 0;
fBuffer = nullptr;
fBufferLen = 0;
fBufferAlloc = false;
}

// -------------------------------------------------------------------
@@ -253,6 +276,20 @@ public:
return (fBufferLen != 0);
}

/*
* Check if the string contains a specific character, case-sensitive.
*/
bool contains(const char c) const noexcept
{
for (std::size_t i=0; i<fBufferLen; ++i)
{
if (fBuffer[i] == c)
return true;
}

return false;
}

/*
* Check if the string contains another string, optionally ignoring case.
*/
@@ -388,7 +425,7 @@ public:
if (ret < 0)
{
// should never happen!
d_safe_assert("ret >= 0", __FILE__, __LINE__);
d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));

if (found != nullptr)
*found = false;
@@ -490,6 +527,29 @@ public:
return *this;
}

/*
* Remove all occurrences of character 'c', shifting and truncating the string as necessary.
*/
String& remove(const char c) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this);

if (fBufferLen == 0)
return *this;

for (std::size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
--fBufferLen;
std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i);
}
}

fBuffer[fBufferLen] = '\0';
return *this;
}

/*
* Truncate the string to size 'n'.
*/
@@ -498,9 +558,7 @@ public:
if (n >= fBufferLen)
return *this;

for (std::size_t i=n; i < fBufferLen; ++i)
fBuffer[i] = '\0';

fBuffer[n] = '\0';
fBufferLen = n;

return *this;
@@ -529,7 +587,7 @@ public:
}

/*
* Convert to all ascii characters to lowercase.
* Convert all ascii characters to lowercase.
*/
String& toLower() noexcept
{
@@ -545,7 +603,7 @@ public:
}

/*
* Convert to all ascii characters to uppercase.
* Convert all ascii characters to uppercase.
*/
String& toUpper() noexcept
{
@@ -570,13 +628,15 @@ public:

/*
* Get and release the string buffer, while also clearing this string.
* This allows to keep a pointer to the buffer after this object is deleted.
* Result must be freed.
*/
char* getAndReleaseBuffer() noexcept
{
char* const ret = fBuffer;
char* ret = fBufferLen > 0 ? fBuffer : nullptr;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
return ret;
}

@@ -591,7 +651,7 @@ public:
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);

const uchar* bytesToEncode((const uchar*)data);

@@ -723,16 +783,26 @@ public:

String& operator+=(const char* const strBuf) noexcept
{
if (strBuf == nullptr)
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;

const std::size_t strBufLen = std::strlen(strBuf);

// for empty strings, we can just take the appended string as our entire data
if (isEmpty())
{
_dup(strBuf, strBufLen);
return *this;
}

const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
char newBuf[newBufSize];
// we have some data ourselves, reallocate to add the new stuff
char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);

std::strcpy(newBuf, fBuffer);
std::strcat(newBuf, strBuf);
std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

_dup(newBuf, newBufSize-1);
fBuffer = newBuf;
fBufferLen += strBufLen;

return *this;
}
@@ -744,13 +814,18 @@ public:

String operator+(const char* const strBuf) noexcept
{
const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
char newBuf[newBufSize];
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;
if (isEmpty())
return String(strBuf);

std::strcpy(newBuf, fBuffer);
const std::size_t strBufLen = std::strlen(strBuf);
const std::size_t newBufSize = fBufferLen + strBufLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

if (strBuf != nullptr)
std::strcat(newBuf, strBuf);
std::memcpy(newBuf, fBuffer, fBufferLen);
std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

return String(newBuf);
}
@@ -763,8 +838,9 @@ public:
// -------------------------------------------------------------------

private:
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
bool fBufferAlloc; // wherever the buffer is allocated, not using _null()

/*
* Static null string.
@@ -792,7 +868,7 @@ private:
if (std::strcmp(fBuffer, strBuf) == 0)
return;

if (fBuffer != _null())
if (fBufferAlloc)
std::free(fBuffer);

fBufferLen = (size > 0) ? size : std::strlen(strBuf);
@@ -800,28 +876,31 @@ private:

if (fBuffer == nullptr)
{
fBuffer = _null();
fBufferLen = 0;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
return;
}

std::strcpy(fBuffer, strBuf);
fBufferAlloc = true;

std::strcpy(fBuffer, strBuf);
fBuffer[fBufferLen] = '\0';
}
else
{
DISTRHO_SAFE_ASSERT(size == 0);
DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));

// don't recreate null string
if (fBuffer == _null())
if (! fBufferAlloc)
return;

DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
std::free(fBuffer);

fBuffer = _null();
fBufferLen = 0;
fBuffer = _null();
fBufferLen = 0;
fBufferAlloc = false;
}
}

@@ -833,12 +912,19 @@ private:
static inline
String operator+(const String& strBefore, const char* const strBufAfter) noexcept
{
const char* const strBufBefore = strBefore.buffer();
const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
char newBuf[newBufSize];
if (strBufAfter == nullptr || strBufAfter[0] == '\0')
return strBefore;
if (strBefore.isEmpty())
return String(strBufAfter);

const std::size_t strBeforeLen = strBefore.length();
const std::size_t strBufAfterLen = std::strlen(strBufAfter);
const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

std::strcpy(newBuf, strBufBefore);
std::strcat(newBuf, strBufAfter);
std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);

return String(newBuf);
}
@@ -846,12 +932,19 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep
static inline
String operator+(const char* const strBufBefore, const String& strAfter) noexcept
{
const char* const strBufAfter = strAfter.buffer();
const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
char newBuf[newBufSize];

std::strcpy(newBuf, strBufBefore);
std::strcat(newBuf, strBufAfter);
if (strAfter.isEmpty())
return String(strBufBefore);
if (strBufBefore == nullptr || strBufBefore[0] == '\0')
return strAfter;

const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
const std::size_t strAfterLen = strAfter.length();
const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
char* const newBuf = (char*)malloc(newBufSize + 1);
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);

return String(newBuf);
}


+ 65
- 22
distrho/extra/Thread.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -88,33 +88,70 @@ public:
/*
* Start the thread.
*/
bool startThread() noexcept
bool startThread(const bool withRealtimePriority = false) noexcept
{
// check if already running
DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);

pthread_t handle;

pthread_attr_t attr;
pthread_attr_init(&attr);

struct sched_param sched_param;
std::memset(&sched_param, 0, sizeof(sched_param));

if (withRealtimePriority)
{
sched_param.sched_priority = 80;

#ifndef DISTRHO_OS_HAIKU
if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 &&
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 &&
# ifndef DISTRHO_OS_WINDOWS
(pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 ||
pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) &&
# endif
pthread_attr_setschedparam(&attr, &sched_param) == 0)
{
d_stdout("Thread setup with realtime priority successful");
}
else
#endif
{
d_stdout("Thread setup with realtime priority failed, going with normal priority instead");
pthread_attr_destroy(&attr);
pthread_attr_init(&attr);
}
}

const MutexLocker ml(fLock);

fShouldExit = false;

pthread_t handle;
bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
pthread_attr_destroy(&attr);

if (pthread_create(&handle, nullptr, _entryPoint, this) == 0)
{
if (withRealtimePriority && !ok)
{
d_stdout("Thread with realtime priority failed on creation, going with normal priority instead");
pthread_attr_init(&attr);
ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
pthread_attr_destroy(&attr);
}

DISTRHO_SAFE_ASSERT_RETURN(ok, false);
#ifdef PTW32_DLLPORT
DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
#else
DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
#endif
pthread_detach(handle);
_copyFrom(handle);
pthread_detach(handle);
_copyFrom(handle);

// wait for thread to start
fSignal.wait();
return true;
}

return false;
// wait for thread to start
fSignal.wait();
return true;
}

/*
@@ -161,10 +198,7 @@ public:
_copyTo(threadId);
_init();

try {
pthread_cancel(threadId);
} DISTRHO_SAFE_EXCEPTION("pthread_cancel");

pthread_detach(threadId);
return false;
}
}
@@ -191,6 +225,14 @@ public:
return fName;
}

/*
* Returns the Id/handle of the thread.
*/
pthread_t getThreadId() const noexcept
{
return fHandle;
}

/*
* Changes the name of the caller thread.
*/
@@ -201,7 +243,7 @@ public:
#ifdef DISTRHO_OS_LINUX
prctl(PR_SET_NAME, name, 0, 0, 0);
#endif
#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD)
pthread_setname_np(pthread_self(), name);
#endif
}
@@ -259,7 +301,8 @@ private:
*/
void _runEntryPoint() noexcept
{
setCurrentThreadName(fName);
if (fName.isNotEmpty())
setCurrentThreadName(fName);

// report ready
fSignal.signal();
@@ -281,7 +324,7 @@ private:
return nullptr;
}

DISTRHO_DECLARE_NON_COPY_CLASS(Thread)
DISTRHO_DECLARE_NON_COPYABLE(Thread)
};

// -----------------------------------------------------------------------


+ 57
- 29
distrho/src/DistrhoDefines.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -39,6 +39,10 @@
# define DISTRHO_OS_HAIKU 1
# elif defined(__linux__) || defined(__linux)
# define DISTRHO_OS_LINUX 1
# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# define DISTRHO_OS_BSD 1
# elif defined(__GNU__)
# define DISTRHO_OS_GNU_HURD 1
# endif
#endif

@@ -75,12 +79,35 @@
# define DISTRHO_DEPRECATED
#endif

/* Define DISTRHO_DEPRECATED_BY */
#if defined(__clang__)
# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("", other)))
#elif defined(__GNUC__)
# define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("Use " other)))
#else
# define DISTRHO_DEPRECATED_BY(other) DISTRHO_DEPRECATED
#endif

/* Define DISTRHO_SAFE_ASSERT* */
#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert(#cond, __FILE__, __LINE__);
#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert (#cond, __FILE__, __LINE__);
#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (! (cond)) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast<int>(value));
#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (! (cond)) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast<int>(v1), static_cast<int>(v2));
#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (! (cond)) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast<uint>(value));
#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (! (cond)) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast<uint>(v1), static_cast<uint>(v2));

#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; }
#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; }
#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; }

#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (! (cond)) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__);
#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; }

#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } break; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; }
#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; }

#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value); break; }
#define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); continue; }
#define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast<int>(value)); return ret; }
@@ -103,39 +130,23 @@
#define DISTRHO_SAFE_EXCEPTION_CONTINUE(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); continue; }
#define DISTRHO_SAFE_EXCEPTION_RETURN(msg, ret) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); return ret; }

/* Define DISTRHO_DECLARE_NON_COPY_CLASS */
/* Define DISTRHO_DECLARE_NON_COPYABLE */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
private: \
ClassName(ClassName&) = delete; \
ClassName(const ClassName&) = delete; \
ClassName& operator=(ClassName&) = delete ; \
# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
private: \
ClassName(ClassName&) = delete; \
ClassName(const ClassName&) = delete; \
ClassName& operator=(ClassName&) = delete; \
ClassName& operator=(const ClassName&) = delete;
#else
# define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \
private: \
ClassName(ClassName&); \
ClassName(const ClassName&); \
ClassName& operator=(ClassName&); \
# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
private: \
ClassName(ClassName&); \
ClassName(const ClassName&); \
ClassName& operator=(ClassName&); \
ClassName& operator=(const ClassName&);
#endif

/* Define DISTRHO_DECLARE_NON_COPY_STRUCT */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \
StructName(StructName&) = delete; \
StructName(const StructName&) = delete; \
StructName& operator=(StructName&) = delete; \
StructName& operator=(const StructName&) = delete;
#else
# define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \
private: \
StructName(StructName&); \
StructName(const StructName&); \
StructName& operator=(StructName&); \
StructName& operator=(const StructName&);
#endif

/* Define DISTRHO_PREVENT_HEAP_ALLOCATION */
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
# define DISTRHO_PREVENT_HEAP_ALLOCATION \
@@ -157,10 +168,27 @@ private: \
#define END_NAMESPACE_DISTRHO }
#define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE;

/* Define DISTRHO_OS_SEP and DISTRHO_OS_SPLIT */
#ifdef DISTRHO_OS_WINDOWS
# define DISTRHO_OS_SEP '\\'
# define DISTRHO_OS_SEP_STR "\\"
# define DISTRHO_OS_SPLIT ';'
# define DISTRHO_OS_SPLIT_STR ";"
#else
# define DISTRHO_OS_SEP '/'
# define DISTRHO_OS_SEP_STR "/"
# define DISTRHO_OS_SPLIT ':'
# define DISTRHO_OS_SPLIT_STR ":"
#endif

/* Useful typedefs */
typedef unsigned char uchar;
typedef unsigned short int ushort;
typedef unsigned int uint;
typedef unsigned long int ulong;

/* Deprecated macros */
#define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName)
#define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) DISTRHO_DECLARE_NON_COPYABLE(StructName)

#endif // DISTRHO_DEFINES_H_INCLUDED

+ 14
- 5
distrho/src/DistrhoPluginChecks.h View File

@@ -142,11 +142,12 @@
// -----------------------------------------------------------------------
// Enable full state if plugin exports presets

#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE
# warning Plugins with programs and state need to implement full state API
# undef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 1
#endif
// FIXME
// #if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE
// # warning Plugins with programs and state need to implement full state API
// # undef DISTRHO_PLUGIN_WANT_FULL_STATE
// # define DISTRHO_PLUGIN_WANT_FULL_STATE 1
// #endif

// -----------------------------------------------------------------------
// Disable UI if DGL or External UI is not available
@@ -161,6 +162,14 @@
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

// -----------------------------------------------------------------------
// Prevent users from messing about with DPF internals

#ifdef DISTRHO_UI_IS_STANDALONE
# error DISTRHO_UI_IS_STANDALONE must not be defined
#endif


// -----------------------------------------------------------------------

#endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED

+ 1
- 1
distrho/src/DistrhoPluginInternal.hpp View File

@@ -113,7 +113,7 @@ struct Plugin::PrivateData {
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
parameterOffset += 1;


+ 2
- 11
distrho/src/DistrhoPluginJack.cpp View File

@@ -17,6 +17,7 @@
#include "DistrhoPluginInternal.hpp"

#if DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_UI_IS_STANDALONE true
# include "DistrhoUIInternal.hpp"
#else
# include "../extra/Sleep.hpp"
@@ -81,16 +82,6 @@ static void initSignalHandler()

// -----------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_UI
// TODO
static double getDesktopScaleFactor() noexcept
{
return 1.0;
}
#endif

// -----------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_UI
class PluginJack : public IdleCallback
#else
@@ -110,7 +101,7 @@ public:
nullptr, // file request
nullptr, // bundle
fPlugin.getInstancePointer(),
getDesktopScaleFactor()),
0.0),
#endif
fClient(client)
{


+ 122
- 94
distrho/src/DistrhoPluginVST.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,6 +15,7 @@
*/

#include "DistrhoPluginInternal.hpp"
#include "../extra/ScopedSafeLocale.hpp"

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
# undef DISTRHO_PLUGIN_HAS_UI
@@ -22,6 +23,9 @@
#endif

#if DISTRHO_PLUGIN_HAS_UI
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
# define DISTRHO_UI_IS_STANDALONE 0
# include "DistrhoUIInternal.hpp"
#endif

@@ -29,14 +33,16 @@
# define __cdecl
#endif

#define VESTIGE_HEADER
#ifndef VESTIGE_HEADER
# define VESTIGE_HEADER 1
#endif
#define VST_FORCE_DEPRECATED 0

#include <clocale>
#include <map>
#include <string>

#ifdef VESTIGE_HEADER
#if VESTIGE_HEADER
# include "vestige/vestige.h"
#define effFlagsProgramChunks (1 << 5)
#define effSetProgramName 4
@@ -101,35 +107,11 @@ void snprintf_iparam(char* const dst, const int32_t value, const size_t size)

// -----------------------------------------------------------------------

class ScopedSafeLocale {
public:
ScopedSafeLocale() noexcept
: locale(::strdup(::setlocale(LC_NUMERIC, nullptr)))
{
::setlocale(LC_NUMERIC, "C");
}

~ScopedSafeLocale() noexcept
{
if (locale != nullptr)
{
::setlocale(LC_NUMERIC, locale);
std::free(locale);
}
}

private:
char* const locale;

DISTRHO_DECLARE_NON_COPY_CLASS(ScopedSafeLocale)
DISTRHO_PREVENT_HEAP_ALLOCATION
};

// -----------------------------------------------------------------------

class ParameterCheckHelper
struct ParameterCheckHelper
{
public:
bool* parameterChecks;
float* parameterValues;

ParameterCheckHelper()
: parameterChecks(nullptr),
parameterValues(nullptr) {}
@@ -148,9 +130,6 @@ public:
}
}

bool* parameterChecks;
float* parameterValues;

#if DISTRHO_PLUGIN_WANT_STATE
virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0;
#endif
@@ -181,19 +160,8 @@ public:
nullptr,
plugin->getInstancePointer(),
scaleFactor),
fShouldCaptureVstKeys(false)
fKeyboardModifiers(0)
{
// FIXME only needed for windows?
//#ifdef DISTRHO_OS_WINDOWS
char strBuf[0xff+1];
std::memset(strBuf, 0, sizeof(char)*(0xff+1));
hostCallback(audioMasterGetProductString, 0, 0, strBuf);
d_stdout("Plugin UI running in '%s'", strBuf);

// TODO make a white-list of needed hosts
if (/*std::strcmp(strBuf, "") == 0*/ true)
fShouldCaptureVstKeys = true;
//#endif
}

// -------------------------------------------------------------------
@@ -237,12 +205,9 @@ public:
}
# endif

# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value)
{
# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (! fShouldCaptureVstKeys)
return 0;

d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value);

using namespace DGL_NAMESPACE;
@@ -250,62 +215,127 @@ public:
int special = 0;
switch (value)
{
// convert some specials to normal keys
case 1: index = kCharBackspace; break;
case 6: index = kCharEscape; break;
case 7: index = ' '; break;
case 22: index = kCharDelete; break;
// convert some VST special values to normal keys
case 1: index = kKeyBackspace; break;
case 2: index = '\t'; break;
// 3 clear
case 4: index = '\r'; break;
case 6: index = kKeyEscape; break;
case 7: index = ' '; break;
// 8 next
// 17 select
// 18 print
case 19: index = '\n'; break;
// 20 snapshot
case 22: index = kKeyDelete; break;
// 23 help
case 57: index = '='; break;

// numpad stuff follows
case 24: index = '0'; break;
case 25: index = '1'; break;
case 26: index = '2'; break;
case 27: index = '3'; break;
case 28: index = '4'; break;
case 29: index = '5'; break;
case 30: index = '6'; break;
case 31: index = '7'; break;
case 32: index = '8'; break;
case 33: index = '9'; break;
case 34: index = '*'; break;
case 35: index = '+'; break;
// 36 separator
case 37: index = '-'; break;
case 38: index = '.'; break;
case 39: index = '/'; break;

// handle rest of special keys
case 40: special = kKeyF1; break;
case 41: special = kKeyF2; break;
case 42: special = kKeyF3; break;
case 43: special = kKeyF4; break;
case 44: special = kKeyF5; break;
case 45: special = kKeyF6; break;
case 46: special = kKeyF7; break;
case 47: special = kKeyF8; break;
case 48: special = kKeyF9; break;
case 49: special = kKeyF10; break;
case 50: special = kKeyF11; break;
case 51: special = kKeyF12; break;
case 11: special = kKeyLeft; break;
case 12: special = kKeyUp; break;
case 13: special = kKeyRight; break;
case 14: special = kKeyDown; break;
case 15: special = kKeyPageUp; break;
case 16: special = kKeyPageDown; break;
case 10: special = kKeyHome; break;
case 9: special = kKeyEnd; break;
case 21: special = kKeyInsert; break;
case 54: special = kKeyShift; break;
case 55: special = kKeyControl; break;
case 56: special = kKeyAlt; break;
/* these special keys are missing:
- kKeySuper
- kKeyCapsLock
- kKeyPrintScreen
*/
case 40: special = kKeyF1; break;
case 41: special = kKeyF2; break;
case 42: special = kKeyF3; break;
case 43: special = kKeyF4; break;
case 44: special = kKeyF5; break;
case 45: special = kKeyF6; break;
case 46: special = kKeyF7; break;
case 47: special = kKeyF8; break;
case 48: special = kKeyF9; break;
case 49: special = kKeyF10; break;
case 50: special = kKeyF11; break;
case 51: special = kKeyF12; break;
case 11: special = kKeyLeft; break;
case 12: special = kKeyUp; break;
case 13: special = kKeyRight; break;
case 14: special = kKeyDown; break;
case 15: special = kKeyPageUp; break;
case 16: special = kKeyPageDown; break;
case 10: special = kKeyHome; break;
case 9: special = kKeyEnd; break;
case 21: special = kKeyInsert; break;
case 54: special = kKeyShift; break;
case 55: special = kKeyControl; break;
case 56: special = kKeyAlt; break;
case 58: special = kKeyMenu; break;
case 52: special = kKeyNumLock; break;
case 53: special = kKeyScrollLock; break;
case 5: special = kKeyPause; break;
}

switch (special)
{
case kKeyShift:
if (down)
fKeyboardModifiers |= kModifierShift;
else
fKeyboardModifiers &= ~kModifierShift;
break;
case kKeyControl:
if (down)
fKeyboardModifiers |= kModifierControl;
else
fKeyboardModifiers &= ~kModifierControl;
break;
case kKeyAlt:
if (down)
fKeyboardModifiers |= kModifierAlt;
else
fKeyboardModifiers &= ~kModifierAlt;
break;
}

if (special != 0)
return fUI.handlePluginSpecial(down, static_cast<Key>(special));
{
fUI.handlePluginSpecial(down, static_cast<Key>(special), fKeyboardModifiers);
return 1;
}

if (index >= 0)
return fUI.handlePluginKeyboard(down, static_cast<uint>(index));
# endif
if (index > 0)
{
fUI.handlePluginKeyboard(down, static_cast<uint>(index), fKeyboardModifiers);
return 1;
}

return 0;
}
# endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

// -------------------------------------------------------------------

protected:
intptr_t hostCallback(const int32_t opcode,
const int32_t index = 0,
const intptr_t value = 0,
void* const ptr = nullptr,
const float opt = 0.0f)
inline intptr_t hostCallback(const int32_t opcode,
const int32_t index = 0,
const intptr_t value = 0,
void* const ptr = nullptr,
const float opt = 0.0f) const
{
return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
}

void editParameter(const uint32_t index, const bool started)
void editParameter(const uint32_t index, const bool started) const
{
hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index);
}
@@ -357,7 +387,7 @@ private:

// Plugin UI
UIExporter fUI;
bool fShouldCaptureVstKeys;
uint16_t fKeyboardModifiers;

// -------------------------------------------------------------------
// Callbacks
@@ -654,12 +684,12 @@ public:
}
break;

//case effIdle:
case effEditIdle:
if (fVstUI != nullptr)
fVstUI->idle();
break;

# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
case effEditKeyDown:
if (fVstUI != nullptr)
return fVstUI->handlePluginKeyEvent(true, index, value);
@@ -669,6 +699,7 @@ public:
if (fVstUI != nullptr)
return fVstUI->handlePluginKeyEvent(false, index, value);
break;
# endif
#endif // DISTRHO_PLUGIN_HAS_UI

#if DISTRHO_PLUGIN_WANT_STATE
@@ -724,9 +755,6 @@ public:
// add another separator
chunkStr += "\xff";

// temporarily set locale to "C" while converting floats
const ScopedSafeLocale ssl;

for (uint32_t i=0; i<paramCount; ++i)
{
if (fPlugin.isParameterOutputOrTrigger(i))


+ 69
- 57
distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,15 +14,18 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoUIInternal.hpp"
#include "DistrhoUIPrivateData.hpp"
#include "src/WindowPrivateData.hpp"
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "src/WidgetPrivateData.hpp"
# include "src/TopLevelWidgetPrivateData.hpp"
#endif

#include "NanoVG.hpp"

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
* Static data, see DistrhoUIInternal.hpp */
* Static data, see DistrhoUIInternal.hpp and DistrhoUIPrivateData.hpp */

double d_lastUiSampleRate = 0.0;
void* d_lastUiDspPtr = nullptr;
@@ -34,20 +37,46 @@ uintptr_t g_nextWindowId = 0;
Window* d_lastUiWindow = nullptr;
#endif

// -----------------------------------------------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextScaleFactor = scaleFactor;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextScaleFactor = 1.0;
g_nextBundlePath = nullptr;
return ret;
}
#else
UI* createUiWrapper(void* const dspPtr, Window* const window)
{
d_lastUiDspPtr = dspPtr;
d_lastUiWindow = window;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
d_lastUiWindow = nullptr;
return ret;
}
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

/* ------------------------------------------------------------------------------------------------------------
* UI */

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI::UI(uint width, uint height)
: UIWidget(width, height),
pData(new PrivateData()) {}
uiData(new PrivateData()) {}
#else
UI::UI(uint width, uint height)
: UIWidget(*d_lastUiWindow),
pData(new PrivateData())
uiData(new PrivateData())
{
((UIWidget*)this)->pData->needsFullViewport = false;

if (width > 0 && height > 0)
setSize(width, height);
}
@@ -55,75 +84,55 @@ UI::UI(uint width, uint height)

UI::~UI()
{
delete pData;
}

#if DISTRHO_UI_USER_RESIZABLE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale)
{
DISTRHO_SAFE_ASSERT_RETURN(minWidth > 0,);
DISTRHO_SAFE_ASSERT_RETURN(minHeight > 0,);

pData->automaticallyScale = automaticallyScale;
pData->minWidth = minWidth;
pData->minHeight = minHeight;

Window& window(getParentWindow());

const double uiScaleFactor = window.getScaling();
window.setGeometryConstraints(minWidth * uiScaleFactor, minHeight * uiScaleFactor, keepAspectRatio);

if (d_isNotZero(uiScaleFactor - 1.0))
setSize(getWidth() * uiScaleFactor, getHeight() * uiScaleFactor);
delete uiData;
}
#endif

/* ------------------------------------------------------------------------------------------------------------
* Host state */

uint UI::getBackgroundColor() const noexcept
{
return pData->bgColor;
return uiData->bgColor;
}

uint UI::getForegroundColor() const noexcept
{
return pData->fgColor;
return uiData->fgColor;
}

double UI::getSampleRate() const noexcept
{
return pData->sampleRate;
return uiData->sampleRate;
}

void UI::editParameter(uint32_t index, bool started)
{
pData->editParamCallback(index + pData->parameterOffset, started);
uiData->editParamCallback(index + uiData->parameterOffset, started);
}

void UI::setParameterValue(uint32_t index, float value)
{
pData->setParamCallback(index + pData->parameterOffset, value);
uiData->setParamCallback(index + uiData->parameterOffset, value);
}

#if DISTRHO_PLUGIN_WANT_STATE
void UI::setState(const char* key, const char* value)
{
pData->setStateCallback(key, value);
uiData->setStateCallback(key, value);
}
#endif

#if DISTRHO_PLUGIN_WANT_STATEFILES
bool UI::requestStateFile(const char* key)
{
return pData->fileRequestCallback(key);
return uiData->fileRequestCallback(key);
}
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
{
pData->sendNoteCallback(channel, note, velocity);
uiData->sendNoteCallback(channel, note, velocity);
}
#endif

@@ -133,7 +142,7 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)

void* UI::getPluginInstancePointer() const noexcept
{
return pData->dspPtr;
return uiData->dspPtr;
}
#endif

@@ -157,7 +166,7 @@ uintptr_t UI::getNextWindowId() noexcept
return g_nextWindowId;
}
# endif
#endif
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

/* ------------------------------------------------------------------------------------------------------------
* DSP/Plugin Callbacks (optional) */
@@ -174,22 +183,10 @@ void UI::uiFileBrowserSelected(const char*)
}
# endif

void UI::uiReshape(uint width, uint height)
{
#ifdef DGL_OPENGL
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#else
// unused
(void)width;
(void)height;
#endif
void UI::uiReshape(uint, uint)
{
// NOTE this must be the same as Window::onReshape
pData->fallbackOnResize();
}

/* ------------------------------------------------------------------------------------------------------------
@@ -197,13 +194,28 @@ void UI::uiReshape(uint width, uint height)

void UI::onResize(const ResizeEvent& ev)
{
if (pData->resizeInProgress)
if (uiData->resizeInProgress)
return;

pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight());
UIWidget::onResize(ev);

const uint width = ev.size.getWidth();
const uint height = ev.size.getHeight();

/*
pData->window.setSize(width, height);
*/
uiData->setSizeCallback(width, height);
}
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

// -----------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// Possible template data types

// template class NanoBaseWidget<SubWidget>;
// template class NanoBaseWidget<TopLevelWidget>;
// template class NanoBaseWidget<StandaloneWindow>;

+ 1
- 0
distrho/src/DistrhoUIDSSI.cpp View File

@@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#define DISTRHO_UI_IS_STANDALONE 1
#include "DistrhoUIInternal.hpp"

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS


+ 51
- 163
distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,7 +17,7 @@
#ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED
#define DISTRHO_UI_INTERNAL_HPP_INCLUDED

#include "../DistrhoUI.hpp"
#include "DistrhoUIPrivateData.hpp"

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../extra/Sleep.hpp"
@@ -46,163 +46,48 @@ extern Window* d_lastUiWindow;
#endif

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
// DSP
double sampleRate;
uint32_t parameterOffset;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* dspPtr;
#endif

// UI
bool automaticallyScale;
bool resizeInProgress;
uint minWidth;
uint minHeight;
uint bgColor;
uint fgColor;

// Callbacks
void* callbacksPtr;
editParamFunc editParamCallbackFunc;
setParamFunc setParamCallbackFunc;
setStateFunc setStateCallbackFunc;
sendNoteFunc sendNoteCallbackFunc;
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData() noexcept
: sampleRate(d_lastUiSampleRate),
parameterOffset(0),
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
dspPtr(d_lastUiDspPtr),
#endif
automaticallyScale(false),
resizeInProgress(false),
minWidth(0),
minHeight(0),
bgColor(0),
fgColor(0),
callbacksPtr(nullptr),
editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));

#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
parameterOffset += 1;
# endif
# endif
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* dspPtr, uintptr_t winId, double scaleFactor, const char* bundlePath);
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* dspPtr, Window* window);
#endif
}

void editParamCallback(const uint32_t rindex, const bool started)
{
if (editParamCallbackFunc != nullptr)
editParamCallbackFunc(callbacksPtr, rindex, started);
}

void setParamCallback(const uint32_t rindex, const float value)
{
if (setParamCallbackFunc != nullptr)
setParamCallbackFunc(callbacksPtr, rindex, value);
}

void setStateCallback(const char* const key, const char* const value)
{
if (setStateCallbackFunc != nullptr)
setStateCallbackFunc(callbacksPtr, key, value);
}

void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
if (sendNoteCallbackFunc != nullptr)
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
}

void setSizeCallback(const uint width, const uint height)
{
if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height);
}
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

bool fileRequestCallback(const char* key)
class PluginApplication : public Application
{
public:
PluginApplication()
: Application(DISTRHO_UI_IS_STANDALONE)
{
if (fileRequestCallbackFunc != nullptr)
return fileRequestCallbackFunc(callbacksPtr, key);

// TODO use old style DPF dialog here

return false;
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
setClassName(className);
}
};

// -----------------------------------------------------------------------
// Plugin Window, needed to take care of resize properly

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
static inline
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextScaleFactor = scaleFactor;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextScaleFactor = 1.0;
g_nextBundlePath = nullptr;
return ret;
}
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
static inline
UI* createUiWrapper(void* const dspPtr, Window* const window)
{
d_lastUiDspPtr = dspPtr;
d_lastUiWindow = window;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
d_lastUiWindow = nullptr;
return ret;
}

class UIExporterWindow : public Window
{
public:
UIExporterWindow(Application& app, const intptr_t winId, const double scaleFactor, void* const dspPtr)
UIExporterWindow(PluginApplication& app, const intptr_t winId, const double scaleFactor, void* const dspPtr)
: Window(app, winId, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
fUI(createUiWrapper(dspPtr, this)),
fIsReady(false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI->pData != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI->uiData != nullptr,);

setSize(fUI->getWidth(), fUI->getHeight());
}
@@ -228,19 +113,14 @@ protected:
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

UI::PrivateData* const pData = fUI->pData;
DISTRHO_SAFE_ASSERT_RETURN(pData != nullptr,);

if (pData->automaticallyScale)
{
const double scaleHorizontal = static_cast<double>(width) / static_cast<double>(pData->minWidth);
const double scaleVertical = static_cast<double>(height) / static_cast<double>(pData->minHeight);
_setAutoScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical);
}
UI::PrivateData* const uiData = fUI->uiData;
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,);

pData->resizeInProgress = true;
/*
uiData->resizeInProgress = true;
fUI->setSize(width, height);
pData->resizeInProgress = false;
uiData->resizeInProgress = false;
*/

fUI->uiReshape(width, height);
fIsReady = true;
@@ -248,7 +128,7 @@ protected:

# ifndef DGL_FILE_BROWSER_DISABLED
// custom file-browser selected
void fileBrowserSelected(const char* filename) override
void onFileSelected(const char* const filename) override
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

@@ -278,7 +158,7 @@ public:
const fileRequestFunc fileRequestCall,
const char* const bundlePath = nullptr,
void* const dspPtr = nullptr,
const float scaleFactor = 1.0f,
const double scaleFactor = 1.0,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff)
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
@@ -289,7 +169,7 @@ public:
fChangingSize(false),
fUI(glWindow.getUI()),
#endif
fData((fUI != nullptr) ? fUI->pData : nullptr)
fData((fUI != nullptr) ? fUI->uiData : nullptr)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
@@ -339,7 +219,7 @@ public:
return fUI->isRunning();
}

intptr_t getWindowId() const noexcept
uintptr_t getNativeWindowHandle() const noexcept
{
return 0;
}
@@ -359,9 +239,9 @@ public:
return glWindow.isVisible();
}

intptr_t getWindowId() const noexcept
uintptr_t getNativeWindowHandle() const noexcept
{
return glWindow.getWindowId();
return glWindow.getNativeWindowHandle();
}
#endif

@@ -461,8 +341,8 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

glWindow.addIdleCallback(cb);
glWindow.setVisible(true);
glApp.addIdleCallback(cb);
glApp.exec();
}

@@ -549,9 +429,11 @@ public:
fChangingSize = false;
}

void setWindowTransientWinId(const uintptr_t winId)
void setWindowTransientWinId(const uintptr_t /*winId*/)
{
#if 0 /* TODO */
glWindow.setTransientWinId(winId);
#endif
}

bool setWindowVisible(const bool yesNo)
@@ -561,14 +443,20 @@ public:
return ! glApp.isQuiting();
}

bool handlePluginKeyboard(const bool press, const uint key)
bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/)
{
#if 0 /* TODO */
return glWindow.handlePluginKeyboard(press, key);
#endif
return false;
}

bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key)
bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/, const uint16_t /*mods*/)
{
#if 0 /* TODO */
return glWindow.handlePluginSpecial(press, key);
#endif
return false;
}
#endif

@@ -594,8 +482,8 @@ private:
// -------------------------------------------------------------------
// DGL Application and Window for this widget

Application glApp;
UIExporterWindow glWindow;
PluginApplication glApp;
UIExporterWindow glWindow;

// prevent recursion
bool fChangingSize;


+ 2
- 1
distrho/src/DistrhoUILV2.cpp View File

@@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#define DISTRHO_UI_IS_STANDALONE 0
#include "DistrhoUIInternal.hpp"

#include "../extra/String.hpp"
@@ -100,7 +101,7 @@ public:
fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight());

if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getWindowId();
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();

#if DISTRHO_PLUGIN_WANT_STATE
// tell the DSP we're ready to receive msgs


+ 152
- 0
distrho/src/DistrhoUIPrivateData.hpp View File

@@ -0,0 +1,152 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

#include "../DistrhoUI.hpp"

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// Static data, see DistrhoUI.cpp

extern double d_lastUiSampleRate;
extern void* d_lastUiDspPtr;

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
// DSP
double sampleRate;
uint32_t parameterOffset;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* dspPtr;
#endif

// UI
bool automaticallyScale;
bool resizeInProgress;
uint minWidth;
uint minHeight;
uint bgColor;
uint fgColor;

// Callbacks
void* callbacksPtr;
editParamFunc editParamCallbackFunc;
setParamFunc setParamCallbackFunc;
setStateFunc setStateCallbackFunc;
sendNoteFunc sendNoteCallbackFunc;
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData() noexcept
: sampleRate(d_lastUiSampleRate),
parameterOffset(0),
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
dspPtr(d_lastUiDspPtr),
#endif
automaticallyScale(false),
resizeInProgress(false),
minWidth(0),
minHeight(0),
bgColor(0),
fgColor(0),
callbacksPtr(nullptr),
editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));

#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
parameterOffset += 1;
# endif
# endif
#endif
}

void editParamCallback(const uint32_t rindex, const bool started)
{
if (editParamCallbackFunc != nullptr)
editParamCallbackFunc(callbacksPtr, rindex, started);
}

void setParamCallback(const uint32_t rindex, const float value)
{
if (setParamCallbackFunc != nullptr)
setParamCallbackFunc(callbacksPtr, rindex, value);
}

void setStateCallback(const char* const key, const char* const value)
{
if (setStateCallbackFunc != nullptr)
setStateCallbackFunc(callbacksPtr, key, value);
}

void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
if (sendNoteCallbackFunc != nullptr)
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
}

void setSizeCallback(const uint width, const uint height)
{
if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height);
}

bool fileRequestCallback(const char* key)
{
if (fileRequestCallbackFunc != nullptr)
return fileRequestCallbackFunc(callbacksPtr, key);

// TODO use old style DPF dialog here

return false;
}
};

// -----------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

+ 2600
- 0
examples/CairoUI/Artwork.cpp
File diff suppressed because it is too large
View File


+ 19
- 0
examples/CairoUI/Artwork.hpp View File

@@ -0,0 +1,19 @@
/* (Auto-generated binary data file). */

#ifndef BINARY_ARTWORK_HPP
#define BINARY_ARTWORK_HPP

namespace Artwork
{
extern const char* buttonOffData;
const unsigned int buttonOffDataSize = 970;

extern const char* buttonOnData;
const unsigned int buttonOnDataSize = 983;

extern const char* knobData;
const unsigned int knobDataSize = 52344;
}

#endif // BINARY_ARTWORK_HPP


+ 8
- 1
examples/CairoUI/CairoExamplePlugin.cpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -77,7 +78,13 @@ public:

void run(const float** inputs, float** outputs, uint32_t frames)
{
memcpy(outputs[0], inputs[0], frames * sizeof(float));
/**
This plugin does nothing, it just demonstrates cairo UI usage.
So here we directly copy inputs over outputs, leaving the audio untouched.
We need to be careful in case the host re-uses the same buffer for both inputs and outputs.
*/
if (outputs[0] != inputs[0])
std::memcpy(outputs[0], inputs[0], sizeof(float)*frames);
}
};



+ 25
- 8
examples/CairoUI/CairoExampleUI.cpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -15,9 +16,10 @@
*/

#include "DistrhoUI.hpp"

#include "Artwork.hpp"
#include "DemoWidgetBanner.hpp"
#include "DemoWidgetClickable.hpp"
#include "Window.hpp"

START_NAMESPACE_DISTRHO

@@ -36,16 +38,29 @@ public:
fWidgetBanner = widgetBanner;
widgetBanner->setSize(180, 80);
widgetBanner->setAbsolutePos(10, 10);
}

~CairoExampleUI()
{
CairoImage knobSkin;
knobSkin.loadFromPNG(Artwork::knobData, Artwork::knobDataSize);

CairoImageKnob* knob = new CairoImageKnob(this, knobSkin);
fKnob = knob;
knob->setSize(80, 80);
knob->setAbsolutePos(10, 100);

CairoImage buttonOn, buttonOff;
buttonOn.loadFromPNG(Artwork::buttonOnData, Artwork::buttonOnDataSize);
buttonOff.loadFromPNG(Artwork::buttonOffData, Artwork::buttonOffDataSize);

CairoImageButton* button = new CairoImageButton(this, buttonOff, buttonOn);
fButton = button;
button->setSize(60, 35);
button->setAbsolutePos(100, 160);
}

void onDisplay()
protected:
void onCairoDisplay(const CairoGraphicsContext& context)
{
cairo_t* cr = getParentWindow().getGraphicsContext().cairo;

cairo_t* const cr = context.handle;
cairo_set_source_rgb(cr, 1.0, 0.8, 0.5);
cairo_paint(cr);
}
@@ -60,6 +75,8 @@ public:
private:
ScopedPointer<DemoWidgetClickable> fWidgetClickable;
ScopedPointer<DemoWidgetBanner> fWidgetBanner;
ScopedPointer<CairoImageKnob> fKnob;
ScopedPointer<CairoImageButton> fButton;
};

UI* createUI()


+ 9
- 10
examples/CairoUI/DemoWidgetBanner.cpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -16,9 +17,6 @@

#include "DemoWidgetBanner.hpp"

#include "Cairo.hpp"
#include "Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
@@ -54,14 +52,15 @@ enum
columns = 72,
};

DemoWidgetBanner::DemoWidgetBanner(Widget* group)
: Widget(group)
{
}
DemoWidgetBanner::DemoWidgetBanner(SubWidget* parent)
: CairoSubWidget(parent) {}

DemoWidgetBanner::DemoWidgetBanner(TopLevelWidget* parent)
: CairoSubWidget(parent) {}

void DemoWidgetBanner::onDisplay()
void DemoWidgetBanner::onCairoDisplay(const CairoGraphicsContext& context)
{
cairo_t* cr = getParentWindow().getGraphicsContext().cairo;
cairo_t* cr = context.handle;

Size<uint> sz = getSize();
int w = sz.getWidth();


+ 10
- 5
examples/CairoUI/DemoWidgetBanner.hpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,17 +15,21 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "Widget.hpp"
#pragma once

#include "Cairo.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

class DemoWidgetBanner : public Widget
class DemoWidgetBanner : public CairoSubWidget
{
public:
explicit DemoWidgetBanner(Widget* group);
void onDisplay() override;
explicit DemoWidgetBanner(SubWidget* parent);
explicit DemoWidgetBanner(TopLevelWidget* parent);
protected:
void onCairoDisplay(const CairoGraphicsContext& context) override;
};

// -----------------------------------------------------------------------


+ 9
- 10
examples/CairoUI/DemoWidgetClickable.cpp View File

@@ -1,6 +1,7 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -16,21 +17,19 @@

#include "DemoWidgetClickable.hpp"

#include "Cairo.hpp"
#include "Window.hpp"

START_NAMESPACE_DGL

// -----------------------------------------------------------------------

DemoWidgetClickable::DemoWidgetClickable(Widget* group)
: Widget(group)
{
}
DemoWidgetClickable::DemoWidgetClickable(SubWidget* parent)
: CairoSubWidget(parent) {}

DemoWidgetClickable::DemoWidgetClickable(TopLevelWidget* parent)
: CairoSubWidget(parent) {}

void DemoWidgetClickable::onDisplay()
void DemoWidgetClickable::onCairoDisplay(const CairoGraphicsContext& context)
{
cairo_t* cr = getParentWindow().getGraphicsContext().cairo;
cairo_t* cr = context.handle;

Size<uint> sz = getSize();
int w = sz.getWidth();


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save