Browse Source

Update to latest dpf

Signed-off-by: falkTX <falktx@falktx.com>
main
falkTX 1 day ago
parent
commit
ab2b7ada54
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
100 changed files with 4732 additions and 3206 deletions
  1. +3
    -3
      dpf/CMakeLists.txt
  2. +1
    -1
      dpf/LICENSE
  3. +23
    -25
      dpf/Makefile.base.mk
  4. +115
    -51
      dpf/Makefile.plugins.mk
  5. +18
    -1
      dpf/dgl/Application.hpp
  6. +4
    -0
      dpf/dgl/Base.hpp
  7. +3
    -4
      dpf/dgl/Cairo.hpp
  8. +12
    -1
      dpf/dgl/Color.hpp
  9. +9
    -9
      dpf/dgl/Geometry.hpp
  10. +10
    -6
      dpf/dgl/Image.hpp
  11. +76
    -0
      dpf/dgl/Makefile
  12. +6
    -0
      dpf/dgl/OpenGL-include.hpp
  13. +140
    -17
      dpf/dgl/OpenGL.hpp
  14. +9
    -6
      dpf/dgl/Window.hpp
  15. +8
    -3
      dpf/dgl/src/Application.cpp
  16. +5
    -15
      dpf/dgl/src/ApplicationPrivateData.cpp
  17. +8
    -5
      dpf/dgl/src/ApplicationPrivateData.hpp
  18. +96
    -52
      dpf/dgl/src/Cairo.cpp
  19. +9
    -1
      dpf/dgl/src/Color.cpp
  20. +1
    -8
      dpf/dgl/src/EventHandlers.cpp
  21. +2
    -0
      dpf/dgl/src/Geometry.cpp
  22. +6
    -1
      dpf/dgl/src/NanoVG.cpp
  23. +35
    -562
      dpf/dgl/src/OpenGL.cpp
  24. +585
    -0
      dpf/dgl/src/OpenGL2.cpp
  25. +1033
    -0
      dpf/dgl/src/OpenGL3.cpp
  26. +0
    -5
      dpf/dgl/src/TopLevelWidgetPrivateData.cpp
  27. +0
    -1
      dpf/dgl/src/TopLevelWidgetPrivateData.hpp
  28. +39
    -36
      dpf/dgl/src/Window.cpp
  29. +44
    -29
      dpf/dgl/src/WindowPrivateData.cpp
  30. +5
    -1
      dpf/dgl/src/WindowPrivateData.hpp
  31. +24
    -20
      dpf/dgl/src/nanovg/nanovg_gl.h
  32. +1
    -1
      dpf/dgl/src/pugl-extra/haiku.cpp
  33. +93
    -35
      dpf/dgl/src/pugl-extra/wasm.c
  34. +9
    -8
      dpf/dgl/src/pugl-upstream/.clang-format
  35. +4
    -0
      dpf/dgl/src/pugl-upstream/.clang-format-ignore
  36. +4
    -3
      dpf/dgl/src/pugl-upstream/.clang-tidy
  37. +0
    -4
      dpf/dgl/src/pugl-upstream/.clant.json
  38. +1
    -1
      dpf/dgl/src/pugl-upstream/.reuse/dep5
  39. +1
    -5
      dpf/dgl/src/pugl-upstream/README.md
  40. +2
    -17
      dpf/dgl/src/pugl-upstream/include/pugl/attributes.h
  41. +3
    -4
      dpf/dgl/src/pugl-upstream/include/pugl/cairo.h
  42. +6
    -10
      dpf/dgl/src/pugl-upstream/include/pugl/gl.h
  43. +282
    -795
      dpf/dgl/src/pugl-upstream/include/pugl/pugl.h
  44. +3
    -4
      dpf/dgl/src/pugl-upstream/include/pugl/stub.h
  45. +9
    -16
      dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h
  46. +0
    -1
      dpf/dgl/src/pugl-upstream/src/.clang-tidy
  47. +98
    -53
      dpf/dgl/src/pugl-upstream/src/common.c
  48. +95
    -34
      dpf/dgl/src/pugl-upstream/src/internal.c
  49. +30
    -10
      dpf/dgl/src/pugl-upstream/src/internal.h
  50. +1
    -1
      dpf/dgl/src/pugl-upstream/src/mac.h
  51. +143
    -226
      dpf/dgl/src/pugl-upstream/src/mac.m
  52. +1
    -1
      dpf/dgl/src/pugl-upstream/src/mac_cairo.m
  53. +1
    -1
      dpf/dgl/src/pugl-upstream/src/mac_gl.m
  54. +1
    -1
      dpf/dgl/src/pugl-upstream/src/mac_stub.m
  55. +3
    -3
      dpf/dgl/src/pugl-upstream/src/mac_vulkan.m
  56. +22
    -7
      dpf/dgl/src/pugl-upstream/src/platform.h
  57. +2
    -1
      dpf/dgl/src/pugl-upstream/src/stub.h
  58. +18
    -24
      dpf/dgl/src/pugl-upstream/src/types.h
  59. +246
    -293
      dpf/dgl/src/pugl-upstream/src/win.c
  60. +6
    -15
      dpf/dgl/src/pugl-upstream/src/win.h
  61. +7
    -8
      dpf/dgl/src/pugl-upstream/src/win_cairo.c
  62. +37
    -25
      dpf/dgl/src/pugl-upstream/src/win_gl.c
  63. +1
    -1
      dpf/dgl/src/pugl-upstream/src/win_stub.c
  64. +1
    -1
      dpf/dgl/src/pugl-upstream/src/win_vulkan.c
  65. +164
    -267
      dpf/dgl/src/pugl-upstream/src/x11.c
  66. +3
    -7
      dpf/dgl/src/pugl-upstream/src/x11.h
  67. +9
    -9
      dpf/dgl/src/pugl-upstream/src/x11_cairo.c
  68. +4
    -7
      dpf/dgl/src/pugl-upstream/src/x11_gl.c
  69. +2
    -2
      dpf/dgl/src/pugl-upstream/src/x11_stub.c
  70. +2
    -2
      dpf/dgl/src/pugl-upstream/src/x11_vulkan.c
  71. +91
    -122
      dpf/dgl/src/pugl.cpp
  72. +3
    -6
      dpf/dgl/src/pugl.hpp
  73. +18
    -0
      dpf/distrho/DistrhoInfo.hpp
  74. +3
    -4
      dpf/distrho/DistrhoPluginMain.cpp
  75. +29
    -20
      dpf/distrho/DistrhoPluginUtils.hpp
  76. +15
    -19
      dpf/distrho/DistrhoUI.hpp
  77. +13
    -6
      dpf/distrho/DistrhoUIMain.cpp
  78. +6
    -2
      dpf/distrho/extra/RingBuffer.hpp
  79. +5
    -0
      dpf/distrho/extra/ScopedPointer.hpp
  80. +54
    -58
      dpf/distrho/extra/String.hpp
  81. +9
    -6
      dpf/distrho/src/DistrhoPluginCLAP.cpp
  82. +1
    -1
      dpf/distrho/src/DistrhoPluginJACK.cpp
  83. +17
    -0
      dpf/distrho/src/DistrhoPluginLV2export.cpp
  84. +158
    -0
      dpf/distrho/src/DistrhoPluginMAPI.cpp
  85. +6
    -5
      dpf/distrho/src/DistrhoPluginVST2.cpp
  86. +3
    -3
      dpf/distrho/src/DistrhoUI.cpp
  87. +6
    -5
      dpf/distrho/src/DistrhoUIInternal.hpp
  88. +16
    -12
      dpf/distrho/src/DistrhoUILV2.cpp
  89. +58
    -13
      dpf/distrho/src/DistrhoUIPrivateData.hpp
  90. +3
    -2
      dpf/distrho/src/DistrhoUIVST3.cpp
  91. +133
    -70
      dpf/distrho/src/DistrhoUtils.cpp
  92. +28
    -17
      dpf/distrho/src/jackbridge/JackBridge.cpp
  93. +46
    -20
      dpf/distrho/src/jackbridge/NativeBridge.hpp
  94. +29
    -5
      dpf/distrho/src/jackbridge/RtAudioBridge.hpp
  95. +47
    -44
      dpf/distrho/src/jackbridge/WebBridge.hpp
  96. +73
    -0
      dpf/distrho/src/mapi/mapi.h
  97. +203
    -0
      dpf/utils/emscripten.html.in
  98. +6
    -0
      dpf/utils/symbols/mapi.def
  99. +5
    -0
      dpf/utils/symbols/mapi.exp
  100. +4
    -0
      dpf/utils/symbols/mapi.version

+ 3
- 3
dpf/CMakeLists.txt View File

@@ -4,7 +4,7 @@
#
# SPDX-License-Identifier: ISC

cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.8...3.31)

project(DPF)

@@ -30,7 +30,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(DPF-plugin)

if(DPF_LIBRARIES)
find_package(PkgConfig)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND AND (NOT HAIKU))
@@ -42,7 +42,7 @@ if(DPF_LIBRARIES)
endif()

if(DPF_EXAMPLES)
find_package(PkgConfig)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND AND (NOT HAIKU))


+ 1
- 1
dpf/LICENSE View File

@@ -1,4 +1,4 @@
Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
Copyright (C) 2012-2025 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


+ 23
- 25
dpf/Makefile.base.mk View File

@@ -20,16 +20,14 @@
# Tweak `nvgTextBreakLines` to allow space characters
# FIXME proper details

# NVG_FONT_TEXTURE_FLAGS=0
# FILE_BROWSER_DISABLED=true
# WINDOWS_ICON_ID=0
# USE_GLES2=true
# USE_GLES3=true
# USE_OPENGL3=true
# USE_NANOVG_FBO=true
# USE_NANOVG_FREETYPE=true
# NVG_FONT_TEXTURE_FLAGS=
# WINDOWS_ICON_ID=
# USE_NANOVG_FBO=false
# USE_NANOVG_FREETYPE=false
# USE_FILE_BROWSER=true
# USE_WEB_VIEW=true
# USE_GLES2=false
# USE_GLES3=false
# USE_WEB_VIEW=false

# STATIC_BUILD=true
# Tweak build to be able to generate fully static builds (e.g. skip use of libdl)
@@ -333,6 +331,10 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden
CXXFLAGS += -fvisibility-inlines-hidden
endif

ifeq ($(WASM),true)
LINK_OPTS += -sALLOW_MEMORY_GROWTH
endif

ifeq ($(WITH_LTO),true)
BASE_FLAGS += -fno-strict-aliasing -flto
LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr
@@ -469,7 +471,7 @@ endif
else ifeq ($(WASM),true)

# wasm builds cannot work using regular desktop OpenGL
ifeq (,$(USE_GLES2)$(USE_GLES3))
ifeq (,$(findstring true,$(USE_GLES2)$(USE_GLES3)))
USE_GLES2 = true
endif

@@ -486,12 +488,10 @@ endif

else

ifneq ($(FILE_BROWSER_DISABLED),true)
ifeq ($(HAVE_DBUS),true)
ifeq ($(USE_FILE_BROWSER)$(HAVE_DBUS),truetrue)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1)
endif
endif

ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
@@ -544,14 +544,16 @@ else ifeq ($(MACOS),true)
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations
OPENGL_LIBS = -framework OpenGL
else ifeq ($(WASM),true)
ifeq ($(USE_GLES2),true)
OPENGL_FLAGS =
ifeq ($(USE_GLES3),true)
OPENGL_LIBS = -sMIN_WEBGL_VERSION=3 -sMAX_WEBGL_VERSION=3
else ifeq ($(USE_GLES2),true)
OPENGL_LIBS = -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
else
ifneq ($(USE_GLES3),true)
OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0
endif
OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0
endif
else ifeq ($(WINDOWS),true)
OPENGL_FLAGS =
OPENGL_LIBS = -lopengl32
else
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11)
@@ -581,7 +583,7 @@ DGL_FLAGS += -DHAVE_VULKAN
VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan)
VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan)

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

@@ -666,13 +668,9 @@ endif

ifeq ($(USE_GLES2),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2
endif

ifeq ($(USE_GLES3),true)
else ifeq ($(USE_GLES3),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3
endif

ifeq ($(USE_OPENGL3),true)
else ifeq ($(USE_OPENGL3),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3
endif

@@ -700,7 +698,7 @@ endif
# Set app extension

ifeq ($(WASM),true)
APP_EXT = .html
APP_EXT = .js
else ifeq ($(WINDOWS),true)
APP_EXT = .exe
endif


+ 115
- 51
dpf/Makefile.plugins.mk View File

@@ -9,8 +9,8 @@
# extra useful variables to define before including this file:
# - DPF_BUILD_DIR: where to place temporary build files
# - DPF_TARGET_DIR: where to place final binary files
# - UI_TYPE: one of cairo, opengl, opengl3 or external, with opengl being default
# ("generic" is also allowed if only using image widgets)
# - UI_TYPE: one of cairo, external, gles2, gles3, opengl, opengl3 or webview, with opengl being default
# ("generic" is also allowed if only using basic DPF classes like image widgets)

# override the "all" target after including this file to define which plugin formats to build, like so:
# all: au clap jack lv2_sep vst2 vst3
@@ -55,6 +55,10 @@ ifeq ($(UI_TYPE),)
else ifeq ($(UI_TYPE),cairo)
else ifeq ($(UI_TYPE),external)
else ifeq ($(UI_TYPE),generic)
else ifeq ($(UI_TYPE),gles2)
USE_GLES2 = true
else ifeq ($(UI_TYPE),gles3)
USE_GLES3 = true
else ifeq ($(UI_TYPE),opengl)
else ifeq ($(UI_TYPE),opengl3)
USE_OPENGL3 = true
@@ -140,7 +144,7 @@ JACK_FLAGS += -sUSE_SDL=2
JACK_LIBS += -sUSE_SDL=2
JACK_LIBS += -sMAIN_MODULE -ldl

ifneq ($(FILE_BROWSER_DISABLED),true)
ifeq ($(USE_FILE_BROWSER),true)
JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap
endif

@@ -201,8 +205,12 @@ UI_TYPE = none
endif

ifeq ($(UI_TYPE),)
ifeq ($(WASM),true)
UI_TYPE = gles2
else
UI_TYPE = opengl
endif
endif

ifeq ($(UI_TYPE),generic)
ifeq ($(HAVE_OPENGL),true)
@@ -227,6 +235,30 @@ HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),gles2)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-gles2.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),gles3)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-gles3.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),opengl)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL
@@ -241,7 +273,7 @@ endif

ifeq ($(UI_TYPE),opengl3)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl3.a
@@ -287,7 +319,7 @@ HAVE_DGL = false
endif
endif

ifeq ($(HAVE_DGL)$(LINUX)$(UI_TYPE),truetruewebview)
ifeq ($(HAVE_DGL)$(LINUX)$(USE_WEB_VIEW),truetruetrue)
DGL_LIB_SHARED = $(shell $(CC) -print-file-name=Scrt1.o)
endif

@@ -340,6 +372,18 @@ ifeq ($(WINDOWS)$(HAVE_DGL),truetrue)
JACK_LIBS += -Wl,-subsystem,windows
endif

ifeq ($(WASM),true)
MAPI_EXT = -mapi.js
MAPI_SHARED = \
-sEXPORT_NAME="$(MAPI_MODULE_NAME)" \
-sEXPORTED_RUNTIME_METHODS=['addFunction','lengthBytesUTF8','stringToUTF8','UTF8ToString'] \
-sMAIN_MODULE=2 \
-sMODULARIZE=1
else
MAPI_EXT = $(LIB_EXT)
MAPI_SHARED = $(SHARED)
endif

ifeq ($(MACOS_APP_BUNDLE),true)
jack = $(TARGET_DIR)/$(NAME).app/Contents/MacOS/$(NAME)
jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist
@@ -347,19 +391,19 @@ else
jack = $(TARGET_DIR)/$(NAME)$(APP_EXT)
endif

ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT)
clap = $(TARGET_DIR)/$(CLAP_FILENAME)
dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT)
dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT)
ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT)
lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT)
lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT)
lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT)
mapi = $(TARGET_DIR)/$(NAME)$(MAPI_EXT)
static = $(TARGET_DIR)/$(NAME).a
vst2 = $(TARGET_DIR)/$(VST2_FILENAME)
ifneq ($(VST3_FILENAME),)
vst3 = $(TARGET_DIR)/$(VST3_FILENAME)
endif
clap = $(TARGET_DIR)/$(CLAP_FILENAME)
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT)
static = $(TARGET_DIR)/$(NAME).a

ifeq ($(MACOS),true)
BUNDLE_RESOURCES = Info.plist PkgInfo Resources/empty.lproj
@@ -370,6 +414,10 @@ vst3files += $(BUNDLE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst3/Contents/%)
clapfiles += $(BUNDLE_RESOURCES:%=$(TARGET_DIR)/$(NAME).clap/Contents/%)
endif

ifeq ($(WASM),true)
jackfiles += $(TARGET_DIR)/$(NAME).html
endif

ifneq ($(HAVE_DGL),true)
dssi_ui =
lv2_ui =
@@ -386,45 +434,45 @@ endif

ifeq ($(MACOS),true)
SYMBOLS_AU = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/au.exp
SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp
SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp
SYMBOLS_DSSI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/dssi.exp
SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp
SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp
SYMBOLS_LV2DSP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-dsp.exp
SYMBOLS_LV2UI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-ui.exp
SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp
SYMBOLS_MAPI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/mapi.exp
SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.exp
SYMBOLS_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.exp
SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp
SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp
else ifeq ($(WASM),true)
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']"
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']"
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']"
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl']"
SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']"
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_MAPI = -sEXPORTED_FUNCTIONS="['_mapi_create','_mapi_process','_mapi_set_parameter','_mapi_set_state','_mapi_destroy']"
SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']"
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']"
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']"
else ifeq ($(WINDOWS),true)
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def
SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def
SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def
SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def
SYMBOLS_LV2DSP = $(DPF_PATH)/utils/symbols/lv2-dsp.def
SYMBOLS_LV2UI = $(DPF_PATH)/utils/symbols/lv2-ui.def
SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def
SYMBOLS_MAPI = $(DPF_PATH)/utils/symbols/mapi.def
SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def
SYMBOLS_VST3 = $(DPF_PATH)/utils/symbols/vst3.def
SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def
SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def
else ifneq ($(DEBUG),true)
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version
SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version
SYMBOLS_DSSI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/dssi.version
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version
SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version
SYMBOLS_LV2DSP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-dsp.version
SYMBOLS_LV2UI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-ui.version
SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version
SYMBOLS_MAPI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/mapi.version
SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version
SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version
SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version
SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version
endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -500,6 +548,12 @@ DGL_POSSIBLE_DEPS = \
$(DGL_BUILD_DIR)/libdgl-cairo.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl cairo

$(DGL_BUILD_DIR)/libdgl-gles2.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl gles2 USE_GLES2=true

$(DGL_BUILD_DIR)/libdgl-gles3.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl gles3 USE_GLES3=true

$(DGL_BUILD_DIR)/libdgl-opengl.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl opengl

@@ -777,6 +831,16 @@ endif
@echo "Creating AU component for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) -framework AudioToolbox -framework AudioUnit -framework CoreFoundation $(SHARED) $(SYMBOLS_AU) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# MAPI

mapi: $(mapi)

$(mapi): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_MAPI.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating MAPI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(MAPI_SHARED) $(SYMBOLS_MAPI) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Export

@@ -789,20 +853,6 @@ endif
@echo "Creating export tool for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Shared

shared: $(shared)

ifeq ($(HAVE_DGL),true)
$(shared): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED)
else
$(shared): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating shared library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Static

@@ -821,6 +871,8 @@ endif
# ---------------------------------------------------------------------------------------------------------------------
# macOS files

ifeq ($(MACOS),true)

$(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/Info.plist
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@
@@ -837,8 +889,21 @@ $(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.bundle/Contents/
-@mkdir -p $(shell dirname $@)
$(SILENT)cp $< $@

endif

# ---------------------------------------------------------------------------------------------------------------------
# format-specific files
# wasm files

ifeq ($(WASM),true)

$(TARGET_DIR)/$(NAME).html: $(DPF_PATH)/utils/emscripten.html.in
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e 's|@NAME@|$(NAME)|g' $< > $@

endif

# ---------------------------------------------------------------------------------------------------------------------
# auto-generated format-specific files

$(TARGET_DIR)/$(NAME).component/Contents/Info.plist: $(BUILD_DIR)/export$(APP_EXT)
-@mkdir -p $(shell dirname $@)
@@ -851,28 +916,27 @@ ifneq ($(UI_TYPE),)
-include $(OBJS_UI:%.o=%.d)
endif

-include $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_Export.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LV2_single_obj.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_MAPI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_Export.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d

-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_LV2_single_obj.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d

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

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

@@ -83,10 +83,21 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_off)
class DISTRHO_API Application
{
public:
/**
Type of application to setup, either "classic" or "modern".

What this means depends on the OS.
*/
enum Type {
kTypeAuto,
kTypeClassic,
kTypeModern,
};

/**
Constructor for standalone or plugin application.
*/
Application(bool isStandalone = true);
Application(bool isStandalone = true, Type type = kTypeAuto);

/**
Constructor for a standalone application.
@@ -141,6 +152,12 @@ public:
*/
double getTime() const;

/**
Return the application type, either kTypeClassic or kTypeModern.
This function never return kTypeAuto.
*/
Type getType() 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().


+ 4
- 0
dpf/dgl/Base.hpp View File

@@ -49,6 +49,10 @@
# error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out)
#endif

#ifndef DGL_ALLOW_DEPRECATED_METHODS
# define DGL_ALLOW_DEPRECATED_METHODS 1
#endif

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



+ 3
- 4
dpf/dgl/Cairo.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -165,10 +165,9 @@ private:
Widget display function.
Implemented internally to pass context into the drawing function.
*/
void onDisplay() override
void onDisplay() final
{
const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext());
onCairoDisplay(context);
onCairoDisplay(static_cast<const CairoGraphicsContext&>(BaseWidget::getGraphicsContext()));
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget);


+ 12
- 1
dpf/dgl/Color.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -115,6 +115,17 @@ struct Color {
*/
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept;

/**
Create a color from a RGB unsigned integer.
Basically doing:
```
uint8_t red = (color >> 24) & 0xff;
uint8_t green = (color >> 16) & 0xff;
uint8_t blue = (color >> 8) & 0xff;
```
*/
static Color fromRGB(uint color, float alpha = 1.0f) noexcept;

/**
Linearly interpolate this color against another.
*/


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

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -360,25 +360,23 @@ 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
#if DGL_ALLOW_DEPRECATED_METHODS
/**
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
#endif

private:
Point<T> posStart, posEnd;
@@ -489,7 +487,7 @@ public:
bool operator==(const Circle<T>& cir) const noexcept;
bool operator!=(const Circle<T>& cir) const noexcept;

#ifndef DPF_TEST_POINT_CPP
#if DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this circle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -503,7 +501,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif
#endif

private:
Point<T> fPos;
@@ -582,7 +580,7 @@ public:
bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept;

#ifndef DPF_TEST_POINT_CPP
#if DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this triangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -596,7 +594,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif
#endif

private:
Point<T> pos1, pos2, pos3;
@@ -813,6 +811,7 @@ public:
bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept;

#if DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this rectangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -826,6 +825,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> pos;


+ 10
- 6
dpf/dgl/Image.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,17 +17,21 @@
#ifndef DGL_IMAGE_HPP_INCLUDED
#define DGL_IMAGE_HPP_INCLUDED

#ifdef DGL_CAIRO
#include "Cairo.hpp"
#if defined(DGL_CAIRO)
# include "Cairo.hpp"
#elif defined(DGL_OPENGL)
# include "OpenGL.hpp"
#elif defined(DGL_VULKAN)
# include "Vulkan.hpp"
#else
#include "OpenGL.hpp"
# include "Base.hpp"
#endif

START_NAMESPACE_DGL

#ifdef DGL_CAIRO
#if defined(DGL_CAIRO)
typedef CairoImage Image;
#else
#elif defined(DGL_OPENGL)
typedef OpenGLImage Image;
#endif



+ 76
- 0
dpf/dgl/Makefile View File

@@ -70,8 +70,35 @@ endif

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

OBJS_gles2 = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.gles2.o \
$(BUILD_DIR)/dgl/OpenGL3.cpp.gles2.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.gles2.o

ifeq ($(MACOS),true)
OBJS_gles2 += $(BUILD_DIR)/dgl/pugl.mm.gles2.o
else
OBJS_gles2 += $(BUILD_DIR)/dgl/pugl.cpp.gles2.o
endif

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

OBJS_gles3 = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.gles3.o \
$(BUILD_DIR)/dgl/OpenGL3.cpp.gles3.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.gles3.o

ifeq ($(MACOS),true)
OBJS_gles3 += $(BUILD_DIR)/dgl/pugl.mm.gles3.o
else
OBJS_gles3 += $(BUILD_DIR)/dgl/pugl.cpp.gles3.o
endif

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

OBJS_opengl = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.opengl.o \
$(BUILD_DIR)/dgl/OpenGL2.cpp.opengl.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.opengl.o

ifeq ($(MACOS),true)
@@ -84,6 +111,7 @@ endif

OBJS_opengl3 = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.opengl3.o \
$(BUILD_DIR)/dgl/OpenGL3.cpp.opengl3.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.opengl3.o

ifeq ($(MACOS),true)
@@ -121,7 +149,10 @@ TARGETS += $(BUILD_DIR)/libdgl-cairo.a
endif

ifeq ($(HAVE_OPENGL),true)
TARGETS += $(BUILD_DIR)/libdgl-gles2.a
TARGETS += $(BUILD_DIR)/libdgl-gles3.a
TARGETS += $(BUILD_DIR)/libdgl-opengl.a
TARGETS += $(BUILD_DIR)/libdgl-opengl3.a
endif

ifeq ($(HAVE_STUB),true)
@@ -137,8 +168,15 @@ endif
all: $(TARGETS)

cairo: $(BUILD_DIR)/libdgl-cairo.a
gles2: $(BUILD_DIR)/libdgl-gles2.a
gles3: $(BUILD_DIR)/libdgl-gles3.a
ifeq ($(WASM),true)
opengl: gles2
opengl3: gles3
else
opengl: $(BUILD_DIR)/libdgl-opengl.a
opengl3: $(BUILD_DIR)/libdgl-opengl3.a
endif
stub: $(BUILD_DIR)/libdgl-stub.a
vulkan: $(BUILD_DIR)/libdgl-vulkan.a
web: $(BUILD_DIR)/libdgl-web.a
@@ -151,6 +189,18 @@ $(BUILD_DIR)/libdgl-cairo.a: $(OBJS_cairo)
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

$(BUILD_DIR)/libdgl-gles2.a: $(OBJS_gles2)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-gles2.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

$(BUILD_DIR)/libdgl-gles3.a: $(OBJS_gles3)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-gles3.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

$(BUILD_DIR)/libdgl-opengl.a: $(OBJS_opengl)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-opengl.a"
@@ -223,6 +273,30 @@ $(BUILD_DIR)/dgl/%.mm.cairo.o: src/%.mm

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

$(BUILD_DIR)/dgl/%.cpp.gles2.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv2 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2 -UDGL_USE_GLES3 -c -o $@

$(BUILD_DIR)/dgl/%.mm.gles2.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv2 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2 -UDGL_USE_GLES3 -c -ObjC++ -o $@

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

$(BUILD_DIR)/dgl/%.cpp.gles3.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3 -UDGL_USE_GLES2 -c -o $@

$(BUILD_DIR)/dgl/%.mm.gles3.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3 -UDGL_USE_GLES2 -c -ObjC++ -o $@

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

$(BUILD_DIR)/dgl/%.cpp.opengl.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL variant)"
@@ -269,6 +343,8 @@ debug:

-include $(OBJS_common:%.o=%.d)
-include $(OBJS_cairo:%.o=%.d)
-include $(OBJS_gles2:%.o=%.d)
-include $(OBJS_gles3:%.o=%.d)
-include $(OBJS_opengl:%.o=%.d)
-include $(OBJS_opengl3:%.o=%.d)
-include $(OBJS_stub:%.o=%.d)


+ 6
- 0
dpf/dgl/OpenGL-include.hpp View File

@@ -53,6 +53,12 @@

#ifdef DISTRHO_OS_MAC
# ifdef DGL_USE_OPENGL3
// NOTE GLES with macOS is not supported
# ifdef DGL_USE_GLES
# undef DGL_USE_GLES
# undef DGL_USE_GLES2
# undef DGL_USE_GLES3
# endif
# include <OpenGL/gl3.h>
# include <OpenGL/gl3ext.h>
# else


+ 140
- 17
dpf/dgl/OpenGL.hpp View File

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

START_NAMESPACE_DGL

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

#ifdef DGL_USE_OPENGL3
/**
OpenGL Graphics context.
OpenGL3 Graphics context.

This provides access to the program, shaders and uniforms used by the underlying DPF implementation.
*/
struct OpenGLGraphicsContext : GraphicsContext
struct OpenGL3GraphicsContext : GraphicsContext
{
#ifdef DGL_USE_OPENGL3
#endif
/**
The OpenGL3 program used for this context.
It is activated automatically before any widget onDisplay() is called.
If changing the current OpenGL program make sure to revert back to this one at the end of your pipeline.

@code
// use custom program
glUseProgram(context.program);

// custom stuff here

// revert back
glUseProgram(context.program);
@endcode
*/
GLuint program;

/**
A vec4 uniform used to set the next drawing color.

@code
const GLfloat color[4] = { red, green, blue, alpha };
glUniform4fv(context.color, 1, color);
@endcode
*/
GLuint color;

/**
A vertex shader attribute directly linked to gl_Position.
Use this to set the bounds for drawing, normalized as -1.0 to +1.0.
The @a width and @a height provide the total window size for convenience.

@code
const GLfloat triangle[] = { x1, y1, x2, y2, x3, y3 };
glEnableVertexAttribArray(context.bounds);
glVertexAttribPointer(context.bounds, 2, GL_FLOAT, GL_FALSE, 0, triangle);
@endcode
*/
GLuint bounds;

/**
A vertex shader attribute directly linked to GL_TEXTURE0 map.
// TODO find the correct wording, map??.

@code
const GLfloat map[] = { 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f };
glEnableVertexAttribArray(context.textureMap);
glVertexAttribPointer(context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, map);
@endcode
*/
GLuint textureMap;

/**
A boolean uniform used to indicate if next drawing should @a texture or @a color.
Set to 0 for color mode, 1 for texture.
Default mode is color, if changed make sure to revert to color mode at the end of your pipeline.

@code
// setup for drawing based on texture
glUniform1i(context.usingTexture, 1);

// bind texture
glBindTexture(GL_TEXTURE_2D, myTextureId);
// etc..

// glDrawElements or similar

// unbind texture
glBindTexture(GL_TEXTURE_2D, 0);
// etc..

// revert to color mode
glUniform1i(context.usingTexture, 0);
@endcode
*/
GLuint usingTexture;

/**
Set of buffers created with glGenBuffers.
Used internally in DPF to draw generic shapes, can be reused in custom code.
Unbound by default, make sure to leave them unbound at the end of your pipeline.
*/
GLuint buffers[2];

/**
Total width of the window used for this context.
*/
uint width;

/**
Total height of the window used for this context.
*/
uint height;
};
#endif

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

static inline
ImageFormat asDISTRHOImageFormat(const GLenum format)
{
switch (format)
{
#ifdef DGL_USE_OPENGL3
#if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2)
case GL_RED:
#else
#else
case GL_LUMINANCE:
#endif
#endif
return kImageFormatGrayscale;
#ifndef DGL_USE_GLES
case GL_BGR:
return kImageFormatBGR;
case GL_BGRA:
return kImageFormatBGRA;
#endif
case GL_RGB:
return kImageFormatRGB;
case GL_RGBA:
@@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format)
case kImageFormatNull:
break;
case kImageFormatGrayscale:
#ifdef DGL_USE_OPENGL3
#if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2)
return GL_RED;
#else
#else
return GL_LUMINANCE;
#endif
#endif
case kImageFormatBGR:
#ifndef DGL_USE_GLES
return GL_BGR;
#else
return 0;
#endif
case kImageFormatBGRA:
#ifndef DGL_USE_GLES
return GL_BGRA;
#else
return 0;
#endif
case kImageFormatRGB:
return GL_RGB;
case kImageFormatRGBA:
return GL_RGBA;
}

return 0x0;
return 0;
}

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

/**
OpenGL Image class.
@@ -144,6 +249,18 @@ public:
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

#ifdef DGL_USE_GLES
/**
Get the image format.
*/
ImageFormat getFormat() const noexcept;

/**
Get the raw image data.
*/
const char* getRawData() const noexcept;
#endif

/**
TODO document this.
*/
@@ -157,6 +274,7 @@ public:
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); }

#if DGL_ALLOW_DEPRECATED_METHODS
/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
@@ -200,14 +318,19 @@ public:
*/
DISTRHO_DEPRECATED
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; }
#endif // DGL_ALLOW_DEPRECATED_METHODS

private:
bool setupCalled;
bool textureInit;
GLuint textureId;
#ifdef DGL_USE_GLES
mutable char* convertedData;
mutable const char* rawDataLast;
#endif
};

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

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;
@@ -215,7 +338,7 @@ typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

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

END_NAMESPACE_DGL



+ 9
- 6
dpf/dgl/Window.hpp View File

@@ -479,6 +479,7 @@ public:
*/
void setTransientParent(uintptr_t transientParentWindowHandle);

#if DGL_ALLOW_DEPRECATED_METHODS
/** DEPRECATED Use isIgnoringKeyRepeat(). */
DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()")
inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); }
@@ -490,6 +491,7 @@ public:
/** DEPRECATED Use runAsModal(bool). */
DISTRHO_DEPRECATED_BY("runAsModal(bool)")
inline void exec(bool blockWait = false) { runAsModal(blockWait); }
#endif

protected:
/**
@@ -528,12 +530,11 @@ protected:
*/
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.
*/
#if DGL_ALLOW_DEPRECATED_METHODS
/** DEPRECATED DO NOT USE */
DISTRHO_DEPRECATED
virtual void onReshape(uint width, uint height);
#endif

/**
A function called when scale factor requested for this window changes.
@@ -542,7 +543,7 @@ protected:
*/
virtual void onScaleFactorChanged(double scaleFactor);

#ifdef DGL_USE_FILE_BROWSER
#ifdef DGL_USE_FILE_BROWSER
/**
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.
@@ -550,10 +551,12 @@ protected:
*/
virtual void onFileSelected(const char* filename);

#if DGL_ALLOW_DEPRECATED_METHODS
/** DEPRECATED Use onFileSelected(). */
DISTRHO_DEPRECATED_BY("onFileSelected(const char*)")
inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); }
#endif
#endif

private:
PrivateData* const pData;


+ 8
- 3
dpf/dgl/src/Application.cpp View File

@@ -98,8 +98,8 @@ static void app_idle(void* const app)
}
#endif

Application::Application(const bool isStandalone)
: pData(new PrivateData(isStandalone))
Application::Application(const bool isStandalone, const Type type)
: pData(new PrivateData(isStandalone, type))
{
// build config sentinels
#ifdef DPF_DEBUG
@@ -126,7 +126,7 @@ Application::Application(const bool isStandalone)
}

Application::Application(int argc, char* argv[])
: pData(new PrivateData(true))
: pData(new PrivateData(true, kTypeAuto))
{
#if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW)
if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0)
@@ -213,6 +213,11 @@ double Application::getTime() const
return pData->getTime();
}

Application::Type Application::getType() const noexcept
{
return pData->isModern ? kTypeModern : kTypeClassic;
}

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


+ 5
- 15
dpf/dgl/src/ApplicationPrivateData.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -53,13 +53,14 @@ const char* Application::getClassName() const noexcept

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

Application::PrivateData::PrivateData(const bool standalone)
Application::PrivateData::PrivateData(const bool standalone, const Type type)
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE,
standalone ? PUGL_WORLD_THREADS : 0x0)),
(standalone ? PUGL_WORLD_THREADS : 0))),
isModern(false),
isStandalone(standalone),
isStarting(true),
isQuitting(false),
isQuittingInNextCycle(false),
isStarting(true),
needsRepaint(false),
visibleWindows(0),
mainThreadHandle(getCurrentThreadHandle()),
@@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone)
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

#ifdef DGL_USING_SDL
SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO);
#else
puglSetWorldHandle(world, this);
#ifdef __EMSCRIPTEN__
puglSetWorldString(world, PUGL_CLASS_NAME, "canvas");
#else
puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));
#endif
#endif
}

Application::PrivateData::~PrivateData()
@@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData()
windows.clear();
idleCallbacks.clear();

#ifdef DGL_USING_SDL
SDL_Quit();
#else
if (world != nullptr)
puglFreeWorld(world);
#endif
}

// --------------------------------------------------------------------------------------------------------------------
@@ -173,13 +165,11 @@ void Application::PrivateData::quit()

isQuitting = true;

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

double Application::PrivateData::getTime() const


+ 8
- 5
dpf/dgl/src/ApplicationPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -51,18 +51,21 @@ struct Application::PrivateData {
/** Pugl world instance. */
PuglWorld* const world;

/** Whether the applicating uses modern backend, otherwise classic. */
const bool isModern;

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

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

/** 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;

/** When true force all windows to be repainted on next idle. */
bool needsRepaint;

@@ -80,7 +83,7 @@ struct Application::PrivateData {
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks;

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

/** Flag one window as shown, which increments @a visibleWindows.


+ 96
- 52
dpf/dgl/src/Cairo.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -32,21 +32,43 @@
// templated classes
#include "ImageBaseWidgets.cpp"

// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifndef DGL_CAIRO
# error Build config error, Cairo was NOT requested while building Cairo code
#endif
#ifdef DGL_OPENGL
# error Build config error, OpenGL requested while building Cairo code
#endif
#ifdef DGL_VULKAN
# error Build config error, Vulkan requested while building Cairo code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building Cairo code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building Cairo code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building Cairo code
#endif

START_NAMESPACE_DGL

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

static void notImplemented(const char* const name)
{
d_stderr2("cairo function not implemented: %s", 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;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

if (includeAlpha)
cairo_set_source_rgba(handle, red, green, blue, alpha);
@@ -54,7 +76,7 @@ void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
cairo_set_source_rgb(handle, red, green, blue);
}

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

template<typename T>
@@ -63,7 +85,7 @@ 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_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_line_width(handle, width);
cairo_move_to(handle, posStart.getX(), posStart.getY());
@@ -71,18 +93,13 @@ void Line<T>::draw(const GraphicsContext& context, const T width)
cairo_stroke(handle);
}

#if DGL_ALLOW_DEPRECATED_METHODS
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>;
#endif

// -----------------------------------------------------------------------
// Circle
@@ -129,7 +146,7 @@ static void drawCircle(cairo_t* const handle,
template<typename T>
void Circle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
}
@@ -139,12 +156,13 @@ void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

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

cairo_set_line_width(handle, lineWidth);
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
}

#if DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Circle<T>::draw()
{
@@ -156,13 +174,7 @@ 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>;
#endif

// -----------------------------------------------------------------------
// Triangle
@@ -190,7 +202,7 @@ static void drawTriangle(cairo_t* const handle,
template<typename T>
void Triangle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

drawTriangle<T>(handle, pos1, pos2, pos3, false);
}
@@ -200,12 +212,13 @@ 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_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

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

#if DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Triangle<T>::draw()
{
@@ -217,13 +230,7 @@ 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>;
#endif

// -----------------------------------------------------------------------
// Rectangle
@@ -244,7 +251,7 @@ void Rectangle<T>::draw(const GraphicsContext& context)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);

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

drawRectangle(handle, *this, false);
}
@@ -255,12 +262,13 @@ 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_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

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

#if DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
@@ -272,13 +280,7 @@ 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>;
#endif

// -----------------------------------------------------------------------
// CairoImage
@@ -516,7 +518,7 @@ void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noex
if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));
datarefcount = static_cast<int*>(malloc(sizeof(*datarefcount)));

surface = newsurface;
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
@@ -531,7 +533,7 @@ void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
{
DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,);

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

cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
cairo_paint(handle);
@@ -623,7 +625,7 @@ void ImageBaseKnob<CairoImage>::PrivateData::init()
template <>
void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
{
cairo_surface_destroy((cairo_surface_t*)cairoSurface);
cairo_surface_destroy(static_cast<cairo_surface_t*>(cairoSurface));
cairoSurface = nullptr;
}

@@ -672,10 +674,10 @@ template <>
void ImageBaseKnob<CairoImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;
const double normValue = getNormalizedValue();

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

if (! pData->isReady)
{
@@ -826,15 +828,57 @@ void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, u
notImplemented("Window::PrivateData::renderToPicture");
}

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

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

// -----------------------------------------------------------------------
void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
reinterpret_cast<CairoGraphicsContext&>(graphicsContext).handle = static_cast<cairo_t*>(puglGetContext(view));
}

void Window::PrivateData::endContext()
{
}

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

#ifndef DGL_GEOMETRY_CPP_INCLUDED
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 class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

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

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;
#endif

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

END_NAMESPACE_DGL

+ 9
- 1
dpf/dgl/src/Color.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -242,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept
return Color(r, g, b, alpha);
}

Color Color::fromRGB(const uint color, const float alpha) noexcept
{
return Color(static_cast<int>(color >> 24) & 0xff,
static_cast<int>(color >> 16) & 0xff,
static_cast<int>(color >> 8) & 0xff,
alpha);
}

void Color::interpolate(const Color& other, float u) noexcept
{
fixRange(u);


+ 1
- 8
dpf/dgl/src/EventHandlers.cpp View File

@@ -108,13 +108,6 @@ struct ButtonEventHandler::PrivateData {
if (! enabledInput)
return false;

// keep pressed
if (button != -1)
{
lastMotionPos = ev.pos;
return true;
}

bool ret = false;

if (widget->contains(ev.pos))
@@ -143,7 +136,7 @@ struct ButtonEventHandler::PrivateData {
}

lastMotionPos = ev.pos;
return ret;
return ret || button != -1;
}

void setActive(const bool active2, const bool sendCallback) noexcept


+ 2
- 0
dpf/dgl/src/Geometry.cpp View File

@@ -23,6 +23,8 @@

#include "../Geometry.hpp"

#define DGL_GEOMETRY_CPP_INCLUDED

#include <cmath>

START_NAMESPACE_DGL


+ 6
- 1
dpf/dgl/src/NanoVG.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding)
//#define STB_IMAGE_STATIC
#if defined(DGL_USE_GLES2)
# define NANOVG_GLES2_IMPLEMENTATION
#elif defined(DGL_USE_GLES3)
# define NANOVG_GLES3_IMPLEMENTATION
#elif defined(DGL_USE_OPENGL3)
# define NANOVG_GL3_IMPLEMENTATION
#else
@@ -1142,6 +1144,9 @@ inline void NanoBaseWidget<SubWidget>::onDisplay()
onNanoDisplay();
displayChildren();
NanoVG::endFrame();
#ifdef DGL_USE_OPENGL3
glUseProgram(reinterpret_cast<const OpenGL3GraphicsContext&>(getGraphicsContext()).program);
#endif
}
}



+ 35
- 562
dpf/dgl/src/OpenGL.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,6 @@
#endif

#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "SubWidgetPrivateData.hpp"
@@ -28,416 +27,20 @@
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

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

#ifdef DGL_USE_OPENGL3
static void notImplemented(const char* const name)
{
d_stderr2("OpenGL3 function not implemented: %s", name);
}
#else
# define DGL_USE_COMPAT_OPENGL
#endif

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

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
#ifdef DGL_USE_COMPAT_OPENGL
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
#else
notImplemented("Color::setFor");
// unused
(void)includeAlpha;
#endif
}

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

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}
#endif

template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
#ifdef DGL_USE_COMPAT_OPENGL
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

// deprecated calls
template<typename T>
void Line<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

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

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

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

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

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i=0; i<numSegments; ++i)
{
glVertex2d(x + origx, y + origy);

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

glEnd();
}
#endif

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

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

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Circle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

template<typename T>
void Circle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

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

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

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

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}
#endif

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

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

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Triangle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

template<typename T>
void Triangle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

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

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

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
}
#endif

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

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

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Rectangle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

template<typename T>
void Rectangle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

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

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

#ifdef DGL_USE_COMPAT_OPENGL
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

#ifdef DGL_USE_COMPAT_OPENGL
glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
#endif

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

OpenGLImage::OpenGLImage()
: ImageBase(),
setupCalled(false),
textureInit(false),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
}

@@ -446,6 +49,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, co
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const Ima
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image)
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage()
{
if (textureId != 0)
glDeleteTextures(1, &textureId);

#ifdef DGL_USE_GLES
std::free(convertedData);
#endif
}

void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
@@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, c
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;
@@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
return *this;
}

// deprecated calls
#if DGL_ALLOW_DEPRECATED_METHODS
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLe
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

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

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

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

template class ImageBaseButton<OpenGLImage>;

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

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
#ifdef DGL_USE_COMPAT_OPENGL
glPushMatrix();
#endif

const int w2 = w/2;
const int h2 = h/2;

#ifdef DGL_USE_COMPAT_OPENGL
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);
#endif

Rectangle<int>(-w2, -h2, w, h).draw(context);

#ifdef DGL_USE_COMPAT_OPENGL
glPopMatrix();
#endif
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

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

template class ImageBaseSlider<OpenGLImage>;

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

template class ImageBaseSwitch<OpenGLImage>;

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

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
@@ -736,7 +216,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

void TopLevelWidget::PrivateData::display()
{
@@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display()
selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor);
}

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

void Window::PrivateData::renderToPicture(const char* const filename,
const GraphicsContext&,
@@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename,
fclose(f);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

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

END_NAMESPACE_DGL

+ 585
- 0
dpf/dgl/src/OpenGL2.cpp View File

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

#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

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

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifndef DGL_OPENGL
# error Build config error, OpenGL was NOT requested while building OpenGL2 code
#endif
#ifdef DGL_CAIRO
# error Build config error, Cairo requested while building OpenGL2 code
#endif
#ifdef DGL_VULKAN
# error Build config error, Vulkan requested while building OpenGL2 code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building OpenGL2 code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building OpenGL2 code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building OpenGL2 code
#endif

// --------------------------------------------------------------------------------------------------------------------
// 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>
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}

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

#if DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Line<T>::draw()
{
drawLine<T>(posStart, posEnd);
}
#endif

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

template<typename T>
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

const double origx = static_cast<double>(pos.getX());
const double origy = static_cast<double>(pos.getY());
double t;
double x = size;
double y = 0.0;

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i = 0; i < numSegments; ++i)
{
glVertex2d(x + origx, y + origy);

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

glEnd();
}

template<typename T>
static void drawCircle(const GraphicsContext&,
const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
drawCircle<T>(pos, numSegments, size, sin, cos, outline);
}

template<typename T>
void Circle<T>::draw(const GraphicsContext& context)
{
drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, false);
}

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

glLineWidth(static_cast<GLfloat>(lineWidth));
drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, true);
}

#if DGL_ALLOW_DEPRECATED_METHODS
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);
}
#endif

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

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

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}

template<typename T>
static void drawTriangle(const GraphicsContext&,
const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
drawTriangle<T>(pos1, pos2, pos3, outline);
}

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

#if DGL_ALLOW_DEPRECATED_METHODS
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);
}
#endif

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

template<typename T>
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
}

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
drawRectangle<T>(*this, false);
}

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

glLineWidth(static_cast<GLfloat>(lineWidth));
drawRectangle<T>(*this, true);
}

#if DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
drawRectangle<T>(*this, false);
}

template<typename T>
void Rectangle<T>::drawOutline()
{
drawRectangle<T>(*this, true);
}
#endif

// --------------------------------------------------------------------------------------------------------------------
// 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;
}

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

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

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

#if DGL_ALLOW_DEPRECATED_METHODS
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);
}
#endif

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

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

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

template class ImageBaseButton<OpenGLImage>;

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

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
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 Window::PrivateData::createContextIfNeeded()
{
}

void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, static_cast<GLsizei>(size.width), static_cast<GLsizei>(size.height));
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(size.width), static_cast<GLdouble>(size.height), 0.0, 0.0, 1.0);
glViewport(0, 0, static_cast<GLsizei>(size.width), static_cast<GLsizei>(size.height));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void Window::PrivateData::endContext()
{
}

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

#ifndef DGL_GEOMETRY_CPP_INCLUDED
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 class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

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

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;
#endif

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

END_NAMESPACE_DGL

+ 1033
- 0
dpf/dgl/src/OpenGL3.cpp
File diff suppressed because it is too large
View File


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

@@ -135,11 +135,6 @@ bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev)
return selfw->pData->giveScrollEventForSubWidgets(rev);
}

void TopLevelWidget::PrivateData::fallbackOnResize(const uint width, const uint height)
{
puglFallbackOnResize(window.pData->view, width, height);
}

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

END_NAMESPACE_DGL

+ 0
- 1
dpf/dgl/src/TopLevelWidgetPrivateData.hpp View File

@@ -38,7 +38,6 @@ struct TopLevelWidget::PrivateData {
bool mouseEvent(const MouseEvent& ev);
bool motionEvent(const MotionEvent& ev);
bool scrollEvent(const ScrollEvent& ev);
void fallbackOnResize(uint width, uint height);

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
};


+ 39
- 36
dpf/dgl/src/Window.cpp View File

@@ -28,7 +28,11 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win)
: window(win),
ppData(nullptr),
active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)),
reenter(false) {}
reenter(false)
{
if (active)
window.pData->createContextIfNeeded();
}

Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin)
: window(win),
@@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi
{
puglBackendLeave(ppData->view);
active = puglBackendEnter(window.pData->view);
if (active)
window.pData->createContextIfNeeded();
}
}

@@ -180,22 +186,22 @@ int Window::getOffsetX() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

return puglGetFrame(pData->view).x;
return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).x;
}

int Window::getOffsetY() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

return puglGetFrame(pData->view).y;
return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y;
}

Point<int> Window::getOffset() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>());

const PuglRect rect = puglGetFrame(pData->view);
return Point<int>(rect.x, rect.y);
const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION);
return Point<int>(pos.x, pos.y);
}

void Window::setOffsetX(const int x)
@@ -214,7 +220,7 @@ void Window::setOffset(const int x, const int y)
DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,);

if (pData->view != nullptr)
puglSetPosition(pData->view, x, y);
puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y);
}

void Window::setOffset(const Point<int>& offset)
@@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

const double width = puglGetFrame(pData->view).width;
DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0);
return static_cast<uint>(width + 0.5);
const PuglSpan width = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).width;
DISTRHO_SAFE_ASSERT(width > 0);
return width;
}

uint Window::getHeight() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

const double height = puglGetFrame(pData->view).height;
DISTRHO_SAFE_ASSERT_RETURN(height > 0.0, 0);
return static_cast<uint>(height + 0.5);
const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height;
DISTRHO_SAFE_ASSERT(height > 0);
return height;
}

Size<uint> Window::getSize() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>());

const PuglRect rect = puglGetFrame(pData->view);
DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size<uint>());
DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size<uint>());
return Size<uint>(static_cast<uint>(rect.width + 0.5),
static_cast<uint>(rect.height + 0.5));
const PuglArea size = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE);
DISTRHO_SAFE_ASSERT(size.width > 0);
DISTRHO_SAFE_ASSERT(size.height > 0);
return Size<uint>(size.width, size.height);
}

void Window::setWidth(const uint width)
@@ -392,12 +397,10 @@ Application& Window::getApp() const noexcept
return pData->app;
}

#ifndef DPF_TEST_WINDOW_CPP
const GraphicsContext& Window::getGraphicsContext() const noexcept
{
return pData->getGraphicsContext();
}
#endif

uintptr_t Window::getNativeWindowHandle() const noexcept
{
@@ -443,7 +446,7 @@ void Window::repaint() noexcept
if (pData->usesScheduledRepaints)
pData->appData->needsRepaint = true;

puglPostRedisplay(pData->view);
puglObscureView(pData->view);
}

void Window::repaint(const Rectangle<uint>& rect) noexcept
@@ -454,22 +457,22 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept
if (pData->usesScheduledRepaints)
pData->appData->needsRepaint = true;

PuglRect prect = {
static_cast<PuglCoord>(rect.getX()),
static_cast<PuglCoord>(rect.getY()),
static_cast<PuglSpan>(rect.getWidth()),
static_cast<PuglSpan>(rect.getHeight()),
};
int x = static_cast<int>(rect.getX());
int y = static_cast<int>(rect.getY());
uint width = rect.getWidth();
uint height = rect.getHeight();

if (pData->autoScaling)
{
const double autoScaleFactor = pData->autoScaleFactor;

prect.x = static_cast<PuglCoord>(prect.x * autoScaleFactor);
prect.y = static_cast<PuglCoord>(prect.y * autoScaleFactor);
prect.width = static_cast<PuglSpan>(prect.width * autoScaleFactor + 0.5);
prect.height = static_cast<PuglSpan>(prect.height * autoScaleFactor + 0.5);
x = d_roundToIntPositive(x * autoScaleFactor);
y = d_roundToIntPositive(y * autoScaleFactor);
width = d_roundToUnsignedInt(width * autoScaleFactor);
height = d_roundToUnsignedInt(height * autoScaleFactor);
}
puglPostRedisplayRect(pData->view, prect);

puglObscureRegion(pData->view, x, y, width, height);
}

void Window::renderToPicture(const char* const filename)
@@ -523,8 +526,8 @@ void Window::setGeometryConstraints(uint minimumWidth,
{
const Size<uint> size(getSize());

setSize(static_cast<uint>(size.getWidth() * scaleFactor + 0.5),
static_cast<uint>(size.getHeight() * scaleFactor + 0.5));
setSize(d_roundToUnsignedInt(size.getWidth() * scaleFactor),
d_roundToUnsignedInt(size.getHeight() * scaleFactor));
}
}

@@ -578,11 +581,11 @@ void Window::onFocus(bool, CrossingMode)
{
}

void Window::onReshape(const uint width, const uint height)
#if DGL_ALLOW_DEPRECATED_METHODS
void Window::onReshape(uint, uint)
{
if (pData->view != nullptr)
puglFallbackOnResize(pData->view, width, height);
}
#endif

void Window::onScaleFactorChanged(double)
{


+ 44
- 29
dpf/dgl/src/WindowPrivateData.cpp View File

@@ -91,10 +91,10 @@ static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintp

if (PuglView* const view = puglNewView(world))
{
puglSetParentWindow(view, parentWindowHandle);
puglSetParent(view, parentWindowHandle);

if (parentWindowHandle != 0)
puglSetPosition(view, 0, 0);
puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0);

return view;
}
@@ -268,6 +268,7 @@ Window::PrivateData::~PrivateData()
isVisible = false;
}

destroyContext();
puglFreeView(view);
}

@@ -300,8 +301,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo
// PUGL_SAMPLES ??
puglSetEventFunc(view, puglEventCallback);

// setting default size triggers system-level calls, do it last
puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height));
// setting size triggers system-level calls, do it last
puglSetSizeAndDefault(view, width, height);
}

bool Window::PrivateData::initPost()
@@ -366,10 +367,6 @@ void Window::PrivateData::show()
isClosed = false;
appData->oneWindowShown();

// FIXME
// PuglRect rect = puglGetFrame(view);
// puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));

#if defined(DISTRHO_OS_WINDOWS)
puglWin32ShowCentered(view);
#elif defined(DISTRHO_OS_MAC)
@@ -453,6 +450,13 @@ void Window::PrivateData::setResizable(const bool resizable)
puglSetResizable(view, resizable);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return reinterpret_cast<const GraphicsContext&>(graphicsContext);
}

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

void Window::PrivateData::idleCallback()
@@ -538,9 +542,9 @@ bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPA
if (webViewHandle != nullptr)
webViewDestroy(webViewHandle);

const PuglRect rect = puglGetFrame(view);
uint initialWidth = static_cast<uint>(rect.width) - options.offset.x;
uint initialHeight = static_cast<uint>(rect.height) - options.offset.y;
const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
uint initialWidth = size.width - options.offset.x;
uint initialHeight = size.height - options.offset.y;

webViewOffset = Point<int>(options.offset.x, options.offset.y);

@@ -640,6 +644,8 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height)

DGL_DBGp("PUGL: onReshape : %d %d\n", width, height);

createContextIfNeeded();

if (autoScaling)
{
const double scaleHorizontal = width / static_cast<double>(minWidth);
@@ -662,9 +668,27 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height)
autoScaling ? autoScaleFactor : scaleFactor);
#endif

#if DGL_ALLOW_DEPRECATED_METHODS
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
self->onReshape(uwidth, uheight);
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic pop
#endif
#endif

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET(it)
{
TopLevelWidget* const widget = *it;
@@ -679,10 +703,9 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height)
*/
((Widget*)widget)->setSize(uwidth, uheight);
}
#endif

// always repaint after a resize
puglPostRedisplay(view);
puglObscureView(view);
}

void Window::PrivateData::onPuglExpose()
@@ -691,7 +714,8 @@ void Window::PrivateData::onPuglExpose()

puglOnDisplayPrepare(view);

#ifndef DPF_TEST_WINDOW_CPP
startContext();

FOR_EACH_TOP_LEVEL_WIDGET(it)
{
TopLevelWidget* const widget(*it);
@@ -702,12 +726,13 @@ void Window::PrivateData::onPuglExpose()

if (char* const filename = filenameToRenderInto)
{
const PuglRect rect = puglGetFrame(view);
const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
filenameToRenderInto = nullptr;
renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height));
renderToPicture(filename, getGraphicsContext(), size.width, size.height);
std::free(filename);
}
#endif

endContext();
}

void Window::PrivateData::onPuglClose()
@@ -760,7 +785,6 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
if (modal.child != nullptr)
return modal.child->focus();

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);
@@ -768,7 +792,6 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
if (widget->isVisible() && widget->onKeyboard(ev))
break;
}
#endif
}

void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
@@ -778,7 +801,6 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
if (modal.child != nullptr)
return modal.child->focus();

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);
@@ -786,7 +808,6 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
if (widget->isVisible() && widget->onCharacterInput(ev))
break;
}
#endif
}

void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
@@ -796,7 +817,6 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
if (modal.child != nullptr)
return modal.child->focus();

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);
@@ -804,7 +824,6 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
if (widget->isVisible() && widget->onMouse(ev))
break;
}
#endif
}

void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
@@ -814,7 +833,6 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
if (modal.child != nullptr)
return modal.child->focus();

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);
@@ -822,7 +840,6 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
if (widget->isVisible() && widget->onMotion(ev))
break;
}
#endif
}

void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
@@ -832,7 +849,6 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
if (modal.child != nullptr)
return modal.child->focus();

#ifndef DPF_TEST_WINDOW_CPP
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);
@@ -840,7 +856,6 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
if (widget->isVisible() && widget->onScroll(ev))
break;
}
#endif
}

const void* Window::PrivateData::getClipboard(size_t& dataSize)
@@ -978,7 +993,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)));
#endif
#ifdef DGL_USING_X11
puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
puglX11SetWindowType(view, pData->appData->isStandalone);
#endif
}
break;


+ 5
- 1
dpf/dgl/src/WindowPrivateData.hpp View File

@@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback {
PuglView* view;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];
mutable uint8_t graphicsContext[sizeof(int) * 9];
void createContextIfNeeded();
void destroyContext();
void startContext();
void endContext();

/** The top-level widgets associated with this Window. */
std::list<TopLevelWidget*> topLevelWidgets;


+ 24
- 20
dpf/dgl/src/nanovg/nanovg_gl.h View File

@@ -173,7 +173,7 @@ struct GLNVGtexture {
int width, height;
int type;
int flags;
#if defined NANOVG_GLES2
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
unsigned char* data;
#endif
};
@@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
switch (type)
{
case NVG_TEXTURE_BGR:
#if NANOVG_GLES2
// GLES2 cannot handle GL_BGR, do local conversion to GL_RGB
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// GLES cannot handle GL_BGR, do local conversion to GL_RGB
tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h);
for (uint32_t i=0; i<w*h; ++i)
for (int i = 0; i < w * h; ++i)
{
tex->data[i*3+0] = data[i*3+2];
tex->data[i*3+1] = data[i*3+1];
@@ -829,34 +829,34 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
#endif
break;
case NVG_TEXTURE_BGRA:
#if NANOVG_GLES2
// GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// GLES cannot handle GL_BGRA, do local conversion to GL_RGBA
tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h);
for (uint32_t i=0; i<w*h; ++i)
for (int i = 0; i < w * h; ++i)
{
tex->data[i*3+0] = data[i*3+3];
tex->data[i*3+1] = data[i*3+2];
tex->data[i*3+2] = data[i*3+1];
tex->data[i*3+3] = data[i*3+0];
tex->data[i*4+0] = data[i*4+2];
tex->data[i*4+1] = data[i*4+1];
tex->data[i*4+2] = data[i*4+0];
tex->data[i*4+3] = data[i*4+3];
}
data = tex->data;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
#endif
break;
case NVG_TEXTURE_RGB:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGBA:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
break;
default:
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2)
#if defined(NANOVG_GL2) || defined(NANOVG_GLES2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#elif defined(NANOVG_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#endif
break;
@@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w
switch (tex->type)
{
case NVG_TEXTURE_BGR:
#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3))
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
#endif
case NVG_TEXTURE_RGB:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3))
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
#endif
case NVG_TEXTURE_RGBA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
break;
default:
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2)
#if defined(NANOVG_GL2) || defined(NANOVG_GLES2)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);


+ 1
- 1
dpf/dgl/src/pugl-extra/haiku.cpp View File

@@ -246,7 +246,7 @@ puglPostRedisplayRect(PuglView* const view, const PuglRect rect)
}

PuglNativeView
puglGetNativeView(PuglView* const view)
puglGetNativeView(const PuglView* const view)
{
return 0;
}


+ 93
- 35
dpf/dgl/src/pugl-extra/wasm.c View File

@@ -1,10 +1,11 @@
// Copyright 2012-2022 David Robillard <d@drobilla.net>
// Copyright 2021-2022 Filipe Coelho <falktx@falktx.com>
// Copyright 2021-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

#include "wasm.h"

#include "../pugl-upstream/src/internal.h"
#include "../pugl-upstream/src/platform.h"

#include <stdio.h>

@@ -82,6 +83,73 @@ puglInitViewInternals(PuglWorld* const world)
return impl;
}

PuglStatus
puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint))
{
// No fine-grained updates, hints are always recalculated together
return puglUpdateSizeHints(view);
}

PuglStatus
puglUpdateSizeHints(PuglView* const view)
{
const char* const className = view->world->strings[PUGL_CLASS_NAME];

if (!view->hints[PUGL_RESIZABLE]) {
PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
if (!puglIsValidSize(size.width, size.height)) {
size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE);
}
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
var width = parseInt($1 / window.devicePixelRatio);
var height = parseInt($2 / window.devicePixelRatio);
canvasWrapper.style.setProperty("min-width", width + 'px');
canvasWrapper.style.setProperty("max-width", width + 'px');
canvasWrapper.style.setProperty("min-height", height + 'px');
canvasWrapper.style.setProperty("max-height", height + 'px');
}, className, size.width, size.height);
} else {
const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE];
if (puglIsValidArea(minSize)) {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, minSize.width, minSize.height);
} else {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.removeProperty("min-width");
canvasWrapper.style.removeProperty("min-height");
}, className);
}

const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE];
if (puglIsValidArea(maxSize)) {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("max-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("max-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, maxSize.width, maxSize.height);
} else {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.removeProperty("max-width");
canvasWrapper.style.removeProperty("max-height");
}, className);
}

/* TODO
const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT];
const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT];
*/
}

return PUGL_SUCCESS;
}

static PuglStatus
puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event)
{
@@ -729,7 +797,8 @@ puglRealize(PuglView* const view)
const char* const className = view->world->strings[PUGL_CLASS_NAME];
d_stdout("className is %s", className);

PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION];
const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
if (!defaultSize.width || !defaultSize.height) {
return PUGL_BAD_CONFIGURATION;
}
@@ -747,8 +816,8 @@ puglRealize(PuglView* const view)
puglDispatchSimpleEvent(view, PUGL_REALIZE);

PuglEvent event = {{PUGL_CONFIGURE, 0}};
event.configure.x = view->defaultX;
event.configure.y = view->defaultY;
event.configure.x = defaultPos.x;
event.configure.y = defaultPos.y;
event.configure.width = defaultSize.width;
event.configure.height = defaultSize.height;
puglDispatchEvent(view, &event);
@@ -760,6 +829,8 @@ puglRealize(PuglView* const view)
canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio);
}, className);

puglUpdateSizeHints(view);

emscripten_set_canvas_element_size(className, defaultSize.width, defaultSize.height);
#ifndef PUGL_WASM_NO_KEYBOARD_INPUT
// emscripten_set_keypress_callback(className, view, false, puglKeyCallback);
@@ -794,7 +865,7 @@ puglShow(PuglView* const view, PuglShowCommand)
{
view->impl->visible = true;
view->impl->needsRepaint = true;
return puglPostRedisplay(view);
return puglObscureView(view);
}

PuglStatus
@@ -884,21 +955,29 @@ puglUpdate(PuglWorld* const world, const double timeout)
}

PuglStatus
puglPostRedisplay(PuglView* const view)
puglObscureView(PuglView* const view)
{
view->impl->needsRepaint = true;
return PUGL_SUCCESS;
}

PuglStatus
puglPostRedisplayRect(PuglView* const view, const PuglRect rect)
puglObscureRegion(PuglView* view,
const int x,
const int y,
const unsigned width,
const unsigned height)
{
if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

view->impl->needsRepaint = true;
return PUGL_FAILURE;
}

PuglNativeView
puglGetNativeView(PuglView* const view)
puglGetNativeView(const PuglView* const view)
{
return 0;
}
@@ -918,13 +997,13 @@ puglViewStringChanged(PuglView*, const PuglStringHint key, const char* const val
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
puglSetWindowSize(PuglView* const view, const unsigned width, const unsigned height)
{
view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;
if (view->impl->created) {
const char* const className = view->world->strings[PUGL_CLASS_NAME];
emscripten_set_canvas_element_size(className, width, height);
}

return PUGL_SUCCESS;
}

@@ -1152,24 +1231,3 @@ puglSetTransientParent(PuglView* const view, const PuglNativeView parent)
view->transientParent = parent;
return PUGL_FAILURE;
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
{
printf("TODO: %s %d\n", __func__, __LINE__);

if (x > INT16_MAX || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->created) {
// Set defaults to be used when realized
view->defaultX = x;
view->defaultY = y;
return PUGL_SUCCESS;
}

view->lastConfigure.x = (PuglCoord)x;
view->lastConfigure.y = (PuglCoord)y;
return puglPostRedisplay(view);
}

+ 9
- 8
dpf/dgl/src/pugl-upstream/.clang-format View File

@@ -1,10 +1,17 @@
# Copyright 2020-2022 David Robillard <d@drobilla.net>
# Copyright 2020-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC

---
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: true
AlignEscapedNewlines: Left
AttributeMacros:
- PUGL_API
- PUGL_CONST_API
- PUGL_CONST_FUNC
- PUGL_MALLOC_API
- PUGL_MALLOC_FUNC
- PUGL_WARN_UNUSED_RESULT
BasedOnStyle: Mozilla
BraceWrapping:
AfterNamespace: false
@@ -22,11 +29,5 @@ IndentPPDirectives: AfterHash
KeepEmptyLinesAtTheStartOfBlocks: false
SpacesInContainerLiterals: false
StatementMacros:
- PUGL_API
- PUGL_CONST_API
- PUGL_CONST_FUNC
- PUGL_DEPRECATED_BY
- PUGL_UNUSED
- PUGL_WARN_UNUSED_RESULT
- _Pragma
...

+ 4
- 0
dpf/dgl/src/pugl-upstream/.clang-format-ignore View File

@@ -0,0 +1,4 @@
# Copyright 2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC

examples/glad/

+ 4
- 3
dpf/dgl/src/pugl-upstream/.clang-tidy View File

@@ -1,4 +1,4 @@
# Copyright 2020-2024 David Robillard <d@drobilla.net>
# Copyright 2020-2025 David Robillard <d@drobilla.net>
# SPDX-License-Identifier: 0BSD OR ISC

Checks: >
@@ -13,10 +13,11 @@ Checks: >
-misc-include-cleaner,
-readability-avoid-nested-conditional-operator,
-readability-identifier-length,
-readability-implicit-bool-conversion,
CheckOptions:
- key: hicpp-uppercase-literal-suffix.NewSuffixes
- key: hicpp-uppercase-literal-suffix.NewSuffixes
value: L;U;f
- key: readability-uppercase-literal-suffix.NewSuffixes
- key: readability-uppercase-literal-suffix.NewSuffixes
value: L;U;f
FormatStyle: file
HeaderFilterRegex: 'pugl/.*'

+ 0
- 4
dpf/dgl/src/pugl-upstream/.clant.json View File

@@ -1,9 +1,5 @@
{
"version": "1.0.0",
"include_dirs": [
"bindings/cpp/include",
"include"
],
"exclude_patterns": [
"glad\\.c"
],


+ 1
- 1
dpf/dgl/src/pugl-upstream/.reuse/dep5 View File

@@ -23,6 +23,6 @@ Copyright: 2008-2018 The Khronos Group Inc.
License: MIT

Files: doc/*.rst resources/pugl.ipe resources/pugl.png resources/pugl.svg
Copyright: 2021-2023 David Robillard <d@drobilla.net>
Copyright: 2021-2025 David Robillard <d@drobilla.net>
Comment: Documentation
License: ISC

+ 1
- 5
dpf/dgl/src/pugl-upstream/README.md View File

@@ -39,11 +39,7 @@ Stability
Pugl is currently being developed towards a long-term stable API. For the time
being, however, the API may break occasionally. Please report any relevant
feedback, or file feature requests, so that we can ensure that the released API
is stable for as long as possible.

When the API changes, backwards compatibility is maintained where possible.
These compatibility shims will be removed before release, so users are
encouraged to build with `PUGL_DISABLE_DEPRECATED` defined.
will be stable for as long as possible.

Documentation
-------------


+ 2
- 17
dpf/dgl/src/pugl-upstream/include/pugl/attributes.h View File

@@ -26,17 +26,6 @@
# endif
#endif

// Deprecated API
#ifndef PUGL_DISABLE_DEPRECATED
# if defined(__clang__)
# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("", rep)))
# elif defined(__GNUC__)
# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("Use " rep)))
# else
# define PUGL_DEPRECATED_BY(rep)
# endif
#endif

// GCC function attributes
#if defined(__GNUC__)
# define PUGL_CONST_FUNC __attribute__((const))
@@ -47,13 +36,9 @@
#endif

/// A const function in the public API that only reads parameters
#define PUGL_CONST_API \
PUGL_API \
PUGL_CONST_FUNC
#define PUGL_CONST_API PUGL_API PUGL_CONST_FUNC

/// A malloc function in the public API that returns allocated memory
#define PUGL_MALLOC_API \
PUGL_API \
PUGL_MALLOC_FUNC
#define PUGL_MALLOC_API PUGL_API PUGL_MALLOC_FUNC

#endif // PUGL_ATTRIBUTES_H

+ 3
- 4
dpf/dgl/src/pugl-upstream/include/pugl/cairo.h View File

@@ -4,8 +4,8 @@
#ifndef PUGL_CAIRO_H
#define PUGL_CAIRO_H

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

PUGL_BEGIN_DECLS

@@ -21,8 +21,7 @@ PUGL_BEGIN_DECLS

Pass the returned value to puglSetBackend() to draw to a view with Cairo.
*/
PUGL_CONST_API
const PuglBackend*
PUGL_CONST_API const PuglBackend*
puglCairoBackend(void);

/**


+ 6
- 10
dpf/dgl/src/pugl-upstream/include/pugl/gl.h View File

@@ -4,8 +4,8 @@
#ifndef PUGL_GL_H
#define PUGL_GL_H

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

// IWYU pragma: begin_exports

@@ -42,8 +42,7 @@ typedef void (*PuglGlFunc)(void);
/**
Return the address of an OpenGL extension function.
*/
PUGL_API
PuglGlFunc
PUGL_API PuglGlFunc
puglGetProcAddress(const char* name);

/**
@@ -53,8 +52,7 @@ puglGetProcAddress(const char* name);
doing things like loading textures. Note that this must not be used for
drawing, which may only be done while processing an expose event.
*/
PUGL_API
PuglStatus
PUGL_API PuglStatus
puglEnterContext(PuglView* view);

/**
@@ -62,8 +60,7 @@ puglEnterContext(PuglView* view);

This must only be called after puglEnterContext().
*/
PUGL_API
PuglStatus
PUGL_API PuglStatus
puglLeaveContext(PuglView* view);

/**
@@ -71,8 +68,7 @@ puglLeaveContext(PuglView* view);

Pass the returned value to puglSetBackend() to draw to a view with OpenGL.
*/
PUGL_CONST_API
const PuglBackend*
PUGL_CONST_API const PuglBackend*
puglGlBackend(void);

PUGL_END_DECLS


+ 282
- 795
dpf/dgl/src/pugl-upstream/include/pugl/pugl.h
File diff suppressed because it is too large
View File


+ 3
- 4
dpf/dgl/src/pugl-upstream/include/pugl/stub.h View File

@@ -4,8 +4,8 @@
#ifndef PUGL_STUB_H
#define PUGL_STUB_H

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

PUGL_BEGIN_DECLS

@@ -22,8 +22,7 @@ PUGL_BEGIN_DECLS
This backend just creates a simple native window without setting up any
portable graphics API.
*/
PUGL_CONST_API
const PuglBackend*
PUGL_CONST_API const PuglBackend*
puglStubBackend(void);

/**


+ 9
- 16
dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h View File

@@ -10,8 +10,8 @@
#ifndef PUGL_VULKAN_H
#define PUGL_VULKAN_H

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

#include <vulkan/vulkan_core.h>

@@ -70,8 +70,7 @@ typedef struct PuglVulkanLoaderImpl PuglVulkanLoader;

@return A new Vulkan loader, or null on failure.
*/
PUGL_API
PuglVulkanLoader*
PUGL_API PuglVulkanLoader*
puglNewVulkanLoader(PuglWorld* world, const char* libraryName);

/**
@@ -80,8 +79,7 @@ puglNewVulkanLoader(PuglWorld* world, const char* libraryName);
Note that this closes the Vulkan library, so no Vulkan objects or API may be
used after this is called.
*/
PUGL_API
void
PUGL_API void
puglFreeVulkanLoader(PuglVulkanLoader* loader);

/**
@@ -90,8 +88,7 @@ puglFreeVulkanLoader(PuglVulkanLoader* loader);
@return Null if the Vulkan library does not contain this function (which is
unlikely and indicates a broken system).
*/
PUGL_API
PFN_vkGetInstanceProcAddr
PUGL_API PFN_vkGetInstanceProcAddr
puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader);

/**
@@ -100,8 +97,7 @@ puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader);
@return Null if the Vulkan library does not contain this function (which is
unlikely and indicates a broken system).
*/
PUGL_API
PFN_vkGetDeviceProcAddr
PUGL_API PFN_vkGetDeviceProcAddr
puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader);

/**
@@ -113,8 +109,7 @@ puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader);
@param[out] count The number of extensions in the returned array.
@return An array of extension name strings.
*/
PUGL_API
const char* const*
PUGL_API const char* const*
puglGetInstanceExtensions(uint32_t* count);

/**
@@ -127,8 +122,7 @@ puglGetInstanceExtensions(uint32_t* count);
@param[out] surface Pointed to a newly created Vulkan surface.
@return `VK_SUCCESS` on success, or a Vulkan error code.
*/
PUGL_API
VkResult
PUGL_API VkResult
puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr,
PuglView* view,
VkInstance instance,
@@ -140,8 +134,7 @@ puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr,

Pass the returned value to puglSetBackend() to draw to a view with Vulkan.
*/
PUGL_CONST_API
const PuglBackend*
PUGL_CONST_API const PuglBackend*
puglVulkanBackend(void);

/**


+ 0
- 1
dpf/dgl/src/pugl-upstream/src/.clang-tidy View File

@@ -4,7 +4,6 @@
Checks: >
-bugprone-easily-swappable-parameters,
-bugprone-multi-level-implicit-pointer-conversion,
-clang-analyzer-optin.core.EnumCastOutOfRange,
-hicpp-multiway-paths-covered,
-hicpp-signed-bitwise,
-llvm-header-guard,


+ 98
- 53
dpf/dgl/src/pugl-upstream/src/common.c View File

@@ -4,13 +4,11 @@
// Common implementations of public API functions in the core library

#include "internal.h"

#include "platform.h"
#include "types.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -27,6 +25,7 @@ puglStrerror(const PuglStatus status)
case PUGL_BAD_BACKEND: return "Invalid or missing backend";
case PUGL_BAD_CONFIGURATION: return "Invalid view configuration";
case PUGL_BAD_PARAMETER: return "Invalid parameter";
case PUGL_BAD_CALL: return "Invalid call";
case PUGL_BACKEND_FAILED: return "Backend initialisation failed";
case PUGL_REGISTRATION_FAILED: return "Class registration failed";
case PUGL_REALIZE_FAILED: return "View creation failed";
@@ -106,27 +105,37 @@ puglGetWorldString(const PuglWorld* const world, const PuglStringHint key)
}

static void
puglSetDefaultHints(PuglHints hints)
puglSetDefaultHints(PuglView* const view)
{
hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API;
hints[PUGL_CONTEXT_VERSION_MAJOR] = 2;
hints[PUGL_CONTEXT_VERSION_MINOR] = 0;
hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE;
hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE;
hints[PUGL_RED_BITS] = 8;
hints[PUGL_GREEN_BITS] = 8;
hints[PUGL_BLUE_BITS] = 8;
hints[PUGL_ALPHA_BITS] = 8;
hints[PUGL_DEPTH_BITS] = 0;
hints[PUGL_STENCIL_BITS] = 0;
hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE;
hints[PUGL_SAMPLES] = 0;
hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE;
hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE;
hints[PUGL_RESIZABLE] = PUGL_FALSE;
hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE;
hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE;
hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE;
view->hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API;
view->hints[PUGL_CONTEXT_VERSION_MAJOR] = 2;
view->hints[PUGL_CONTEXT_VERSION_MINOR] = 0;
view->hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE;
view->hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE;
view->hints[PUGL_RED_BITS] = 8;
view->hints[PUGL_GREEN_BITS] = 8;
view->hints[PUGL_BLUE_BITS] = 8;
view->hints[PUGL_ALPHA_BITS] = 8;
view->hints[PUGL_DEPTH_BITS] = 0;
view->hints[PUGL_STENCIL_BITS] = 0;
view->hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE;
view->hints[PUGL_SAMPLES] = 0;
view->hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE;
view->hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE;
view->hints[PUGL_RESIZABLE] = PUGL_FALSE;
view->hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE;
view->hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE;
view->hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE;

for (unsigned i = 0U; i < PUGL_NUM_POSITION_HINTS; ++i) {
view->positionHints[i].x = INT16_MIN;
view->positionHints[i].y = INT16_MIN;
}

for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) {
view->sizeHints[i].width = 0U;
view->sizeHints[i].height = 0U;
}
}

PuglView*
@@ -138,13 +147,8 @@ puglNewView(PuglWorld* const world)
return NULL;
}

view->world = world;
view->sizeHints[PUGL_MIN_SIZE].width = 1;
view->sizeHints[PUGL_MIN_SIZE].height = 1;
view->defaultX = INT_MIN;
view->defaultY = INT_MIN;

puglSetDefaultHints(view->hints);
view->world = world;
puglSetDefaultHints(view);

// Enlarge world view list
const size_t newNumViews = world->numViews + 1U;
@@ -287,43 +291,84 @@ puglGetViewString(const PuglView* const view, const PuglStringHint key)
return view->strings[key];
}

PuglRect
puglGetFrame(const PuglView* view)
PuglPoint
puglGetPositionHint(const PuglView* const view, const PuglPositionHint hint)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Return the last configured frame
const PuglRect frame = {view->lastConfigure.x,
view->lastConfigure.y,
view->lastConfigure.width,
view->lastConfigure.height};
return frame;
if (hint == PUGL_CURRENT_POSITION) {
PuglPoint pos = {0, 0};
if (view->lastConfigure.type == PUGL_CONFIGURE) {
pos.x = view->lastConfigure.x;
pos.y = view->lastConfigure.y;
} else {
const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION];
if (puglIsValidPosition(defaultPos.x, defaultPos.y)) {
pos.x = defaultPos.x;
pos.y = defaultPos.y;
}
}
return pos;
}

// Get the default position if set, or fallback to (0, 0)
int x = view->defaultX;
int y = view->defaultY;
if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) {
x = 0;
y = 0;
return view->positionHints[hint];
}

PuglStatus
puglSetPositionHint(PuglView* const view,
const PuglPositionHint hint,
const int x,
const int y)
{
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

// Return the default frame, sanitized if necessary
const PuglRect frame = {(PuglCoord)x,
(PuglCoord)y,
view->sizeHints[PUGL_DEFAULT_SIZE].width,
view->sizeHints[PUGL_DEFAULT_SIZE].height};
return frame;
if (x <= INT16_MIN || x > INT16_MAX || y <= INT16_MIN || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

view->positionHints[hint].x = (PuglCoord)x;
view->positionHints[hint].y = (PuglCoord)y;

return (hint != PUGL_CURRENT_POSITION) ? PUGL_SUCCESS
: puglGetNativeView(view) ? puglSetWindowPosition(view, x, y)
: PUGL_FAILURE;
}

PuglArea
puglGetSizeHint(const PuglView* const view, const PuglSizeHint hint)
{
if (hint == PUGL_CURRENT_SIZE && view->lastConfigure.type == PUGL_CONFIGURE) {
const PuglArea area = {view->lastConfigure.width,
view->lastConfigure.height};
return area;
}

return view->sizeHints[hint];
}

PuglStatus
puglSetParentWindow(PuglView* view, PuglNativeView parent)
puglSetParent(PuglView* view, PuglNativeView parent)
{
view->parent = parent;
return PUGL_SUCCESS;
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const unsigned width,
const unsigned height)
{
const PuglStatus st = puglStoreSizeHint(view, hint, width, height);

return st ? st
: (hint != PUGL_CURRENT_SIZE) ? puglApplySizeHint(view, hint)
: puglGetNativeView(view) ? puglSetWindowSize(view, width, height)
: PUGL_FAILURE;
}

PuglNativeView
puglGetParentWindow(const PuglView* const view)
puglGetParent(const PuglView* const view)
{
return view->parent;
}


+ 95
- 34
dpf/dgl/src/pugl-upstream/src/internal.c View File

@@ -1,23 +1,84 @@
// Copyright 2012-2023 David Robillard <d@drobilla.net>
// Copyright 2012-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC

#include "internal.h"

#include "types.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

static PuglPoint
make_point(const PuglCoord x, const PuglCoord y)
{
const PuglPoint point = {x, y};
return point;
}

bool
puglIsValidPosition(const int x, const int y)
{
// INT16_MIN is a sentinel, INT16_MAX is impossible with non-zero size
return x > INT16_MIN && x < INT16_MAX && y > INT16_MIN && y < INT16_MAX;
}

bool
puglIsValidSize(const PuglViewSize size)
puglIsValidSize(const unsigned width, const unsigned height)
{
return width && height && width <= INT16_MAX && height <= INT16_MAX;
}

bool
puglIsValidArea(const PuglArea size)
{
return size.width && size.height;
}

PuglArea
puglGetInitialSize(const PuglView* const view)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the last configured size
const PuglConfigureEvent config = view->lastConfigure;
const PuglArea size = {config.width, config.height};
return size;
}

// Use the default size hint set by the application
return view->sizeHints[PUGL_DEFAULT_SIZE];
}

PuglPoint
puglGetInitialPosition(const PuglView* const view, const PuglArea size)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the last configured frame
return make_point(view->lastConfigure.x, view->lastConfigure.y);
}

const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION];
if (puglIsValidPosition(defaultPos.x, defaultPos.y)) {
// Use the default position hint set by the application
return make_point(defaultPos.x, defaultPos.y);
}

if (view->parent) {
// Default to the top/left origin of the parent
return make_point(0, 0);
}

// Center frame on a transient ancestor, or failing that, the screen
const PuglPoint center = puglGetAncestorCenter(view);
const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)),
(PuglCoord)(center.y - (size.height / 2))};
return pos;
}

void
puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value)
{
@@ -68,6 +129,25 @@ puglSetString(char** dest, const char* string)
}
}

PuglStatus
puglStoreSizeHint(PuglView* const view,
const PuglSizeHint hint,
const unsigned width,
const unsigned height)
{
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

if (!puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

view->sizeHints[hint].width = (PuglSpan)width;
view->sizeHints[hint].height = (PuglSpan)height;
return PUGL_SUCCESS;
}

uint32_t
puglDecodeUTF8(const uint8_t* buf)
{
@@ -159,11 +239,12 @@ puglPreRealize(PuglView* const view)
}

// Ensure that the default size is set to a valid size
if (!puglIsValidSize(view->sizeHints[PUGL_DEFAULT_SIZE])) {
if (!puglIsValidArea(view->sizeHints[PUGL_DEFAULT_SIZE])) {
return PUGL_BAD_CONFIGURATION;
}

return PUGL_SUCCESS;
return (view->world->state == PUGL_WORLD_EXPOSING) ? PUGL_BAD_CALL
: PUGL_SUCCESS;
}

PuglStatus
@@ -173,30 +254,10 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type)
type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER ||
type == PUGL_LOOP_LEAVE);

const PuglEvent event = {{type, 0}};
const PuglEvent event = {{type, 0U}};
return puglDispatchEvent(view, &event);
}

static inline bool
puglMustConfigure(PuglView* view, const PuglConfigureEvent* configure)
{
return !!memcmp(configure, &view->lastConfigure, sizeof(PuglConfigureEvent));
}

PuglStatus
puglConfigure(PuglView* view, const PuglEvent* event)
{
PuglStatus st = PUGL_SUCCESS;

assert(event->type == PUGL_CONFIGURE);
if (puglMustConfigure(view, &event->configure)) {
st = view->eventFunc(view, event);
view->lastConfigure = event->configure;
}

return st;
}

PuglStatus
puglDispatchEvent(PuglView* view, const PuglEvent* event)
{
@@ -226,12 +287,8 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event)
break;

case PUGL_CONFIGURE:
if (puglMustConfigure(view, &event->configure)) {
if (!(st0 = view->backend->enter(view, NULL))) {
st0 = puglConfigure(view, event);
st1 = view->backend->leave(view, NULL);
}
}
st0 = view->eventFunc(view, event);
view->lastConfigure = event->configure;
if (view->stage == PUGL_VIEW_STAGE_REALIZED) {
view->stage = PUGL_VIEW_STAGE_CONFIGURED;
}
@@ -240,8 +297,12 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event)
case PUGL_EXPOSE:
assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED);
if (!(st0 = view->backend->enter(view, &event->expose))) {
st0 = view->eventFunc(view, event);
st1 = view->backend->leave(view, &event->expose);
const PuglWorldState old_state = view->world->state;

view->world->state = PUGL_WORLD_EXPOSING;
st0 = view->eventFunc(view, event);
view->world->state = old_state;
st1 = view->backend->leave(view, &event->expose);
}
break;



+ 30
- 10
dpf/dgl/src/pugl-upstream/src/internal.h View File

@@ -6,11 +6,10 @@
#ifndef PUGL_INTERNAL_H
#define PUGL_INTERNAL_H

#include "attributes.h"
#include "types.h"

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

#include <stdbool.h>
#include <stddef.h>
@@ -18,9 +17,29 @@

PUGL_BEGIN_DECLS

/// Return true if `x`,`y` is a valid position
bool
puglIsValidPosition(int x, int y);

/// Return true if `width`,`height` is a valid position
bool
puglIsValidSize(unsigned width, unsigned height);

/// Return true if `size` is a valid view size
bool
puglIsValidSize(PuglViewSize size);
puglIsValidArea(PuglArea size);

/// Return the center point of some "soft" ancestor (parent window or screen)
PuglPoint
puglGetAncestorCenter(const PuglView* view);

/// Return the initial size of a view
PuglArea
puglGetInitialSize(const PuglView* view);

/// Return the initial position of a view if known, or an invalid position
PuglPoint
puglGetInitialPosition(const PuglView* view, PuglArea size);

/// Set hint to a default value if it is unset (PUGL_DONT_CARE)
void
@@ -34,8 +53,14 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len);
void
puglSetString(char** dest, const char* string);

/// Store `width` and `height` as the current value of a size `hint`
PuglStatus
puglStoreSizeHint(PuglView* view,
PuglSizeHint hint,
unsigned width,
unsigned height);

/// Handle a changed string property
PUGL_API
PuglStatus
puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value);

@@ -55,11 +80,6 @@ puglPreRealize(PuglView* view);
PuglStatus
puglDispatchSimpleEvent(PuglView* view, PuglEventType type);

/// Process configure event while already in the graphics context
PUGL_WARN_UNUSED_RESULT
PuglStatus
puglConfigure(PuglView* view, const PuglEvent* event);

/// Dispatch `event` to `view`, entering graphics context if necessary
PuglStatus
puglDispatchEvent(PuglView* view, const PuglEvent* event);


+ 1
- 1
dpf/dgl/src/pugl-upstream/src/mac.h View File

@@ -5,7 +5,7 @@
#ifndef PUGL_SRC_MAC_H
#define PUGL_SRC_MAC_H

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#import <Cocoa/Cocoa.h>



+ 143
- 226
dpf/dgl/src/pugl-upstream/src/mac.m View File

@@ -7,9 +7,10 @@
#include "mac.h"

#include "internal.h"
#include "macros.h"
#include "platform.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#import <Cocoa/Cocoa.h>

@@ -121,7 +122,7 @@ viewScreen(const PuglView* view)
}

static NSRect
nsRectToPoints(PuglView* view, const NSRect rect)
nsRectToPoints(const PuglView* view, const NSRect rect)
{
const double scaleFactor = [viewScreen(view) backingScaleFactor];

@@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect)
}

static NSRect
nsRectFromPoints(PuglView* view, const NSRect rect)
nsRectFromPoints(const PuglView* view, const NSRect rect)
{
const double scaleFactor = [viewScreen(view) backingScaleFactor];

@@ -143,21 +144,15 @@ nsRectFromPoints(PuglView* view, const NSRect rect)
}

static NSPoint
nsPointFromPoints(PuglView* view, const NSPoint point)
nsPointFromPoints(const PuglView* view, const NSPoint point)
{
const double scaleFactor = [viewScreen(view) backingScaleFactor];

return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor);
}

static NSRect
rectToNsRect(const PuglRect rect)
{
return NSMakeRect(rect.x, rect.y, rect.width, rect.height);
}

static NSSize
sizePoints(PuglView* view, const double width, const double height)
sizePoints(PuglView* view, const PuglSpan width, const PuglSpan height)
{
const double scaleFactor = [viewScreen(view) backingScaleFactor];

@@ -188,7 +183,7 @@ getCurrentViewStyleFlags(PuglView* const view)
}

static PuglStatus
dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)
dispatchCurrentChildViewConfiguration(PuglView* const view)
{
const NSRect framePt = [view->impl->wrapperView frame];
const NSRect framePx = nsRectFromPoints(view, framePt);
@@ -197,14 +192,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)
return PUGL_SUCCESS;
}

if (drawViewResize) {
const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size];
[view->impl->drawView setFrameSize:sizePt];
}

const PuglConfigureEvent ev = {
PUGL_CONFIGURE,
0,
0U,
(PuglCoord)framePx.origin.x,
(PuglCoord)framePx.origin.y,
(PuglSpan)framePx.size.width,
@@ -254,7 +244,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)

const PuglConfigureEvent ev = {
PUGL_CONFIGURE,
0,
0U,
(PuglCoord)contentPx.origin.x,
(PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height),
(PuglSpan)contentPx.size.width,
@@ -322,7 +312,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)
if (puglview->impl->window) {
[puglview->impl->window dispatchCurrentConfiguration];
} else {
dispatchCurrentChildViewConfiguration(puglview, true);
dispatchCurrentChildViewConfiguration(puglview);
}
reshaped = false;
}
@@ -336,7 +326,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)

const PuglExposeEvent ev = {
PUGL_EXPOSE,
0,
0U,
(PuglCoord)(rect.origin.x * scaleFactor),
(PuglCoord)viewY,
(PuglSpan)(rect.size.width * scaleFactor),
@@ -350,9 +340,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize)

- (NSSize)intrinsicContentSize
{
const PuglViewSize defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE];
const PuglArea defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE];

return puglIsValidSize(defaultSize)
return puglIsValidArea(defaultSize)
? sizePoints(puglview, defaultSize.width, defaultSize.height)
: NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric);
}
@@ -468,7 +458,7 @@ keySymToSpecial(const NSEvent* const ev)
return PUGL_KEY_PAD_9;
}

return (PuglKey)0;
return PUGL_KEY_NONE;
}

- (void)updateTrackingAreas
@@ -501,7 +491,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint rloc = [NSEvent mouseLocation];
const PuglCrossingEvent ev = {
type,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -542,7 +532,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint rloc = [NSEvent mouseLocation];
const PuglMotionEvent ev = {
PUGL_MOTION,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -577,7 +567,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint rloc = [NSEvent mouseLocation];
const PuglButtonEvent ev = {
PUGL_BUTTON_PRESS,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -598,7 +588,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint rloc = [NSEvent mouseLocation];
const PuglButtonEvent ev = {
PUGL_BUTTON_RELEASE,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -638,11 +628,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint wloc = [self eventLocation:event];
const NSPoint rloc = [NSEvent mouseLocation];

double dx = -[event scrollingDeltaX] / 2.0;
double dy = [event scrollingDeltaY] / 2.0;
double dx = -[event scrollingDeltaX];
double dy = [event scrollingDeltaY];
if ([event hasPreciseScrollingDeltas]) {
dx /= 10.0;
dy /= 10.0;
dx /= 20.0;
dy /= 20.0;
}

const PuglScrollDirection dir =
@@ -657,7 +647,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)

const PuglScrollEvent ev = {
PUGL_SCROLL,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -689,7 +679,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)

const PuglKeyEvent ev = {
PUGL_KEY_PRESS,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -720,7 +710,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)

const PuglKeyEvent ev = {
PUGL_KEY_RELEASE,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -832,7 +822,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)

PuglTextEvent ev = {
PUGL_TEXT,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -861,7 +851,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask)
- (void)flagsChanged:(NSEvent*)event
{
const uint32_t mods = getModifiers(event);
PuglKey special = (PuglKey)0;
PuglKey special = PUGL_KEY_NONE;

const uint16_t keyCode = [event keyCode];
if (flagDiffers(mods, puglview->impl->mods, PUGL_MOD_SHIFT)) {
@@ -882,7 +872,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask)
const bool release = [event type] == NSEventTypeKeyUp;

const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS,
0,
0U,
[event timestamp],
wloc.x,
wloc.y,
@@ -924,7 +914,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask)
- (void)timerTick:(NSTimer*)userTimer
{
const NSNumber* userInfo = userTimer.userInfo;
const PuglTimerEvent ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue};
const PuglTimerEvent ev = {PUGL_TIMER, 0U, userInfo.unsignedLongValue};

PuglEvent timerEvent;
timerEvent.timer = ev;
@@ -989,7 +979,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask)
{
(void)notification;

PuglEvent ev = {{PUGL_FOCUS_IN, 0}};
PuglEvent ev = {{PUGL_FOCUS_IN, 0U}};
ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@@ -998,7 +988,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask)
{
(void)notification;

PuglEvent ev = {{PUGL_FOCUS_OUT, 0}};
PuglEvent ev = {{PUGL_FOCUS_OUT, 0U}};
ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@@ -1117,17 +1107,18 @@ puglConstraint(const id item,
constant:(CGFloat)constant];
}

static PuglStatus
updateSizeHint(PuglView* const view, const PuglSizeHint hint)
PuglStatus
puglApplySizeHint(PuglView* const view, const PuglSizeHint hint)
{
const PuglSpan width = view->sizeHints[hint].width;
const PuglSpan height = view->sizeHints[hint].height;
if (!puglIsValidSize(view->sizeHints[hint])) {
if (!puglIsValidArea(view->sizeHints[hint])) {
return PUGL_FAILURE;
}

switch (hint) {
case PUGL_DEFAULT_SIZE:
case PUGL_CURRENT_SIZE:
break;

case PUGL_MIN_SIZE:
@@ -1150,38 +1141,18 @@ updateSizeHint(PuglView* const view, const PuglSizeHint hint)
return PUGL_SUCCESS;
}

static void
updateSizeHints(PuglView* const view)
PuglStatus
puglUpdateSizeHints(PuglView* const view)
{
for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) {
updateSizeHint(view, (PuglSizeHint)i);
puglApplySizeHint(view, (PuglSizeHint)i);
}
return PUGL_SUCCESS;
}

static PuglRect
getInitialFrame(PuglView* const view)
PuglPoint
puglGetAncestorCenter(const PuglView* const view)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the last configured frame
const PuglRect frame = {view->lastConfigure.x,
view->lastConfigure.y,
view->lastConfigure.width,
view->lastConfigure.height};
return frame;
}

const int x = view->defaultX;
const int y = view->defaultY;
if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) {
// Use the default position set with puglSetPosition while unrealized
const PuglRect frame = {(PuglCoord)x,
(PuglCoord)y,
view->sizeHints[PUGL_DEFAULT_SIZE].width,
view->sizeHints[PUGL_DEFAULT_SIZE].height};
return frame;
}

// Get a bounding rect from the transient parent or the screen
const NSScreen* const screen = viewScreen(view);
const NSRect boundsPt =
rectFromScreen(screen,
@@ -1189,17 +1160,11 @@ getInitialFrame(PuglView* const view)
? [[(const NSView*)view->transientParent window] frame]
: [screen frame]);

// Center the frame around the center of the bounding rectangle
const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
const NSRect boundsPx = nsRectFromPoints(view, boundsPt);
const double centerX = boundsPx.origin.x + boundsPx.size.width / 2;
const double centerY = boundsPx.origin.y + boundsPx.size.height / 2;

const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)),
(PuglCoord)(centerY - (defaultSize.height / 2U)),
view->sizeHints[PUGL_DEFAULT_SIZE].width,
view->sizeHints[PUGL_DEFAULT_SIZE].height};
return frame;
const NSRect boundsPx = nsRectFromPoints(view, boundsPt);
const PuglPoint center = {
(PuglCoord)(boundsPx.origin.x + (boundsPx.size.width / 2.0)),
(PuglCoord)(boundsPx.origin.y + (boundsPx.size.height / 2.0))};
return center;
}

PuglStatus
@@ -1247,11 +1212,12 @@ puglRealize(PuglView* view)
CVDisplayLinkRelease(link);
}

// Get the initial frame to use from the defaults or last configuration
const PuglRect initialFrame = getInitialFrame(view);
// Get the initial size and position from the defaults or last configuration
const PuglArea size = puglGetInitialSize(view);
const PuglPoint pos = puglGetInitialPosition(view, size);

// Convert frame to points
const NSRect framePx = rectToNsRect(initialFrame);
const NSRect framePx = NSMakeRect(pos.x, pos.y, size.width, size.height);
const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor,
framePx.origin.y / scaleFactor,
framePx.size.width / scaleFactor,
@@ -1277,7 +1243,7 @@ puglRealize(PuglView* view)
NSLayoutRelationGreaterThanOrEqual,
view->sizeHints[PUGL_MIN_SIZE].height)];

if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) {
if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) {
[impl->wrapperView
addConstraint:puglConstraint(impl->wrapperView,
NSLayoutAttributeWidth,
@@ -1337,10 +1303,19 @@ puglRealize(PuglView* view)
((NSWindow*)window).delegate =
[[PuglWindowDelegate alloc] initWithPuglWindow:window];

// Set basic window hints and attributes
puglSetFrame(view, initialFrame);
// Set window frame
const NSRect screenPt = rectToScreen(screen, framePt);
const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];
[impl->window setFrame:winFrame display:NO];

// Resize views and move them to (0, 0)
const NSRect sizePx = {{0, 0}, {framePx.size.width, framePx.size.height}};
const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx];
[impl->wrapperView setFrame:sizePt];
[impl->drawView setFrame:sizePt];

puglSetTransientParent(view, view->transientParent);
updateSizeHints(view);
puglUpdateSizeHints(view);

[window setContentView:impl->wrapperView];
[view->world->impl->app activateIgnoringOtherApps:YES];
@@ -1393,24 +1368,23 @@ puglUnrealize(PuglView* const view)
PuglStatus
puglShow(PuglView* view, const PuglShowCommand command)
{
if (!view->impl->wrapperView) {
const PuglStatus st = puglRealize(view);
if (st) {
return st;
}
PuglInternals* impl = view->impl;
PuglStatus st = impl->wrapperView ? PUGL_SUCCESS : puglRealize(view);
if (st || !impl->wrapperView) {
return st;
}

NSWindow* const window = [view->impl->wrapperView window];
NSWindow* const window = [impl->wrapperView window];
if (![window isVisible]) {
[window setIsVisible:YES];
[view->impl->drawView setNeedsDisplay:YES];
[impl->drawView setNeedsDisplay:YES];
}

switch (command) {
case PUGL_SHOW_PASSIVE:
break;
case PUGL_SHOW_RAISE:
[window orderFront:view->impl->wrapperView];
[window orderFront:impl->wrapperView];
break;
case PUGL_SHOW_FORCE_RAISE:
[window orderFrontRegardless];
@@ -1583,6 +1557,10 @@ puglStopTimer(PuglView* view, uintptr_t id)
PuglStatus
puglSendEvent(PuglView* view, const PuglEvent* event)
{
if (!view->impl->window || view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_FAILURE;
}

if (event->type == PUGL_CLIENT) {
PuglEvent copiedEvent = *event;

@@ -1610,18 +1588,17 @@ puglSendEvent(PuglView* view, const PuglEvent* event)
return PUGL_UNSUPPORTED;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglWaitForEvent(PuglView* view)
{
return puglPollEvents(view->world, -1.0);
}
#endif

PuglStatus
puglUpdate(PuglWorld* world, const double timeout)
{
@autoreleasepool {
const PuglWorldState startState = world->state;
if (startState == PUGL_WORLD_IDLE) {
world->state = PUGL_WORLD_UPDATING;
} else if (startState != PUGL_WORLD_RECURSING) {
return PUGL_BAD_CALL;
}

if (world->type == PUGL_PROGRAM) {
NSDate* date =
((timeout < 0) ? [NSDate distantFuture]
@@ -1650,19 +1627,13 @@ puglUpdate(PuglWorld* world, const double timeout)

[view->impl->drawView displayIfNeeded];
}

world->state = startState;
}

return PUGL_SUCCESS;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* view)
{
return puglDispatchEvents(view->world);
}
#endif

double
puglGetTime(const PuglWorld* world)
{
@@ -1674,28 +1645,51 @@ puglGetTime(const PuglWorld* world)
}

PuglStatus
puglPostRedisplay(PuglView* view)
puglObscureView(PuglView* view)
{
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

[view->impl->drawView setNeedsDisplay:YES];
return PUGL_SUCCESS;
}

PuglStatus
puglPostRedisplayRect(PuglView* view, const PuglRect rect)
puglObscureRegion(PuglView* view,
const int x,
const int y,
const unsigned width,
const unsigned height)
{
const NSRect rectPx = {
{(double)rect.x,
(double)view->lastConfigure.height - (rect.y + rect.height)},
{(double)rect.width, (double)rect.height},
};
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

const PuglSpan viewHeight = view->lastConfigure.height;

const int cx = MAX(0, x);
const int cy = MAX(0, viewHeight - y - (int)height);
const unsigned cw = MIN(view->lastConfigure.width, width);
const unsigned ch = MIN(view->lastConfigure.height, height);

if (cw == view->lastConfigure.width && ch == view->lastConfigure.height) {
[view->impl->drawView setNeedsDisplay:YES];
} else {
const NSRect rectPx = NSMakeRect(cx, cy, cw, ch);

[view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)];
[view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)];
}

return PUGL_SUCCESS;
}

PuglNativeView
puglGetNativeView(PuglView* view)
puglGetNativeView(const PuglView* view)
{
return (PuglNativeView)view->impl->wrapperView;
}
@@ -1735,107 +1729,35 @@ puglGetScaleFactor(const PuglView* const view)
}

PuglStatus
puglSetFrame(PuglView* view, const PuglRect frame)
{
PuglInternals* const impl = view->impl;
const NSRect framePx = rectToNsRect(frame);
const NSRect framePt = nsRectToPoints(view, framePx);

if (!impl->wrapperView) {
// Set defaults to be used when realized
view->defaultX = frame.x;
view->defaultY = frame.y;
view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height;
return PUGL_SUCCESS;
}

if (impl->window) {
const NSRect screenPt = rectToScreen(viewScreen(view), framePt);

// Move and resize window to fit new content rect
const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];
[impl->window setFrame:winFrame display:NO];

// Resize views
const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height);
const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx];
[impl->wrapperView setFrame:sizePt];
[impl->drawView setFrame:sizePt];
[impl->window dispatchCurrentConfiguration];
return PUGL_SUCCESS;
}

// Resize view
const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height);
const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx];

[impl->wrapperView setFrame:framePt];
[impl->drawView setFrame:sizePt];
return dispatchCurrentChildViewConfiguration(view, false);
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
puglSetWindowPosition(PuglView* const view, const int x, const int y)
{
if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

PuglInternals* const impl = view->impl;
if (!impl->wrapperView) {
// Set defaults to be used when realized
view->defaultX = x;
view->defaultY = y;
return PUGL_SUCCESS;
}

const PuglRect frame = {(PuglCoord)x,
(PuglCoord)y,
view->lastConfigure.width,
view->lastConfigure.height};
const NSRect framePx =
NSMakeRect(x, y, view->lastConfigure.width, view->lastConfigure.height);

const NSRect framePt = nsRectToPoints(view, framePx);

if (impl->window) {
// Adjust top-level window frame
return puglSetFrame(view, frame);
const NSRect screenPt = rectToScreen(viewScreen(view), framePt);
[impl->window setFrameOrigin:screenPt.origin];
return PUGL_SUCCESS;
}

// Set wrapper view origin
const NSRect framePx = rectToNsRect(frame);
const NSRect framePt = nsRectToPoints(view, framePx);
[impl->wrapperView setFrameOrigin:framePt.origin];

// Set draw view origin
const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height);
const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx];
[impl->drawView setFrameOrigin:drawPt.origin];

// Dispatch new configuration
return dispatchCurrentChildViewConfiguration(view, false);
return dispatchCurrentChildViewConfiguration(view);
}

PuglStatus
puglSetSize(PuglView* const view, const unsigned width, const unsigned height)
puglSetWindowSize(PuglView* const view,
const unsigned width,
const unsigned height)
{
if (width > INT16_MAX || height > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

PuglInternals* const impl = view->impl;
if (!impl->wrapperView) {
// Set defaults to be used when realized
view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height;
return PUGL_SUCCESS;
}

if (impl->window) {
// Adjust top-level window frame
PuglRect frame = puglGetFrame(view);
frame.width = (PuglSpan)width;
frame.height = (PuglSpan)height;
return puglSetFrame(view, frame);
}

// Set wrapper view size
const double scaleFactor = [viewScreen(view) backingScaleFactor];
@@ -1847,24 +1769,20 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height)
const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx];
[impl->drawView setFrameSize:drawPt.size];

// Dispatch new configuration
return dispatchCurrentChildViewConfiguration(view, false);
}
if (impl->window) {
const NSRect framePx =
NSMakeRect(view->lastConfigure.x, view->lastConfigure.y, width, height);
const NSRect framePt = nsRectToPoints(view, framePx);
const NSRect screenPt = rectToScreen(viewScreen(view), framePt);

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
{
if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) {
return PUGL_BAD_PARAMETER;
// Resize window to fit new content rect
const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];
[impl->window setFrame:winFrame display:NO];
[impl->window dispatchCurrentConfiguration];
}

view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;

return view->impl->window ? updateSizeHint(view, hint) : PUGL_SUCCESS;
// Dispatch new configuration
return dispatchCurrentChildViewConfiguration(view);
}

PuglStatus
@@ -1892,7 +1810,7 @@ puglPaste(PuglView* const view)
{
const PuglDataOfferEvent offer = {
PUGL_DATA_OFFER,
0,
0U,
puglGetTime(view->world),
};

@@ -1939,7 +1857,7 @@ puglAcceptOffer(PuglView* const view,
PuglWrapperView* const wrapper = view->impl->wrapperView;
NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard];
if (!pasteboard) {
return PUGL_BAD_PARAMETER;
return PUGL_UNKNOWN_ERROR;
}

const NSArray<NSPasteboardType>* const types = [pasteboard types];
@@ -1950,13 +1868,12 @@ puglAcceptOffer(PuglView* const view,
wrapper->dragOperation = NSDragOperationCopy;
wrapper->dragTypeIndex = typeIndex;

const PuglDataEvent data = {
PUGL_DATA, 0U, puglGetTime(view->world), (uint32_t)typeIndex};
const double now = puglGetTime(view->world);
const PuglDataEvent data = {PUGL_DATA, 0U, now, typeIndex};

PuglEvent dataEvent;
dataEvent.data = data;
puglDispatchEvent(view, &dataEvent);
return PUGL_SUCCESS;
return puglDispatchEvent(view, &dataEvent);
}

const void*


+ 1
- 1
dpf/dgl/src/pugl-upstream/src/mac_cairo.m View File

@@ -5,7 +5,7 @@
#include "mac.h"
#include "stub.h"

#include "pugl/cairo.h"
#include <pugl/cairo.h>

#include <cairo-quartz.h>



+ 1
- 1
dpf/dgl/src/pugl-upstream/src/mac_gl.m View File

@@ -5,7 +5,7 @@
#include "mac.h"
#include "stub.h"

#include "pugl/gl.h"
#include <pugl/gl.h>

#ifndef __MAC_10_10
# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core


+ 1
- 1
dpf/dgl/src/pugl-upstream/src/mac_stub.m View File

@@ -5,7 +5,7 @@
#include "mac.h"
#include "stub.h"

#include "pugl/stub.h"
#include <pugl/stub.h>

#import <Cocoa/Cocoa.h>



+ 3
- 3
dpf/dgl/src/pugl-upstream/src/mac_vulkan.m View File

@@ -8,9 +8,9 @@
#include "stub.h"
#include "types.h"

#include "pugl/pugl.h"
#include "pugl/stub.h"
#include "pugl/vulkan.h"
#include <pugl/pugl.h>
#include <pugl/stub.h>
#include <pugl/vulkan.h>

#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_macos.h>


+ 22
- 7
dpf/dgl/src/pugl-upstream/src/platform.h View File

@@ -1,20 +1,20 @@
// Copyright 2012-2022 David Robillard <d@drobilla.net>
// Copyright 2012-2025 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC

// The API that a platform implementation must define
// The internal API that a platform implementation must define

#ifndef PUGL_PLATFORM_H
#define PUGL_PLATFORM_H

#include "types.h"

#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

PUGL_BEGIN_DECLS

/// Allocate and initialise world internals (implemented once per platform)
PUGL_MALLOC_FUNC
PuglWorldInternals*
PUGL_MALLOC_FUNC PuglWorldInternals*
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags);

/// Destroy and free world internals (implemented once per platform)
@@ -22,14 +22,29 @@ void
puglFreeWorldInternals(PuglWorld* world);

/// Allocate and initialise view internals (implemented once per platform)
PUGL_MALLOC_FUNC
PuglInternals*
PUGL_MALLOC_FUNC PuglInternals*
puglInitViewInternals(PuglWorld* world);

/// Destroy and free view internals (implemented once per platform)
void
puglFreeViewInternals(PuglView* view);

/// Adapt to the change of a size hint if necessary
PuglStatus
puglApplySizeHint(PuglView* view, PuglSizeHint hint);

/// Adapt to all configured size hints
PuglStatus
puglUpdateSizeHints(PuglView* view);

/// Set the current position of a view window
PuglStatus
puglSetWindowPosition(PuglView* view, int x, int y);

/// Set the current size of a view window
PuglStatus
puglSetWindowSize(PuglView* view, unsigned width, unsigned height);

PUGL_END_DECLS

#endif // PUGL_PLATFORM_H

+ 2
- 1
dpf/dgl/src/pugl-upstream/src/stub.h View File

@@ -4,7 +4,8 @@
#ifndef PUGL_SRC_STUB_H
#define PUGL_SRC_STUB_H

#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

#include <stddef.h>



+ 18
- 24
dpf/dgl/src/pugl-upstream/src/types.h View File

@@ -6,7 +6,7 @@

#include "attributes.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <stdbool.h>
#include <stddef.h>
@@ -21,24 +21,20 @@ typedef struct PuglInternalsImpl PuglInternals;
/// View hints
typedef int PuglHints[PUGL_NUM_VIEW_HINTS];

/// View position (both X and Y coordinates) or general point
typedef struct {
PuglCoord x;
PuglCoord y;
} PuglPoint;

/// View size (both X and Y coordinates)
typedef struct {
PuglSpan width;
PuglSpan height;
} PuglViewSize;

/// Blob of arbitrary data
typedef struct {
void* data; ///< Dynamically allocated data
size_t len; ///< Length of data in bytes
} PuglBlob;

/// State of the world in the process of an update
typedef enum {
PUGL_WORLD_IDLE, ///< Idle, not in puglUpdate()
PUGL_WORLD_UPDATING, ///< Event processing stage of puglUpdate()
PUGL_WORLD_EXPOSING, ///< Exposing stage of puglUpdate()
PUGL_WORLD_RECURSING, ///< Currently in recursive loop (Windows)
} PuglWorldState;

/// Stage of a view along its lifespan
typedef enum {
PUGL_VIEW_STAGE_ALLOCATED,
@@ -57,10 +53,9 @@ struct PuglViewImpl {
uintptr_t transientParent;
PuglConfigureEvent lastConfigure;
PuglHints hints;
PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS];
PuglPoint positionHints[PUGL_NUM_POSITION_HINTS];
PuglArea sizeHints[PUGL_NUM_SIZE_HINTS];
char* strings[PUGL_NUM_STRING_HINTS];
int defaultX;
int defaultY;
PuglViewStage stage;
bool resizing;
};
@@ -74,6 +69,7 @@ struct PuglWorldImpl {
PuglView** views;
char* strings[PUGL_NUM_STRING_HINTS];
PuglWorldType type;
PuglWorldState state;
};

/// Opaque surface used by graphics backend
@@ -82,23 +78,21 @@ typedef void PuglSurface;
/// Graphics backend interface
struct PuglBackendImpl {
/// Get visual information from display and setup view as necessary
PUGL_WARN_UNUSED_RESULT
PuglStatus (*configure)(PuglView*);
PUGL_WARN_UNUSED_RESULT PuglStatus (*configure)(PuglView*);

/// Create surface and drawing context
PUGL_WARN_UNUSED_RESULT
PuglStatus (*create)(PuglView*);
PUGL_WARN_UNUSED_RESULT PuglStatus (*create)(PuglView*);

/// Destroy surface and drawing context
void (*destroy)(PuglView*);

/// Enter drawing context, for drawing if expose is non-null
PUGL_WARN_UNUSED_RESULT
PuglStatus (*enter)(PuglView*, const PuglExposeEvent*);
PUGL_WARN_UNUSED_RESULT PuglStatus (*enter)(PuglView*,
const PuglExposeEvent*);

/// Leave drawing context, after drawing if expose is non-null
PUGL_WARN_UNUSED_RESULT
PuglStatus (*leave)(PuglView*, const PuglExposeEvent*);
PUGL_WARN_UNUSED_RESULT PuglStatus (*leave)(PuglView*,
const PuglExposeEvent*);

/// Return the puglGetContext() handle for the application, if any
void* (*getContext)(PuglView*);


+ 246
- 293
dpf/dgl/src/pugl-upstream/src/win.c View File

@@ -4,9 +4,10 @@
#include "win.h"

#include "internal.h"
#include "macros.h"
#include "platform.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <dwmapi.h>
#include <windows.h>
@@ -42,13 +43,11 @@
#define PUGL_USER_TIMER_MIN 9470

#ifdef __cplusplus
# define PUGL_INIT_STRUCT \
{}
#else
# define PUGL_INIT_STRUCT \
{ \
0 \
}
#else
# define PUGL_INIT_STRUCT {0}
#endif

typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void);
@@ -58,8 +57,12 @@ typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*);
LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

static wchar_t*
puglUtf8ToWideChar(const char* const utf8)
#ifdef UNICODE

typedef wchar_t ArgStringChar;

static ArgStringChar*
puglArgStringNew(const char* const utf8)
{
const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
if (len > 0) {
@@ -71,6 +74,30 @@ puglUtf8ToWideChar(const char* const utf8)
return NULL;
}

static void
puglArgStringFree(ArgStringChar* const utf8)
{
free(utf8);
}

#else // !defined(UNICODE)

typedef const char ArgStringChar;

static ArgStringChar*
puglArgStringNew(const char* const utf8)
{
return utf8;
}

static void
puglArgStringFree(ArgStringChar* const utf8)
{
(void)utf8;
}

#endif

static char*
puglWideCharToUtf8(const wchar_t* const wstr, size_t* len)
{
@@ -94,11 +121,7 @@ puglWinStatus(const BOOL success)
static bool
puglRegisterWindowClass(const char* name)
{
#ifdef UNICODE
wchar_t* const wname = puglUtf8ToWideChar(name);
#else
const char* const wname = name;
#endif
ArgStringChar* const nameArg = puglArgStringNew(name);

HMODULE module = NULL;
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
@@ -109,7 +132,7 @@ puglRegisterWindowClass(const char* name)
}

WNDCLASSEX wc = PUGL_INIT_STRUCT;
if (GetClassInfoEx(module, wname, &wc)) {
if (GetClassInfoEx(module, nameArg, &wc)) {
return true; // Already registered
}

@@ -120,12 +143,10 @@ puglRegisterWindowClass(const char* name)
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = wname;
wc.lpszClassName = nameArg;

const bool success = !!RegisterClassEx(&wc);
#ifdef UNICODE
free(wname);
#endif
puglArgStringFree(nameArg);
return success;
}

@@ -184,7 +205,7 @@ static double
puglWinGetViewScaleFactor(const PuglView* const view)
{
const HMODULE shcore =
LoadLibraryExA("Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
LoadLibraryEx(TEXT("Shcore.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!shcore) {
return 1.0;
}
@@ -219,7 +240,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags))

if (type == PUGL_PROGRAM) {
HMODULE user32 =
LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
LoadLibraryEx(TEXT("user32.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (user32) {
PFN_SetProcessDPIAware SetProcessDPIAware =
(PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware");
@@ -250,20 +271,6 @@ puglInitViewInternals(PuglWorld* PUGL_UNUSED(world))
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}

static PuglStatus
puglPollWinEvents(PuglWorld* world, const double timeout)
{
(void)world;

if (timeout < 0) {
WaitMessage();
} else {
MsgWaitForMultipleObjects(
0, NULL, FALSE, (DWORD)(timeout * 1e3), QS_ALLEVENTS);
}
return PUGL_SUCCESS;
}

PuglStatus
puglRealize(PuglView* view)
{
@@ -287,9 +294,9 @@ puglRealize(PuglView* view)
puglEnsureHint(view, PUGL_ALPHA_BITS, 8);

// Get refresh rate for resize draw timer
DEVMODEA devMode;
DEVMODE devMode;
memset(&devMode, 0, sizeof(devMode));
EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode);
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode);
view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency;

// Register window class if necessary
@@ -308,16 +315,16 @@ puglRealize(PuglView* view)
puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]);
puglSetTransientParent(view, view->transientParent);

view->impl->scaleFactor = puglWinGetViewScaleFactor(view);
view->impl->cursor = LoadCursor(NULL, IDC_ARROW);
impl->scaleFactor = puglWinGetViewScaleFactor(view);
impl->cursor = LoadCursor(NULL, IDC_ARROW);

if (view->hints[PUGL_DARK_FRAME]) {
const BOOL useDarkMode = TRUE;
if ((DwmSetWindowAttribute(view->impl->hwnd,
if ((DwmSetWindowAttribute(impl->hwnd,
DWMWA_USE_IMMERSIVE_DARK_MODE,
&useDarkMode,
sizeof(useDarkMode)) != S_OK)) {
DwmSetWindowAttribute(view->impl->hwnd,
DwmSetWindowAttribute(impl->hwnd,
PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE,
&useDarkMode,
sizeof(useDarkMode));
@@ -343,51 +350,48 @@ puglUnrealize(PuglView* const view)
view->backend->destroy(view);
}

ReleaseDC(view->impl->hwnd, view->impl->hdc);
view->impl->hdc = NULL;

DestroyWindow(view->impl->hwnd);
view->impl->hwnd = NULL;

memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent));
return PUGL_SUCCESS;
ReleaseDC(impl->hwnd, impl->hdc);
impl->hdc = NULL;

const PuglStatus st = puglWinStatus(DestroyWindow(impl->hwnd));
impl->hwnd = NULL;
return st;
}

PuglStatus
puglShow(PuglView* view, const PuglShowCommand command)
{
PuglInternals* impl = view->impl;
PuglStatus st = impl->hwnd ? PUGL_SUCCESS : puglRealize(view);

if (!impl->hwnd) {
const PuglStatus st = puglRealize(view);
if (st) {
return st;
if (!st) {
switch (command) {
case PUGL_SHOW_PASSIVE:
ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE);
break;
case PUGL_SHOW_RAISE:
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
st = SetActiveWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE;
break;
case PUGL_SHOW_FORCE_RAISE:
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
st = SetForegroundWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE;
break;
}
}

switch (command) {
case PUGL_SHOW_PASSIVE:
ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE);
break;
case PUGL_SHOW_RAISE:
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
SetActiveWindow(impl->hwnd);
break;
case PUGL_SHOW_FORCE_RAISE:
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
SetForegroundWindow(impl->hwnd);
break;
}

return PUGL_SUCCESS;
return st;
}

PuglStatus
puglHide(PuglView* view)
{
PuglInternals* impl = view->impl;
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

ShowWindow(impl->hwnd, SW_HIDE);
ShowWindow(view->impl->hwnd, SW_HIDE);
return PUGL_SUCCESS;
}

@@ -408,14 +412,11 @@ puglFreeViewInternals(PuglView* view)
void
puglFreeWorldInternals(PuglWorld* world)
{
#ifdef UNICODE
wchar_t* const wname = puglUtf8ToWideChar(world->strings[PUGL_CLASS_NAME]);
UnregisterClass(wname, NULL);
free(wname);
#else
UnregisterClass(world->strings[PUGL_CLASS_NAME], NULL);
#endif
const char* const className = world->strings[PUGL_CLASS_NAME];
ArgStringChar* const classNameArg = puglArgStringNew(className);

UnregisterClass(classNameArg, NULL);
puglArgStringFree(classNameArg);
free(world->impl);
}

@@ -426,14 +427,14 @@ keyInRange(const WPARAM winSym,
const PuglKey puglMin)
{
return (winSym >= winMin && winSym <= winMax)
? (PuglKey)(puglMin + (winSym - winMin))
: (PuglKey)0;
? (PuglKey)((WPARAM)puglMin + (winSym - winMin))
: PUGL_KEY_NONE;
}

static PuglKey
keySymToSpecial(const WPARAM sym, const bool ext)
{
PuglKey key = (PuglKey)0;
PuglKey key = PUGL_KEY_NONE;
if ((key = keyInRange(sym, VK_F1, VK_F12, PUGL_KEY_F1)) ||
(key = keyInRange(sym,
VK_PRIOR,
@@ -482,23 +483,26 @@ keySymToSpecial(const WPARAM sym, const bool ext)
// clang-format on
}

return (PuglKey)0;
return PUGL_KEY_NONE;
}

static bool
is_toggled(int vkey)
{
return (unsigned)GetKeyState(vkey) & 1U;
}

static uint32_t
getModifiers(void)
{
// clang-format off
return (
((GetKeyState(VK_SHIFT) < 0) ? (uint32_t)PUGL_MOD_SHIFT : 0U) |
((GetKeyState(VK_CONTROL) < 0) ? (uint32_t)PUGL_MOD_CTRL : 0U) |
((GetKeyState(VK_MENU) < 0) ? (uint32_t)PUGL_MOD_ALT : 0U) |
((GetKeyState(VK_LWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) |
((GetKeyState(VK_RWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) |
((GetKeyState(VK_NUMLOCK) & 1U) ? (uint32_t)PUGL_MOD_NUM_LOCK : 0U) |
((GetKeyState(VK_SCROLL) & 1U) ? (uint32_t)PUGL_MOD_SCROLL_LOCK : 0U) |
((GetKeyState(VK_CAPITAL) & 1U) ? (uint32_t)PUGL_MOD_CAPS_LOCK : 0U));
// clang-format on
return ((uint32_t)(((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0) |
((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0) |
((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0) |
((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0) |
((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0) |
(is_toggled(VK_NUMLOCK) ? PUGL_MOD_NUM_LOCK : 0) |
(is_toggled(VK_SCROLL) ? PUGL_MOD_SCROLL_LOCK : 0) |
(is_toggled(VK_CAPITAL) ? PUGL_MOD_CAPS_LOCK : 0)));
}

static void
@@ -673,7 +677,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos)

const PuglCrossingEvent ev = {
type,
0,
0U,
GetMessageTime() / 1e3,
(double)pos.x,
(double)pos.y,
@@ -683,7 +687,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos)
PUGL_CROSSING_NORMAL,
};

PuglEvent crossingEvent = {{type, 0}};
PuglEvent crossingEvent = {{type, 0U}};
crossingEvent.crossing = ev;
puglDispatchEvent(view, &crossingEvent);
}
@@ -693,8 +697,8 @@ constrainAspect(const PuglView* const view,
RECT* const size,
const WPARAM wParam)
{
const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT];
const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT];

const float minA = (float)minAspect.width / (float)minAspect.height;
const float maxA = (float)maxAspect.width / (float)maxAspect.height;
@@ -733,7 +737,7 @@ constrainAspect(const PuglView* const view,
static LRESULT
handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
{
PuglEvent event = {{PUGL_NOTHING, 0}};
PuglEvent event = {{PUGL_NOTHING, 0U}};
RECT rect = {0, 0, 0, 0};
POINT pt = {0, 0};
MINMAXINFO* mmi = NULL;
@@ -779,8 +783,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
handleConfigure(view, &event);
break;
case WM_SIZING:
if (puglIsValidSize(view->sizeHints[PUGL_MIN_ASPECT]) &&
puglIsValidSize(view->sizeHints[PUGL_MAX_ASPECT])) {
if (puglIsValidArea(view->sizeHints[PUGL_MIN_ASPECT]) &&
puglIsValidArea(view->sizeHints[PUGL_MAX_ASPECT])) {
constrainAspect(view, (RECT*)lParam, wParam);
return TRUE;
}
@@ -795,7 +799,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_TIMER:
if (wParam >= PUGL_USER_TIMER_MIN) {
PuglEvent ev = {{PUGL_TIMER, 0}};
PuglEvent ev = {{PUGL_TIMER, 0U}};
ev.timer.id = wParam - PUGL_USER_TIMER_MIN;
puglDispatchEvent(view, &ev);
}
@@ -812,7 +816,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width;
mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height;
if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) {
if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) {
mmi->ptMaxTrackSize.x = view->sizeHints[PUGL_MAX_SIZE].width;
mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height;
}
@@ -948,8 +952,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
PuglStatus
puglGrabFocus(PuglView* view)
{
SetFocus(view->impl->hwnd);
return PUGL_SUCCESS;
return puglWinStatus(!!SetFocus(view->impl->hwnd));
}

bool
@@ -1023,20 +1026,32 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags)
const bool newMaximized = styleIsMaximized(flags);
if (oldMaximized != newMaximized) {
ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE);
puglPostRedisplay(view);
puglObscureView(view);
}

return PUGL_SUCCESS;
}

PuglStatus
puglApplySizeHint(PuglView* const PUGL_UNUSED(view),
const PuglSizeHint PUGL_UNUSED(hint))
{
return PUGL_SUCCESS;
}

PuglStatus
puglUpdateSizeHints(PuglView* const PUGL_UNUSED(view))
{
return PUGL_SUCCESS;
}

PuglStatus
puglStartTimer(PuglView* view, uintptr_t id, double timeout)
{
const UINT msec = (UINT)floor(timeout * 1000.0);

return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL)
? PUGL_SUCCESS
: PUGL_UNKNOWN_ERROR);
SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL);
return PUGL_SUCCESS;
}

PuglStatus
@@ -1048,32 +1063,24 @@ puglStopTimer(PuglView* view, uintptr_t id)
PuglStatus
puglSendEvent(PuglView* view, const PuglEvent* event)
{
if (!view->impl->hwnd || view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_FAILURE;
}

if (event->type == PUGL_CLOSE) {
PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0);
return PUGL_SUCCESS;
return puglWinStatus(PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0));
}

if (event->type == PUGL_CLIENT) {
PostMessage(view->impl->hwnd,
PUGL_LOCAL_CLIENT_MSG,
(WPARAM)event->client.data1,
(LPARAM)event->client.data2);

return PUGL_SUCCESS;
return puglWinStatus(PostMessage(view->impl->hwnd,
PUGL_LOCAL_CLIENT_MSG,
(WPARAM)event->client.data1,
(LPARAM)event->client.data2));
}

return PUGL_UNSUPPORTED;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglWaitForEvent(PuglView* PUGL_UNUSED(view))
{
WaitMessage();
return PUGL_SUCCESS;
}
#endif

static PuglStatus
puglDispatchViewEvents(PuglView* view)
{
@@ -1118,21 +1125,31 @@ puglDispatchWinEvents(PuglWorld* world)
PuglStatus
puglUpdate(PuglWorld* world, double timeout)
{
const double startTime = puglGetTime(world);
PuglStatus st = PUGL_SUCCESS;
static const double minWaitSeconds = 0.002;

const double startTime = puglGetTime(world);
const PuglWorldState startState = world->state;
PuglStatus st = PUGL_SUCCESS;

if (startState == PUGL_WORLD_IDLE) {
world->state = PUGL_WORLD_UPDATING;
} else if (startState != PUGL_WORLD_RECURSING) {
return PUGL_BAD_CALL;
}

if (timeout < 0.0) {
st = puglPollWinEvents(world, timeout);
st = st ? st : puglDispatchWinEvents(world);
} else if (timeout <= 0.001) {
WaitMessage();
st = puglDispatchWinEvents(world);
} else if (timeout < minWaitSeconds) {
st = puglDispatchWinEvents(world);
} else {
const double endTime = startTime + timeout - 0.001;
for (double t = startTime; t < endTime; t = puglGetTime(world)) {
if ((st = puglPollWinEvents(world, endTime - t)) ||
(st = puglDispatchWinEvents(world))) {
break;
}
const double endTime = startTime + timeout - minWaitSeconds;
double t = startTime;
while (!st && t < endTime) {
const DWORD timeoutMs = (DWORD)((endTime - t) * 1e3);
MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutMs, QS_ALLEVENTS);
st = puglDispatchWinEvents(world);
t = puglGetTime(world);
}
}

@@ -1144,17 +1161,10 @@ puglUpdate(PuglWorld* world, double timeout)
UpdateWindow(world->views[i]->impl->hwnd);
}

world->state = startState;
return st;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* view)
{
return puglUpdate(view->world, 0.0);
}
#endif

LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -1188,27 +1198,39 @@ puglGetTime(const PuglWorld* world)
}

PuglStatus
puglPostRedisplay(PuglView* view)
puglObscureView(PuglView* view)
{
InvalidateRect(view->impl->hwnd, NULL, false);
return PUGL_SUCCESS;
return view->world->state == PUGL_WORLD_EXPOSING
? PUGL_BAD_CALL
: puglWinStatus(InvalidateRect(view->impl->hwnd, NULL, false));
}

PuglStatus
puglPostRedisplayRect(PuglView* view, const PuglRect rect)
puglObscureRegion(PuglView* const view,
const int x,
const int y,
const unsigned width,
const unsigned height)
{
const RECT r = {(long)floor(rect.x),
(long)floor(rect.y),
(long)ceil(rect.x + rect.width),
(long)ceil(rect.y + rect.height)};
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

InvalidateRect(view->impl->hwnd, &r, false);
if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

return PUGL_SUCCESS;
const int cx = MAX(0, x);
const int cy = MAX(0, y);
const unsigned cw = MIN(view->lastConfigure.width, width);
const unsigned ch = MIN(view->lastConfigure.height, height);

const RECT r = {cx, cy, cx + (long)cw, cy + (long)ch};
return puglWinStatus(InvalidateRect(view->impl->hwnd, &r, false));
}

PuglNativeView
puglGetNativeView(PuglView* view)
puglGetNativeView(const PuglView* view)
{
return (PuglNativeView)view->impl->hwnd;
}
@@ -1218,19 +1240,18 @@ puglViewStringChanged(PuglView* const view,
const PuglStringHint key,
const char* const value)
{
PuglStatus st = PUGL_SUCCESS;
if (!view->impl->hwnd) {
return PUGL_SUCCESS;
return st;
}

if (key == PUGL_WINDOW_TITLE) {
wchar_t* const wtitle = puglUtf8ToWideChar(value);
if (wtitle) {
SetWindowTextW(view->impl->hwnd, wtitle);
free(wtitle);
}
ArgStringChar* const titleArg = puglArgStringNew(value);
st = puglWinStatus(SetWindowText(view->impl->hwnd, titleArg));
puglArgStringFree(titleArg);
}

return PUGL_SUCCESS;
return st;
}

static RECT
@@ -1256,44 +1277,8 @@ puglGetScaleFactor(const PuglView* const view)
}

PuglStatus
puglSetFrame(PuglView* view, const PuglRect frame)
puglSetWindowPosition(PuglView* const view, const int x, const int y)
{
if (!view->impl->hwnd) {
// Set defaults to be used when realized
view->defaultX = frame.x;
view->defaultY = frame.y;
view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height;
return PUGL_SUCCESS;
}

const RECT rect =
adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height);

return puglWinStatus(
SetWindowPos(view->impl->hwnd,
HWND_TOP,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER));
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
{
if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->hwnd) {
// Set defaults to be used when realized
view->defaultX = x;
view->defaultY = y;
return PUGL_SUCCESS;
}

const RECT rect = adjustedWindowRect(
view, x, y, view->lastConfigure.width, view->lastConfigure.height);

@@ -1305,19 +1290,10 @@ puglSetPosition(PuglView* const view, const int x, const int y)
}

PuglStatus
puglSetSize(PuglView* const view, const unsigned width, const unsigned height)
puglSetWindowSize(PuglView* const view,
const unsigned width,
const unsigned height)
{
if (width > INT16_MAX || height > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->hwnd) {
// Set defaults to be used when realized
view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height;
return PUGL_SUCCESS;
}

const RECT rect = adjustedWindowRect(view,
view->lastConfigure.x,
view->lastConfigure.y,
@@ -1336,21 +1312,6 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height)
flags));
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
{
if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) {
return PUGL_BAD_PARAMETER;
}

view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;
return PUGL_SUCCESS;
}

PuglStatus
puglSetTransientParent(PuglView* view, PuglNativeView parent)
{
@@ -1394,15 +1355,14 @@ puglAcceptOffer(PuglView* const view,

const PuglDataEvent data = {
PUGL_DATA,
0,
0U,
GetMessageTime() / 1e3,
0,
};

PuglEvent dataEvent;
dataEvent.data = data;
puglDispatchEvent(view, &dataEvent);
return PUGL_SUCCESS;
return puglDispatchEvent(view, &dataEvent);
}

const void*
@@ -1412,8 +1372,21 @@ puglGetClipboard(PuglView* const view,
{
PuglInternals* const impl = view->impl;

if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT) ||
!OpenClipboard(impl->hwnd)) {
if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT)) {
return NULL;
}

// Try to open the clipboard several times since others may have locked it
BOOL opened = FALSE;
static const unsigned max_tries = 16U;
for (unsigned i = 0U; !opened && i < max_tries; ++i) {
opened = OpenClipboard(impl->hwnd);
if (!opened) {
Sleep(0);
}
}

if (!opened) {
return NULL;
}

@@ -1424,15 +1397,14 @@ puglGetClipboard(PuglView* const view,
return NULL;
}

free(view->impl->clipboard.data);
view->impl->clipboard.data =
puglWideCharToUtf8(wstr, &view->impl->clipboard.len);
free(impl->clipboard.data);
impl->clipboard.data = puglWideCharToUtf8(wstr, &impl->clipboard.len);

GlobalUnlock(mem);
CloseClipboard();

*len = view->impl->clipboard.len;
return view->impl->clipboard.data;
*len = impl->clipboard.len;
return impl->clipboard.data;
}

PuglStatus
@@ -1443,7 +1415,7 @@ puglSetClipboard(PuglView* const view,
{
PuglInternals* const impl = view->impl;

PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len);
PuglStatus st = puglSetBlob(&impl->clipboard, data, len);
if (st) {
return st;
}
@@ -1488,14 +1460,13 @@ puglPaste(PuglView* const view)
{
const PuglDataOfferEvent offer = {
PUGL_DATA_OFFER,
0,
0U,
GetMessageTime() / 1e3,
};

PuglEvent offerEvent;
offerEvent.offer = offer;
puglDispatchEvent(view, &offerEvent);
return PUGL_SUCCESS;
return puglDispatchEvent(view, &offerEvent);
}

static const TCHAR* const cursor_ids[] = {
@@ -1563,42 +1534,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints)
return pfd;
}

static PuglRect
getInitialFrame(PuglView* const view)
PuglPoint
puglGetAncestorCenter(const PuglView* const view)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the last configured frame
const PuglRect frame = {view->lastConfigure.x,
view->lastConfigure.y,
view->lastConfigure.width,
view->lastConfigure.height};
return frame;
}

const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width;
const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height;
const int x = view->defaultX;
const int y = view->defaultY;
if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) {
// Use the default position set with puglSetPosition while unrealized
const PuglRect frame = {
(PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight};
return frame;
}

// Get a bounding rect from the "nearest" parent or parent-like window
const HWND hwnd = puglWinGetWindow(view);
RECT rect = {0, 0, 0, 0};
GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect);
RECT rect = {0, 0, 0, 0};
GetWindowRect(view->transientParent ? (HWND)view->transientParent
: GetDesktopWindow(),
&rect);

// Center the frame around the center of the bounding rectangle
const LONG centerX = rect.left + (rect.right - rect.left) / 2;
const LONG centerY = rect.top + (rect.bottom - rect.top) / 2;
const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)),
(PuglCoord)(centerY - (defaultHeight / 2)),
defaultWidth,
defaultHeight};
return frame;
const PuglPoint center = {
(PuglCoord)(rect.left + ((rect.right - rect.left) / 2)),
(PuglCoord)(rect.top + ((rect.bottom - rect.top) / 2))};
return center;
}

PuglStatus
@@ -1613,28 +1560,35 @@ puglWinCreateWindow(PuglView* const view,
PuglNativeView parent = view->parent ? view->parent : view->transientParent;

// Calculate initial window rectangle
const unsigned winFlags = puglWinGetWindowFlags(view);
const unsigned winExFlags = puglWinGetWindowExFlags(view);
const PuglRect frame = getInitialFrame(view);
RECT wr = {(long)frame.x,
(long)frame.y,
(long)frame.x + frame.width,
(long)frame.y + frame.height};
const unsigned winFlags = puglWinGetWindowFlags(view);
const unsigned winExFlags = puglWinGetWindowExFlags(view);
const PuglArea size = puglGetInitialSize(view);
const PuglPoint pos = puglGetInitialPosition(view, size);
RECT wr = {(long)pos.x,
(long)pos.y,
(long)pos.x + size.width,
(long)pos.y + size.height};
AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags);

ArgStringChar* const classNameArg = puglArgStringNew(className);
ArgStringChar* const titleArg = puglArgStringNew(title);

// Create window and get drawing context
if (!(*hwnd = CreateWindowExA(winExFlags,
className,
title,
winFlags,
wr.left,
wr.right,
wr.right - wr.left,
wr.bottom - wr.top,
(HWND)parent,
NULL,
NULL,
NULL))) {
*hwnd = CreateWindowEx(winExFlags,
classNameArg,
titleArg,
winFlags,
wr.left,
wr.right,
wr.right - wr.left,
wr.bottom - wr.top,
(HWND)parent,
NULL,
NULL,
NULL);
puglArgStringFree(titleArg);
puglArgStringFree(classNameArg);
if (!*hwnd) {
return PUGL_REALIZE_FAILED;
}

@@ -1660,7 +1614,6 @@ puglWinConfigure(PuglView* view)
{
PuglInternals* const impl = view->impl;
PuglStatus st = PUGL_SUCCESS;

if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
return st;
}
@@ -1673,17 +1626,17 @@ puglWinConfigure(PuglView* view)
DestroyWindow(impl->hwnd);
impl->hwnd = NULL;
impl->hdc = NULL;
return PUGL_SET_FORMAT_FAILED;
st = PUGL_SET_FORMAT_FAILED;
}

return PUGL_SUCCESS;
return st;
}

PuglStatus
puglWinEnter(PuglView* view, const PuglExposeEvent* expose)
{
if (expose) {
BeginPaint(view->impl->hwnd, &view->impl->paint);
return puglWinStatus(!!BeginPaint(view->impl->hwnd, &view->impl->paint));
}

return PUGL_SUCCESS;


+ 6
- 15
dpf/dgl/src/pugl-upstream/src/win.h View File

@@ -6,7 +6,7 @@

#include "internal.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <windows.h>

@@ -37,28 +37,19 @@ struct PuglInternalsImpl {
bool fullscreen;
};

PUGL_API
PuglWinPFD
PUGL_API PuglWinPFD
puglWinGetPixelFormatDescriptor(const PuglHints hints);

PUGL_WARN_UNUSED_RESULT
PUGL_API
PuglStatus
PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus
puglWinCreateWindow(PuglView* view, const char* title, HWND* hwnd, HDC* hdc);

PUGL_WARN_UNUSED_RESULT
PUGL_API
PuglStatus
PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus
puglWinConfigure(PuglView* view);

PUGL_WARN_UNUSED_RESULT
PUGL_API
PuglStatus
PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus
puglWinEnter(PuglView* view, const PuglExposeEvent* expose);

PUGL_WARN_UNUSED_RESULT
PUGL_API
PuglStatus
PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus
puglWinLeave(PuglView* view, const PuglExposeEvent* expose);

#endif // PUGL_SRC_WIN_H

+ 7
- 8
dpf/dgl/src/pugl-upstream/src/win_cairo.c View File

@@ -5,7 +5,7 @@
#include "types.h"
#include "win.h"

#include "pugl/cairo.h"
#include <pugl/cairo.h>

#include <cairo-win32.h>
#include <cairo.h>
@@ -27,7 +27,7 @@ puglWinCairoCreateDrawContext(PuglView* view)

surface->drawDc = CreateCompatibleDC(impl->hdc);
surface->drawBitmap = CreateCompatibleBitmap(
impl->hdc, (int)view->lastConfigure.width, (int)view->lastConfigure.height);
impl->hdc, view->lastConfigure.width, view->lastConfigure.height);

DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap));

@@ -106,12 +106,11 @@ puglWinCairoEnter(PuglView* view, const PuglExposeEvent* expose)
{
PuglStatus st = PUGL_SUCCESS;

if (expose && !(st = puglWinCairoCreateDrawContext(view)) &&
!(st = puglWinCairoOpen(view))) {
st = puglWinEnter(view, expose);
if (expose && !(st = puglWinCairoCreateDrawContext(view))) {
st = puglWinCairoOpen(view);
}

return st;
return st ? st : puglWinEnter(view, expose);
}

static PuglStatus
@@ -125,8 +124,8 @@ puglWinCairoLeave(PuglView* view, const PuglExposeEvent* expose)
BitBlt(impl->hdc,
0,
0,
(int)view->lastConfigure.width,
(int)view->lastConfigure.height,
view->lastConfigure.width,
view->lastConfigure.height,
surface->drawDc,
0,
0,


+ 37
- 25
dpf/dgl/src/pugl-upstream/src/win_gl.c View File

@@ -5,7 +5,7 @@
#include "types.h"
#include "win.h"

#include "pugl/gl.h"
#include <pugl/gl.h>

#include <windows.h>

@@ -14,30 +14,42 @@
#include <stdbool.h>
#include <stdlib.h>

#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_RED_BITS_ARB 0x2015
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_ALPHA_BITS_ARB 0x201b
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202b
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042

#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126

#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
typedef enum {
WGL_DRAW_TO_WINDOW_ARB = 0x2001,
WGL_ACCELERATION_ARB = 0x2003,
WGL_SUPPORT_OPENGL_ARB = 0x2010,
WGL_DOUBLE_BUFFER_ARB = 0x2011,
WGL_PIXEL_TYPE_ARB = 0x2013,
WGL_RED_BITS_ARB = 0x2015,
WGL_GREEN_BITS_ARB = 0x2017,
WGL_BLUE_BITS_ARB = 0x2019,
WGL_ALPHA_BITS_ARB = 0x201B,
WGL_DEPTH_BITS_ARB = 0x2022,
WGL_STENCIL_BITS_ARB = 0x2023,
WGL_SAMPLE_BUFFERS_ARB = 0x2041,
WGL_SAMPLES_ARB = 0x2042,
} PuglWinGlHintName;

typedef enum {
WGL_FULL_ACCELERATION_ARB = 0x2027,
WGL_TYPE_RGBA_ARB = 0x202B,
} PuglWinGlHintValue;

typedef enum {
WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091,
WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092,
WGL_CONTEXT_FLAGS_ARB = 0x2094,
WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126,
} PuglWinGlContextAttribName;

typedef enum {
WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001,
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002,
} PuglWinGlContextProfileBit;

typedef enum {
WGL_CONTEXT_DEBUG_BIT_ARB = 0x00000001,
} PuglWinGlContextDebugBit;

typedef HGLRC(WINAPI* WglCreateContextAttribs)(HDC, HGLRC, const int*);



+ 1
- 1
dpf/dgl/src/pugl-upstream/src/win_stub.c View File

@@ -5,7 +5,7 @@
#include "types.h"
#include "win.h"

#include "pugl/stub.h"
#include <pugl/stub.h>

static PuglStatus
puglWinStubConfigure(PuglView* view)


+ 1
- 1
dpf/dgl/src/pugl-upstream/src/win_vulkan.c View File

@@ -7,7 +7,7 @@
#include "types.h"
#include "win.h"

#include "pugl/vulkan.h"
#include <pugl/vulkan.h>

#include <vulkan/vulkan.h>
#include <vulkan/vulkan_win32.h>


+ 164
- 267
dpf/dgl/src/pugl-upstream/src/x11.c View File

@@ -11,7 +11,7 @@
#include "platform.h"
#include "types.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

#include <X11/X.h>
#include <X11/Xatom.h>
@@ -69,13 +69,11 @@
#include <unistd.h>

#ifdef __cplusplus
# define PUGL_INIT_STRUCT \
{}
#else
# define PUGL_INIT_STRUCT \
{ \
0 \
}
#else
# define PUGL_INIT_STRUCT {0}
#endif

enum WmClientStateMessageAction {
@@ -396,8 +394,15 @@ findView(PuglWorld* const world, const Window window)
return NULL;
}

static PuglStatus
updateSizeHints(const PuglView* const view)
PuglStatus
puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint))
{
// No fine-grained updates, hints are always recalculated together
return puglUpdateSizeHints(view);
}

PuglStatus
puglUpdateSizeHints(PuglView* const view)
{
if (!view->impl->win) {
return PUGL_SUCCESS;
@@ -407,40 +412,44 @@ updateSizeHints(const PuglView* const view)
XSizeHints sizeHints = PUGL_INIT_STRUCT;

if (!view->hints[PUGL_RESIZABLE]) {
const PuglRect frame = puglGetFrame(view);
PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
if (!puglIsValidSize(size.width, size.height)) {
size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE);
}

sizeHints.flags = PBaseSize | PMinSize | PMaxSize;
sizeHints.base_width = (int)frame.width;
sizeHints.base_height = (int)frame.height;
sizeHints.min_width = (int)frame.width;
sizeHints.min_height = (int)frame.height;
sizeHints.max_width = (int)frame.width;
sizeHints.max_height = (int)frame.height;
sizeHints.base_width = size.width;
sizeHints.base_height = size.height;
sizeHints.min_width = size.width;
sizeHints.min_height = size.height;
sizeHints.max_width = size.width;
sizeHints.max_height = size.height;
} else {
// Avoid setting PBaseSize for top level views to avoid window manager bugs
const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
if (puglIsValidSize(defaultSize) && view->parent) {
const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
if (puglIsValidArea(defaultSize) && view->parent) {
sizeHints.flags |= PBaseSize;
sizeHints.base_width = defaultSize.width;
sizeHints.base_height = defaultSize.height;
}

const PuglViewSize minSize = view->sizeHints[PUGL_MIN_SIZE];
if (puglIsValidSize(minSize)) {
const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE];
if (puglIsValidArea(minSize)) {
sizeHints.flags |= PMinSize;
sizeHints.min_width = minSize.width;
sizeHints.min_height = minSize.height;
}

const PuglViewSize maxSize = view->sizeHints[PUGL_MAX_SIZE];
if (puglIsValidSize(maxSize)) {
const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE];
if (puglIsValidArea(maxSize)) {
sizeHints.flags |= PMaxSize;
sizeHints.max_width = maxSize.width;
sizeHints.max_height = maxSize.height;
}

const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT];
if (puglIsValidSize(minAspect) && puglIsValidSize(maxAspect)) {
const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT];
if (puglIsValidArea(minAspect) && puglIsValidArea(maxAspect)) {
sizeHints.flags |= PAspect;
sizeHints.min_aspect.x = minAspect.width;
sizeHints.min_aspect.y = minAspect.height;
@@ -448,8 +457,8 @@ updateSizeHints(const PuglView* const view)
sizeHints.max_aspect.y = maxAspect.height;
}

const PuglViewSize fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT];
if (puglIsValidSize(fixedAspect)) {
const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT];
if (puglIsValidArea(fixedAspect)) {
sizeHints.flags |= PAspect;
sizeHints.min_aspect.x = fixedAspect.width;
sizeHints.min_aspect.y = fixedAspect.height;
@@ -513,48 +522,21 @@ clearX11Clipboard(PuglX11Clipboard* const board)
board->data.len = 0;
}

static PuglRect
getInitialFrame(PuglView* const view)
PuglPoint
puglGetAncestorCenter(const PuglView* const view)
{
if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the last configured frame
const PuglRect frame = {view->lastConfigure.x,
view->lastConfigure.y,
view->lastConfigure.width,
view->lastConfigure.height};
return frame;
}
Display* const display = view->world->impl->display;
const int screen = view->impl->screen;
XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT;
XGetWindowAttributes(display,
view->transientParent ? (Window)view->transientParent
: RootWindow(display, screen),
&ancestorAttrs);

const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width;
const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height;
const int x = view->defaultX;
const int y = view->defaultY;
if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) {
// Use the default position set with puglSetPosition while unrealized
const PuglRect frame = {
(PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight};
return frame;
}

// Get the best "parentish" window to position the window in
Display* const display = view->world->impl->display;
const Window parent =
(view->parent ? (Window)view->parent
: view->transientParent ? (Window)view->transientParent
: RootWindow(display, view->impl->screen));

// Get the position/size of the parent as bounds for the new window
XWindowAttributes parentAttrs = PUGL_INIT_STRUCT;
XGetWindowAttributes(display, parent, &parentAttrs);

// Center the frame within the parent bounds
const int centerX = parentAttrs.x + parentAttrs.width / 2;
const int centerY = parentAttrs.y + parentAttrs.height / 2;
const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)),
(PuglCoord)(centerY - (defaultHeight / 2)),
defaultWidth,
defaultHeight};
return frame;
const PuglPoint center = {
(PuglCoord)(ancestorAttrs.x + (ancestorAttrs.width / 2)),
(PuglCoord)(ancestorAttrs.y + (ancestorAttrs.height / 2))};
return center;
}

PuglStatus
@@ -609,16 +591,17 @@ puglRealize(PuglView* const view)
attr.event_mask |= StructureNotifyMask;
attr.event_mask |= VisibilityChangeMask;

// Calculate the initial window rectangle
const PuglRect initialFrame = getInitialFrame(view);
// Calculate the initial window frame
const PuglArea initialSize = puglGetInitialSize(view);
const PuglPoint initialPos = puglGetInitialPosition(view, initialSize);

// Create the window
impl->win = XCreateWindow(display,
parent,
initialFrame.x,
initialFrame.y,
initialFrame.width,
initialFrame.height,
initialPos.x,
initialPos.y,
initialSize.width,
initialSize.height,
0,
impl->vi->depth,
InputOutput,
@@ -650,7 +633,7 @@ puglRealize(PuglView* const view)
if (XRRQueryExtension(display, &ignored, &ignored)) {
// Set refresh rate hint to the real refresh rate
XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent);
short current_rate = XRRConfigCurrentRate(conf);
const short current_rate = XRRConfigCurrentRate(conf);

view->hints[PUGL_REFRESH_RATE] = current_rate;
XRRFreeScreenConfigInfo(conf);
@@ -663,7 +646,7 @@ puglRealize(PuglView* const view)
XSetClassHint(display, impl->win, &classHint);
puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]);
puglSetTransientParent(view, view->transientParent);
updateSizeHints(view);
puglUpdateSizeHints(view);

// Set PID and hostname so the window manager can access our process
char hostname[256] = PUGL_INIT_STRUCT;
@@ -746,34 +729,29 @@ puglUnrealize(PuglView* const view)
impl->vi = NULL;

memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent));
memset(&view->impl->pendingConfigure, 0, sizeof(PuglEvent));
memset(&view->impl->pendingExpose, 0, sizeof(PuglEvent));

if (impl->mapped) {
view->impl->pendingConfigure.configure.style |= PUGL_VIEW_STYLE_MAPPED;
}

return PUGL_SUCCESS;
}

PuglStatus
puglShow(PuglView* const view, const PuglShowCommand command)
{
PuglStatus st = view->impl->win ? PUGL_SUCCESS : puglRealize(view);
PuglInternals* impl = view->impl;
PuglStatus st = impl->win ? PUGL_SUCCESS : puglRealize(view);

if (!st) {
switch (command) {
case PUGL_SHOW_PASSIVE:
XMapWindow(view->world->impl->display, view->impl->win);
XMapWindow(view->world->impl->display, impl->win);
break;
case PUGL_SHOW_RAISE:
case PUGL_SHOW_FORCE_RAISE:
XMapRaised(view->world->impl->display, view->impl->win);
XMapRaised(view->world->impl->display, impl->win);
break;
}

if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) {
st = puglPostRedisplay(view);
st = puglObscureView(view);
}
}

@@ -783,6 +761,10 @@ puglShow(PuglView* const view, const PuglShowCommand command)
PuglStatus
puglHide(PuglView* const view)
{
if (view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_BAD_CALL;
}

XUnmapWindow(view->world->impl->display, view->impl->win);
return PUGL_SUCCESS;
}
@@ -817,13 +799,13 @@ keyInRange(const KeySym xSym,
const PuglKey puglMin)
{
return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin))
: (PuglKey)0;
: PUGL_KEY_NONE;
}

static PuglKey
keySymToSpecial(const KeySym sym)
{
PuglKey key = (PuglKey)0;
PuglKey key = PUGL_KEY_NONE;
if ((key = keyInRange(sym, XK_F1, XK_F12, PUGL_KEY_F1)) ||
(key = keyInRange(sym, XK_Page_Up, XK_End, PUGL_KEY_PAGE_UP)) ||
(key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) ||
@@ -856,7 +838,7 @@ keySymToSpecial(const KeySym sym)
}
// clang-format on

return (PuglKey)0;
return PUGL_KEY_NONE;
}

static int
@@ -897,7 +879,8 @@ translateKey(PuglView* const view, XEvent* const xevent, PuglEvent* const event)
event->key.key = (PuglKey)puglDecodeUTF8((const uint8_t*)ustr);
}

if (xevent->type == KeyPress && !filter && !special && view->impl->xic) {
if (xevent->type == KeyPress && !filter && (!special || ufound > 0) &&
view->impl->xic) {
// Lookup shifted key for possible text event
xevent->xkey.state = state;

@@ -1028,7 +1011,7 @@ translateClientMessage(PuglView* const view, XClientMessageEvent message)
{
Display* const display = view->world->impl->display;
const PuglX11Atoms* const atoms = &view->world->impl->atoms;
PuglEvent event = {{PUGL_NOTHING, 0}};
PuglEvent event = {{PUGL_NOTHING, 0U}};

if (message.message_type == atoms->WM_PROTOCOLS) {
const Atom protocol = (Atom)message.data.l[0];
@@ -1083,6 +1066,7 @@ getCurrentViewStyleFlags(PuglView* const view)
state |= PUGL_VIEW_STYLE_DEMANDING;
}
}
XFree(hints);
}

if (view->impl->mapped) {
@@ -1101,17 +1085,19 @@ getCurrentConfiguration(PuglView* const view)
XWindowAttributes attrs;
XGetWindowAttributes(display, view->impl->win, &attrs);

// Get window position relative to the root window
// Get window position (relative to the root window if not a child)
Window ignoredChild = 0;
int rootX = 0;
int rootY = 0;
XTranslateCoordinates(
display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild);
int x = attrs.x;
int y = attrs.y;
if (!view->parent) {
XTranslateCoordinates(
display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild);
}

// Build a configure event based on the current window configuration
PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}};
configureEvent.configure.x = (PuglCoord)rootX;
configureEvent.configure.y = (PuglCoord)rootY;
PuglEvent configureEvent = {{PUGL_CONFIGURE, 0U}};
configureEvent.configure.x = (PuglCoord)x;
configureEvent.configure.y = (PuglCoord)y;
configureEvent.configure.width = (PuglSpan)attrs.width;
configureEvent.configure.height = (PuglSpan)attrs.height;
configureEvent.configure.style = getCurrentViewStyleFlags(view);
@@ -1119,28 +1105,12 @@ getCurrentConfiguration(PuglView* const view)
return configureEvent;
}

static PuglEvent
makeConfigureEvent(PuglView* const view)
{
PuglEvent event = view->impl->pendingConfigure;

if (event.type != PUGL_CONFIGURE) {
event = getCurrentConfiguration(view);
} else if (view->impl->mapped) {
event.configure.style |= PUGL_VIEW_STYLE_MAPPED;
} else {
event.configure.style &= ~(PuglViewStyleFlags)PUGL_VIEW_STYLE_MAPPED;
}

return event;
}

static PuglEvent
translatePropertyNotify(PuglView* const view, XPropertyEvent message)
{
const PuglInternals* const impl = view->impl;
const PuglX11Atoms* const atoms = &view->world->impl->atoms;
PuglEvent event = {{PUGL_NOTHING, 0}};
PuglEvent event = {{PUGL_NOTHING, 0U}};

if (message.atom == atoms->NET_WM_STATE) {
// Get all the current states set in the window hints
@@ -1151,8 +1121,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message)
}

// Make a configure event based on the current configuration to update
event = makeConfigureEvent(view);
event.configure.style = getCurrentViewStyleFlags(view); // FIXME: necessary?
event = getCurrentConfiguration(view);

XFree(hints);
} else if (message.atom == atoms->NET_FRAME_EXTENTS) {
@@ -1190,7 +1159,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message)
static PuglEvent
translateEvent(PuglView* const view, XEvent xevent)
{
PuglEvent event = {{PUGL_NOTHING, 0}};
PuglEvent event = {{PUGL_NOTHING, 0U}};
event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0;

switch (xevent.type) {
@@ -1201,18 +1170,21 @@ translateEvent(PuglView* const view, XEvent xevent)
event = translatePropertyNotify(view, xevent.xproperty);
break;
case VisibilityNotify:
event = makeConfigureEvent(view);
event = getCurrentConfiguration(view);
break;
case MapNotify:
view->impl->mapped = true;
event = makeConfigureEvent(view);
event = getCurrentConfiguration(view);
break;
case UnmapNotify:
view->impl->mapped = false;
event = makeConfigureEvent(view);
event = getCurrentConfiguration(view);
break;
case DestroyNotify:
view->impl->win = None;
break;
case ConfigureNotify:
event = makeConfigureEvent(view);
event = getCurrentConfiguration(view);
event.configure.width = (PuglSpan)xevent.xconfigure.width;
event.configure.height = (PuglSpan)xevent.xconfigure.height;
if (view->parent) {
@@ -1503,7 +1475,7 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event)
PuglInternals* const impl = view->impl;
Display* const display = view->world->impl->display;
XEvent xev = PUGL_INIT_STRUCT;
if (!impl->win) {
if (!impl->win || view->world->state == PUGL_WORLD_EXPOSING) {
return PUGL_FAILURE;
}

@@ -1534,21 +1506,11 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event)
return PUGL_UNSUPPORTED;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglWaitForEvent(PuglView* const view)
{
XEvent xevent;
XPeekEvent(view->world->impl->display, &xevent);
return PUGL_SUCCESS;
}
#endif

static void
mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src)
{
if (!dst->type) {
if (src->width > 0.0 && src->height > 0.0) {
if (src->width && src->height) {
*dst = *src;
}
} else {
@@ -1612,7 +1574,7 @@ handleSelectionNotify(const PuglWorld* const world,
Display* const display = view->world->impl->display;
const Atom selection = event->selection;
PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection);
PuglEvent puglEvent = {{PUGL_NOTHING, 0}};
PuglEvent puglEvent = {{PUGL_NOTHING, 0U}};

if (event->target == atoms->TARGETS) {
// Notification of available datatypes
@@ -1622,7 +1584,7 @@ handleSelectionNotify(const PuglWorld* const world,
view, event->requestor, event->property, &numFormats, &formats) &&
!setClipboardFormats(view, board, numFormats, formats)) {
const PuglDataOfferEvent offer = {
PUGL_DATA_OFFER, 0, (double)event->time / 1e3};
PUGL_DATA_OFFER, 0U, (double)event->time / 1e3};

puglEvent.offer = offer;
board->acceptedFormatIndex = UINT32_MAX;
@@ -1699,48 +1661,35 @@ handleSelectionRequest(const PuglWorld* const world,
}

/// Flush pending configure and expose events for all views
PUGL_WARN_UNUSED_RESULT
static PuglStatus
PUGL_WARN_UNUSED_RESULT static PuglStatus
flushExposures(PuglWorld* const world)
{
PuglStatus st0 = PUGL_SUCCESS;
PuglStatus st1 = PUGL_SUCCESS;
PuglStatus st2 = PUGL_SUCCESS;

// Send update events so the application can trigger redraws
for (size_t i = 0; i < world->numViews; ++i) {
PuglView* const view = world->views[i];

// Send update event so the application can trigger redraws
if (puglGetVisible(view)) {
puglDispatchSimpleEvent(view, PUGL_UPDATE);
if (puglGetVisible(world->views[i])) {
puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE);
}
}

// Copy and reset pending events (in case their handlers write new ones)
const PuglEvent configure = view->impl->pendingConfigure;
const PuglEvent expose = view->impl->pendingExpose;

view->impl->pendingConfigure.type = PUGL_NOTHING;
view->impl->pendingExpose.type = PUGL_NOTHING;

if (expose.type || configure.type) {
const PuglExposeEvent* const exposeEvent =
expose.type ? &expose.expose : NULL;

if (!(st0 = view->backend->enter(view, exposeEvent))) {
if (configure.type) {
st0 = puglConfigure(view, &configure);
}
// Expose any dirty views
world->state = PUGL_WORLD_EXPOSING;
for (size_t i = 0; i < world->numViews; ++i) {
PuglView* const view = world->views[i];

if (expose.type) {
st1 = view->eventFunc(view, &expose);
}
if (puglGetVisible(view)) {
const PuglEvent expose = view->impl->pendingExpose;
if (expose.type && !(st0 = view->backend->enter(view, &expose.expose))) {
st0 = view->eventFunc(view, &expose);
st1 = view->backend->leave(view, &expose.expose);
view->impl->pendingExpose.type = PUGL_NOTHING;
}

st2 = view->backend->leave(view, exposeEvent);
}
}

return st0 ? st0 : st1 ? st1 : st2;
return st0 ? st0 : st1;
}

static bool
@@ -1753,7 +1702,7 @@ handleTimerEvent(PuglWorld* const world, const XEvent xevent)

for (size_t i = 0; i < world->impl->numTimers; ++i) {
if (world->impl->timers[i].alarm == notify->alarm) {
PuglEvent event = {{PUGL_TIMER, 0}};
PuglEvent event = {{PUGL_TIMER, 0U}};
event.timer.id = world->impl->timers[i].id;
puglDispatchEvent(world->impl->timers[i].view, &event);
}
@@ -1821,10 +1770,6 @@ dispatchX11Events(PuglWorld* const world)
const PuglEvent event = translateEvent(view, xevent);

switch (event.type) {
case PUGL_CONFIGURE:
// Update configure event to be dispatched after loop
view->impl->pendingConfigure = event;
break;
case PUGL_EXPOSE:
// Expand expose event to be dispatched after loop
mergeExposeEvents(&view->impl->pendingExpose.expose, &event.expose);
@@ -1851,26 +1796,24 @@ dispatchX11Events(PuglWorld* const world)
return st;
}

#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* const view)
{
return puglUpdate(view->world, 0.0);
}
#endif

PuglStatus
puglUpdate(PuglWorld* const world, const double timeout)
{
const double startTime = puglGetTime(world);
PuglStatus st0 = PUGL_SUCCESS;
PuglStatus st1 = PUGL_SUCCESS;
const double startTime = puglGetTime(world);
const PuglWorldState startState = world->state;
PuglStatus st0 = PUGL_SUCCESS;
PuglStatus st1 = PUGL_SUCCESS;

world->impl->dispatchingEvents = true;
if (startState == PUGL_WORLD_IDLE) {
world->state = PUGL_WORLD_UPDATING;
} else if (startState != PUGL_WORLD_RECURSING) {
return PUGL_BAD_CALL;
}

if (timeout < 0.0) {
st0 = pollX11Socket(world, timeout);
st0 = st0 ? st0 : dispatchX11Events(world);
if (!(st0 = pollX11Socket(world, timeout))) {
st0 = dispatchX11Events(world);
}
} else if (timeout <= 0.001) {
st0 = dispatchX11Events(world);
} else {
@@ -1885,10 +1828,8 @@ puglUpdate(PuglWorld* const world, const double timeout)
}
}

st1 = flushExposures(world);

world->impl->dispatchingEvents = false;

st1 = flushExposures(world);
world->state = startState;
return st0 ? st0 : st1;
}

@@ -1896,42 +1837,56 @@ double
puglGetTime(const PuglWorld* const world)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
return 0.0;
}

return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) -
world->startTime;
}

PuglStatus
puglPostRedisplay(PuglView* const view)
puglObscureView(PuglView* const view)
{
PuglRect rect = puglGetFrame(view);
rect.x = 0;
rect.y = 0;

return puglPostRedisplayRect(view, rect);
return puglObscureRegion(
view, 0, 0, view->lastConfigure.width, view->lastConfigure.height);
}

PuglStatus
puglPostRedisplayRect(PuglView* const view, const PuglRect rect)
puglObscureRegion(PuglView* const view,
const int x,
const int y,
const unsigned width,
const unsigned height)
{
const PuglExposeEvent event = {
PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height};
if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

const PuglCoord cx = MAX((PuglCoord)0, (PuglCoord)x);
const PuglCoord cy = MAX((PuglCoord)0, (PuglCoord)y);
const PuglSpan cw = MIN(view->lastConfigure.width, (PuglSpan)width);
const PuglSpan ch = MIN(view->lastConfigure.height, (PuglSpan)height);

const PuglExposeEvent event = {PUGL_EXPOSE, 0U, cx, cy, cw, ch};

if (view->world->impl->dispatchingEvents) {
PuglStatus st = PUGL_SUCCESS;
if (view->world->state == PUGL_WORLD_UPDATING) {
// Currently dispatching events, add/expand expose for the loop end
mergeExposeEvents(&view->impl->pendingExpose.expose, &event);
} else if (view->world->state == PUGL_WORLD_EXPOSING) {
st = PUGL_BAD_CALL;
} else if (view->impl->win) {
// Not dispatching events, send an X expose so we wake up next time
PuglEvent exposeEvent = {{PUGL_EXPOSE, 0}};
PuglEvent exposeEvent = {{PUGL_EXPOSE, 0U}};
exposeEvent.expose = event;
return puglSendEvent(view, &exposeEvent);
st = puglSendEvent(view, &exposeEvent);
}

return PUGL_SUCCESS;
return st;
}

PuglNativeView
puglGetNativeView(PuglView* const view)
puglGetNativeView(const PuglView* const view)
{
return (PuglNativeView)view->impl->win;
}
@@ -1977,79 +1932,21 @@ puglGetScaleFactor(const PuglView* const view)
}

PuglStatus
puglSetFrame(PuglView* const view, const PuglRect frame)
{
if (!view->impl->win) {
// Set defaults to be used when realized
view->defaultX = frame.x;
view->defaultY = frame.y;
view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height;
return PUGL_SUCCESS;
}

return puglX11Status(XMoveResizeWindow(view->world->impl->display,
view->impl->win,
frame.x,
frame.y,
frame.width,
frame.height));
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
puglSetWindowPosition(PuglView* const view, const int x, const int y)
{
Display* const display = view->world->impl->display;

if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->win) {
// Set defaults to be used when realized
view->defaultX = x;
view->defaultY = y;
return PUGL_SUCCESS;
}

return puglX11Status(XMoveWindow(display,
return puglX11Status(XMoveWindow(view->world->impl->display,
view->impl->win,
(int)(x - view->impl->frameExtentLeft),
(int)(y - view->impl->frameExtentTop)));
}

PuglStatus
puglSetSize(PuglView* const view, const unsigned width, const unsigned height)
{
Display* const display = view->world->impl->display;

if (width > INT16_MAX || height > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->win) {
// Set defaults to be used when realized
view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height;
return PUGL_SUCCESS;
}

return puglX11Status(XResizeWindow(display, view->impl->win, width, height));
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
puglSetWindowSize(PuglView* const view,
const unsigned width,
const unsigned height)
{
if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) {
return PUGL_BAD_PARAMETER;
}

view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;
return updateSizeHints(view);
return puglX11Status(
XResizeWindow(view->world->impl->display, view->impl->win, width, height));
}

PuglStatus


+ 3
- 7
dpf/dgl/src/pugl-upstream/src/x11.h View File

@@ -7,8 +7,8 @@
#include "attributes.h"
#include "types.h"

#include "pugl/attributes.h"
#include "pugl/pugl.h"
#include <pugl/attributes.h>
#include <pugl/pugl.h>

#include <X11/X.h>
#include <X11/Xlib.h>
@@ -75,7 +75,6 @@ struct PuglWorldInternalsImpl {
XID serverTimeCounter;
int syncEventBase;
bool syncSupported;
bool dispatchingEvents;
};

struct PuglInternalsImpl {
@@ -83,7 +82,6 @@ struct PuglInternalsImpl {
Window win;
XIC xic;
PuglSurface* surface;
PuglEvent pendingConfigure;
PuglEvent pendingExpose;
PuglX11Clipboard clipboard;
long frameExtentLeft;
@@ -93,9 +91,7 @@ struct PuglInternalsImpl {
bool mapped;
};

PUGL_WARN_UNUSED_RESULT
PUGL_API
PuglStatus
PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus
puglX11Configure(PuglView* view);

#endif // PUGL_SRC_X11_H

+ 9
- 9
dpf/dgl/src/pugl-upstream/src/x11_cairo.c View File

@@ -5,8 +5,8 @@
#include "types.h"
#include "x11.h"

#include "pugl/cairo.h"
#include "pugl/pugl.h"
#include <pugl/cairo.h>
#include <pugl/pugl.h>

#include <cairo-xlib.h>
#include <cairo.h>
@@ -19,10 +19,10 @@ typedef struct {
cairo_t* cr;
} PuglX11CairoSurface;

static PuglViewSize
static PuglArea
puglX11CairoGetViewSize(const PuglView* const view)
{
PuglViewSize size = {0U, 0U};
PuglArea size = {0U, 0U};

if (view->lastConfigure.type == PUGL_CONFIGURE) {
// Use the size of the last configured frame
@@ -97,11 +97,11 @@ puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose)
PuglStatus st = PUGL_SUCCESS;

if (expose) {
const PuglViewSize viewSize = puglX11CairoGetViewSize(view);
const PuglSpan right = (PuglSpan)(expose->x + expose->width);
const PuglSpan bottom = (PuglSpan)(expose->y + expose->height);
const PuglSpan surfaceWidth = MAX(right, viewSize.width);
const PuglSpan surfaceHeight = MAX(bottom, viewSize.height);
const PuglArea viewSize = puglX11CairoGetViewSize(view);
const PuglSpan right = (PuglSpan)(expose->x + expose->width);
const PuglSpan bottom = (PuglSpan)(expose->y + expose->height);
const PuglSpan surfaceWidth = MAX(right, viewSize.width);
const PuglSpan surfaceHeight = MAX(bottom, viewSize.height);
if (!(st = puglX11CairoOpen(view, surfaceWidth, surfaceHeight))) {
surface->cr = cairo_create(surface->front);
if (cairo_status(surface->cr)) {


+ 4
- 7
dpf/dgl/src/pugl-upstream/src/x11_gl.c View File

@@ -6,15 +6,14 @@
#include "types.h"
#include "x11.h"

#include "pugl/gl.h"
#include "pugl/pugl.h"
#include <pugl/gl.h>
#include <pugl/pugl.h>

#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

@@ -101,8 +100,7 @@ puglX11GlConfigure(PuglView* view)
return PUGL_SUCCESS;
}

PUGL_WARN_UNUSED_RESULT
static PuglStatus
PUGL_WARN_UNUSED_RESULT static PuglStatus
puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose))
{
PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
@@ -115,8 +113,7 @@ puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose))
: PUGL_FAILURE;
}

PUGL_WARN_UNUSED_RESULT
static PuglStatus
PUGL_WARN_UNUSED_RESULT static PuglStatus
puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose)
{
Display* const display = view->world->impl->display;


+ 2
- 2
dpf/dgl/src/pugl-upstream/src/x11_stub.c View File

@@ -1,13 +1,13 @@
// Copyright 2012-2021 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC

#include "pugl/stub.h"
#include <pugl/stub.h>

#include "stub.h"
#include "types.h"
#include "x11.h"

#include "pugl/pugl.h"
#include <pugl/pugl.h>

const PuglBackend*
puglStubBackend(void)


+ 2
- 2
dpf/dgl/src/pugl-upstream/src/x11_vulkan.c View File

@@ -8,8 +8,8 @@
#include "types.h"
#include "x11.h"

#include "pugl/pugl.h"
#include "pugl/vulkan.h"
#include <pugl/pugl.h>
#include <pugl/vulkan.h>

#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_xlib.h>


+ 91
- 122
dpf/dgl/src/pugl.cpp View File

@@ -250,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view)

if (view->backend != nullptr)
{
#ifdef DGL_OPENGL
#if defined(DGL_USE_GLES2)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2);
#elif defined(DGL_USE_GLES3)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
#elif defined(DGL_USE_OPENGL3)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
#else
#elif defined(DGL_OPENGL)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2);
#endif
#endif
}
else
{
@@ -277,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view)

void puglRaiseWindow(PuglView* const view)
{
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (NSWindow* const window = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
[window orderFrontRegardless];
#elif defined(DISTRHO_OS_WASM)
// this does the same as puglShow(view, PUGL_SHOW_FORCE_RAISE) + puglShow(view, PUGL_SHOW_RAISE)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
NSWindow* const window = [view->impl->wrapperView window];
[window orderFrontRegardless];
[window orderFront:view->impl->wrapperView];
#elif defined(DISTRHO_OS_WASM)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WINDOWS)
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
XRaiseWindow(view->world->impl->display, view->impl->win);
#endif
#endif
}

// --------------------------------------------------------------------------------------------------------------------
@@ -306,29 +309,31 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co
view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast<PuglSpan>(height);
}

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (view->impl->window)
{
if (const PuglStatus status = updateSizeHint(view, PUGL_MIN_SIZE))
return status;

if (const PuglStatus status = updateSizeHint(view, PUGL_FIXED_ASPECT))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;
}
#elif defined(DISTRHO_OS_WASM)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WASM)
const char* const className = view->world->strings[PUGL_CLASS_NAME];
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, width, height);
#elif defined(DISTRHO_OS_WINDOWS)
// nothing
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
if (view->impl->win)
{
if (const PuglStatus status = updateSizeHints(view))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

XFlush(view->world->impl->display);
}
#endif
#endif

return PUGL_SUCCESS;
}
@@ -340,99 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable)
{
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (PuglWindow* const window = view->impl->window)
{
const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask)
| (resizable ? NSResizableWindowMask : 0x0);
| (resizable ? NSResizableWindowMask : 0);
[window setStyleMask:style];
}
// FIXME use [view setAutoresizingMask:NSViewNotSizable] ?
#elif defined(DISTRHO_OS_WASM)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WASM)
puglUpdateSizeHints(view);
#elif defined(DISTRHO_OS_WINDOWS)
if (const HWND hwnd = view->impl->hwnd)
{
const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX)
: GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX);
SetWindowLong(hwnd, GWL_STYLE, winFlags);
}
#elif defined(HAVE_X11)
updateSizeHints(view);
#endif
#elif defined(HAVE_X11)
puglUpdateSizeHints(view);
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// set window size while also changing default

PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height)
PuglStatus puglSetSizeAndDefault(PuglView* const view, const uint width, const uint height)
{
if (width > INT16_MAX || height > INT16_MAX)
return PUGL_BAD_PARAMETER;

#ifdef DGL_USING_X11
// workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118
// NOTE troublesome if used under KDE
if (view->impl->win && !view->parent && !view->transientParent && std::getenv("KDE_SESSION_VERSION") == nullptr)
{
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0;
}
else
#endif
// set default size first
{
view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast<PuglSpan>(width);
view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast<PuglSpan>(height);
}
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_CURRENT_SIZE].width = width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = view->sizeHints[PUGL_CURRENT_SIZE].height = height;

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
// matches upstream pugl
if (view->impl->wrapperView)
{
if (const PuglStatus status = puglSetSize(view, width, height))
return status;

// nothing to do for PUGL_DEFAULT_SIZE hint

if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;
}
#elif defined(DISTRHO_OS_WASM)
d_stdout("className is %s", view->world->strings[PUGL_CLASS_NAME]);
#elif defined(DISTRHO_OS_WASM)
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

emscripten_set_canvas_element_size(view->world->strings[PUGL_CLASS_NAME], width, height);
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WINDOWS)
// matches upstream pugl, except we re-enter context after resize
if (view->impl->hwnd)
{
if (const PuglStatus status = puglSetSize(view, width, height))
return status;

// nothing to do for PUGL_DEFAULT_SIZE hint

if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;

// make sure to return context back to ourselves
puglBackendEnter(view);
}
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
// matches upstream pugl, adds flush at the end
if (view->impl->win)
{
if (const PuglStatus status = puglSetSize(view, width, height))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

// updateSizeHints will use last known size, which is not yet updated
const PuglSpan lastWidth = view->lastConfigure.width;
const PuglSpan lastHeight = view->lastConfigure.height;
view->lastConfigure.width = static_cast<PuglSpan>(width);
view->lastConfigure.height = static_cast<PuglSpan>(height);

updateSizeHints(view);

view->lastConfigure.width = lastWidth;
view->lastConfigure.height = lastHeight;
if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;

// flush size changes
XFlush(view->world->impl->display);
}
#endif
#endif

return PUGL_SUCCESS;
}
@@ -450,31 +435,6 @@ void puglOnDisplayPrepare(PuglView*)
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// DGL specific, build-specific fallback resize

void puglFallbackOnResize(PuglView* const view, const uint width, const uint height)
{
#ifdef DGL_OPENGL
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifdef DGL_USE_OPENGL3
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
#else
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();
#endif
#else
return;
// unused
(void)view;
#endif
}

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

#if defined(DISTRHO_OS_HAIKU)
@@ -618,55 +578,64 @@ void puglWin32ShowCentered(PuglView* const view)

PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world)
{
const bool wasDispatchingEvents = world->impl->dispatchingEvents;
world->impl->dispatchingEvents = true;
const PuglWorldState startState = world->state;
world->state = PUGL_WORLD_UPDATING;
PuglStatus st = PUGL_SUCCESS;

const double startTime = puglGetTime(world);
const double endTime = startTime + 0.03;
const double endTime = startTime + 0.03;

for (double t = startTime; !st && t < endTime; t = puglGetTime(world))
{
pollX11Socket(world, endTime - t);
st = dispatchX11Events(world);
if (!(st = pollX11Socket(world, endTime - t)))
st = dispatchX11Events(world);
}

world->impl->dispatchingEvents = wasDispatchingEvents;
world->state = startState;
return st;
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, set dialog window type and pid hints
// X11 specific, set dialog window type

void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone)
void puglX11SetWindowType(const PuglView* const view, const bool isStandalone)
{
const PuglInternals* const impl = view->impl;
Display* const display = view->world->impl->display;

const pid_t pid = getpid();
const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False);
XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);

#if defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE)
if (isStandalone)
{
const Atom _nwi = XInternAtom(display, "_NET_WM_ICON", False);
XChangeProperty(display, impl->win, _nwi, XA_CARDINAL, 32, PropModeReplace,
(const uchar*)DGL_X11_WINDOW_ICON_NAME, DGL_X11_WINDOW_ICON_SIZE);
const Atom NET_WM_ICON = XInternAtom(display, "_NET_WM_ICON", False);
XChangeProperty(display,
impl->win,
NET_WM_ICON,
XA_CARDINAL,
32,
PropModeReplace,
reinterpret_cast<const uchar*>(DGL_X11_WINDOW_ICON_NAME),
DGL_X11_WINDOW_ICON_SIZE);
}
#endif

const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
const Atom NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);

Atom _wts[2];
int numAtoms = 0;
Atom windowTypes[2];
int numWindowTypes = 0;

if (! isStandalone)
_wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);

_wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);

XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms);
windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);

windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);

XChangeProperty(display,
impl->win,
NET_WM_WINDOW_TYPE,
XA_ATOM,
32,
PropModeReplace,
reinterpret_cast<const uchar*>(&windowTypes),
numWindowTypes);
}

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


+ 3
- 6
dpf/dgl/src/pugl.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -84,9 +84,6 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height);
// DGL specific, build-specific drawing prepare
void puglOnDisplayPrepare(PuglView* view);

// DGL specific, build-specific fallback resize
void puglFallbackOnResize(PuglView* view, uint width, uint height);

#if defined(DISTRHO_OS_HAIKU)

// nothing here yet
@@ -121,8 +118,8 @@ void puglWin32ShowCentered(PuglView* view);
// X11 specific, update world without triggering exposure events
PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world);

// X11 specific, set dialog window type and pid hints
void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone);
// X11 specific, set dialog window type
void puglX11SetWindowType(const PuglView* view, bool isStandalone);

#endif



+ 18
- 0
dpf/distrho/DistrhoInfo.hpp View File

@@ -869,6 +869,24 @@ START_NAMESPACE_DISTRHO
*/
#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.effect"

/**
Plugin name abbreviation consisting of 2 or 3 characters in uppercase.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ABBREVIATION "DFX"

/**
Path to a in-bundle/local 200x200 PNG image file to be used as the plugin's block image asset when OFF.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF "anagram-block-off.png"

/**
Path to a in-bundle/local 200x200 PNG image file to be used as the plugin's block image asset when ON.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON "anagram-block-on.png"

/** @} */

/* ------------------------------------------------------------------------------------------------------------


+ 3
- 4
dpf/distrho/DistrhoPluginMain.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -29,15 +29,14 @@
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# include "src/DistrhoPluginLV2.cpp"
# include "src/DistrhoPluginLV2export.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_MAPI)
# include "src/DistrhoPluginMAPI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
# include "src/DistrhoPluginVST2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# include "src/DistrhoPluginVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_EXPORT)
# include "src/DistrhoPluginExport.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED)
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin();
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); }
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC)
START_NAMESPACE_DISTRHO
Plugin* createStaticPlugin() { return createPlugin(); }


+ 29
- 20
dpf/distrho/DistrhoPluginUtils.hpp View File

@@ -38,33 +38,36 @@ START_NAMESPACE_DISTRHO
const char* getBinaryFilename();

/**
Get an OS-specific directory intended to store persistent configuration data about the plugin.@n
Calling this function will ensure the dictory exists on the filesystem.@n
The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator.
*/
const char* getConfigDir();
Get a string representation of the current plugin format we are building against.@n
This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2", "VST3" or "CLAP".@n
This string is purely informational and must not be used to tweak plugin behaviour.

/**
Get an OS-specific directory intended to store "documents" for the plugin.@n
Calling this function will ensure the dictory exists on the filesystem.@n
The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator.
@note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT.
*/
const char* getDocumentsDir();
const char* getPluginFormatName() noexcept;

/**
Get the user "home" directory.@n
This function is provided only for convenience, it should not be needed under normal circunstances.
List of supported OS-specific directories to be used for getSpecialDir.
*/
const char* getHomeDir();
enum SpecialDir {
/** The user "home" directory */
kSpecialDirHome,
/** Directory intended to store persistent configuration data (in general) */
kSpecialDirConfig,
/** Directory intended to store persistent configuration data for the current plugin */
kSpecialDirConfigForPlugin,
/** Directory intended to store "documents" (in general) */
kSpecialDirDocuments,
/** Directory intended to store "documents" for the current plugin */
kSpecialDirDocumentsForPlugin,
};

/**
Get a string representation of the current plugin format we are building against.@n
This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3" or "CLAP".@n
This string is purely informational and must not be used to tweak plugin behaviour.

@note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT.
Get an OS-specific directory.@n
Calling this function will ensure the dictory exists on the filesystem.@n
The returned path always includes a final OS separator.
*/
const char* getPluginFormatName() noexcept;
const char* getSpecialDir(SpecialDir dir);

/**
Get the path to where resources are stored within the plugin bundle.@n
@@ -82,7 +85,13 @@ const char* getPluginFormatName() noexcept;
The other non-mentioned formats do not support bundles.@n

@note For CLAP and VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory
rather than only shipping with the binary (e.g. <myplugin.vst>/myplugin.dll)
rather than only shipping with the binary, like so:
@code
+ myplugin.vst/
- myplugin.dll
+ resources/
- image1.png
@endcode
*/
const char* getResourcePath(const char* bundlePath) noexcept;



+ 15
- 19
dpf/distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -106,10 +106,11 @@ public:

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;
int red = (bgColor >> 24) & 0xff;
int green = (bgColor >> 16) & 0xff;
int blue = (bgColor >> 8) & 0xff;
```
@see Color::fromRGB
*/
uint getBackgroundColor() const noexcept;

@@ -119,10 +120,11 @@ public:

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;
int red = (fgColor >> 24) & 0xff;
int green = (fgColor >> 16) & 0xff;
int blue = (fgColor >> 8) & 0xff;
```
@see Color::fromRGB
*/
uint getForegroundColor() const noexcept;

@@ -289,19 +291,13 @@ protected:
*/
virtual void uiFocus(bool focus, DGL_NAMESPACE::CrossingMode mode);

/**
Window reshape function, called when the window is resized.
This function is for plugin UIs to be able to override Window::onReshape(uint, uint).

The plugin UI size will be set right after this function.
The default implementation sets up the drawing context where necessary.

You should almost never need to override this function.
The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code.
*/
#if DGL_ALLOW_DEPRECATED_METHODS
/** DEPRECATED DO NOT USE */
DISTRHO_DEPRECATED
virtual void uiReshape(uint width, uint height);
#endif

#if DISTRHO_UI_FILE_BROWSER
#if DISTRHO_UI_FILE_BROWSER
/**
Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser().
This function is for plugin UIs to be able to override Window::onFileSelected(const char*).
@@ -312,7 +308,7 @@ protected:
If you need to use files as plugin state, please setup and use states with kStateIsFilenamePath instead.
*/
virtual void uiFileBrowserSelected(const char* filename);
#endif
#endif

/* --------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */


+ 13
- 6
dpf/distrho/DistrhoUIMain.cpp View File

@@ -23,11 +23,7 @@

#if DISTRHO_PLUGIN_HAS_UI

#if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT)
# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0
# endif
#elif defined(DISTRHO_PLUGIN_TARGET_AU)
#if defined(DISTRHO_PLUGIN_TARGET_AU)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
# import "src/DistrhoUIAU.mm"
#elif defined(DISTRHO_PLUGIN_TARGET_CARLA)
@@ -40,7 +36,13 @@
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0
# include "src/DistrhoUIDSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT)
# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0
# endif
# else
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# endif
# include "src/DistrhoUILV2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
@@ -75,6 +77,11 @@ int main(int argc, char* argv[])
{
return DISTRHO_NAMESPACE::dpf_webview_start(argc, argv);
}
#elif defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) && !DISTRHO_IS_STANDALONE
int main()
{
return 0;
}
#endif

#endif

+ 6
- 2
dpf/distrho/extra/RingBuffer.hpp View File

@@ -537,7 +537,7 @@ public:
* Commit all previous write operations to the ringbuffer.
* If a write operation has previously failed, this will reset/invalidate the previous write attempts.
*/
bool commitWrite() noexcept
bool commitWrite(const char* const debugMsg = nullptr) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);

@@ -549,7 +549,11 @@ public:
}

// nothing to commit?
DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
if (debugMsg != nullptr) {
DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(debugMsg, buffer->head != buffer->wrtn, false);
} else {
DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
}

// all ok
buffer->head = buffer->wrtn;


+ 5
- 0
dpf/distrho/extra/ScopedPointer.hpp View File

@@ -172,6 +172,11 @@ public:
/** Lets you access methods and properties of the object that this ScopedPointer refers to. */
ObjectType* operator->() const noexcept { return object; }

//==============================================================================
/** Removes the current object from this ScopedPointer and deletes it.
*/
void reset() noexcept { ObjectType* const o = object; object = nullptr; delete o; }

//==============================================================================
/** Removes the current object from this ScopedPointer without deleting it.
This will return the current object, and set the ScopedPointer to a null pointer.


+ 54
- 58
dpf/distrho/extra/String.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -269,7 +269,7 @@ public:
/*
* Get length of the string.
*/
std::size_t length() const noexcept
size_t length() const noexcept
{
return fBufferLen;
}
@@ -295,7 +295,7 @@ public:
*/
bool contains(const char c) const noexcept
{
for (std::size_t i=0; i<fBufferLen; ++i)
for (size_t i=0; i<fBufferLen; ++i)
{
if (fBuffer[i] == c)
return true;
@@ -334,7 +334,7 @@ public:
/*
* Check if character at 'pos' is a digit.
*/
bool isDigit(const std::size_t pos) const noexcept
bool isDigit(const size_t pos) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);

@@ -358,7 +358,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);

const std::size_t prefixLen(std::strlen(prefix));
const size_t prefixLen(std::strlen(prefix));

if (fBufferLen < prefixLen)
return false;
@@ -383,7 +383,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);

const std::size_t suffixLen(std::strlen(suffix));
const size_t suffixLen(std::strlen(suffix));

if (fBufferLen < suffixLen)
return false;
@@ -395,7 +395,7 @@ public:
* Find the first occurrence of character 'c' in the string.
* Returns "length()" if the character is not found.
*/
std::size_t find(const char c, bool* const found = nullptr) const noexcept
size_t find(const char c, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || c == '\0')
{
@@ -404,7 +404,7 @@ public:
return fBufferLen;
}

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
@@ -423,7 +423,7 @@ public:
* Find the first occurrence of string 'strBuf' in the string.
* Returns "length()" if the string is not found.
*/
std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
{
@@ -448,7 +448,7 @@ public:

if (found != nullptr)
*found = true;
return static_cast<std::size_t>(ret);
return static_cast<size_t>(ret);
}

if (found != nullptr)
@@ -460,7 +460,7 @@ public:
* Find the last occurrence of character 'c' in the string.
* Returns "length()" if the character is not found.
*/
std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
size_t rfind(const char c, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || c == '\0')
{
@@ -469,7 +469,7 @@ public:
return fBufferLen;
}

for (std::size_t i=fBufferLen; i > 0; --i)
for (size_t i=fBufferLen; i > 0; --i)
{
if (fBuffer[i-1] == c)
{
@@ -488,7 +488,7 @@ public:
* Find the last occurrence of string 'strBuf' in the string.
* Returns "length()" if the string is not found.
*/
std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
{
if (found != nullptr)
*found = false;
@@ -496,12 +496,12 @@ public:
if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
return fBufferLen;

const std::size_t strBufLen(std::strlen(strBuf));
const size_t strBufLen(std::strlen(strBuf));

std::size_t ret = fBufferLen;
size_t ret = fBufferLen;
const char* tmpBuf = fBuffer;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
{
@@ -532,7 +532,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == before)
fBuffer[i] = after;
@@ -551,7 +551,7 @@ public:
if (fBufferLen == 0)
return *this;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
@@ -567,7 +567,7 @@ public:
/*
* Truncate the string to size 'n'.
*/
String& truncate(const std::size_t n) noexcept
String& truncate(const size_t n) noexcept
{
if (n >= fBufferLen)
return *this;
@@ -583,7 +583,7 @@ public:
*/
String& toBasic() noexcept
{
for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
continue;
@@ -607,7 +607,7 @@ public:
{
static constexpr const char kCharDiff = 'a' - 'A';

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
@@ -623,7 +623,7 @@ public:
{
static constexpr const char kCharDiff = 'a' - 'A';

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
@@ -688,31 +688,28 @@ public:
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
// Copyright (C) 2004-2008 René Nyffenegger

static String asBase64(const void* const data, const std::size_t dataSize)
static String asBase64(const void* const data, const size_t dataSize)
{
static constexpr const char* const kBase64Chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

#ifndef _MSC_VER
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
#else
constexpr std::size_t kTmpBufSize = 65536U;
#endif
const size_t strBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
char* strBuf = static_cast<char*>(std::malloc(strBufSize));
DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, String());

const uchar* bytesToEncode((const uchar*)data);
strBuf[strBufSize - 1] = '\0';
size_t strBufIndex = 0;

const uchar* bytesToEncode = static_cast<const uchar*>(data);

uint i=0, j=0;
uint charArray3[3], charArray4[4];

char strBuf[kTmpBufSize + 1];
strBuf[kTmpBufSize] = '\0';
std::size_t strBufIndex = 0;

String ret;

for (std::size_t s=0; s<dataSize; ++s)
for (size_t s = 0; s < dataSize; ++s)
{
charArray3[i++] = *(bytesToEncode++);

@@ -723,10 +720,10 @@ public:
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;

for (i=0; i<4; ++i)
for (i = 0; i < 4; ++i)
strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];

if (strBufIndex >= kTmpBufSize-7)
if (strBufIndex >= strBufSize - 7)
{
strBuf[strBufIndex] = '\0';
strBufIndex = 0;
@@ -739,7 +736,7 @@ public:

if (i != 0)
{
for (j=i; j<3; ++j)
for (j = i; j < 3; ++j)
charArray3[j] = '\0';

charArray4[0] = (charArray3[0] & 0xfc) >> 2;
@@ -747,7 +744,7 @@ public:
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;

for (j=0; j<4 && i<3 && j<i+1; ++j)
for (j = 0; j < 4 && i < 3 && j < i + 1; ++j)
strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];

for (; i++ < 3;)
@@ -760,6 +757,7 @@ public:
ret += strBuf;
}

std::free(strBuf);
return ret;
}

@@ -778,7 +776,7 @@ public:

char* newbufptr = newbuf;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
const char c = fBuffer[i];

@@ -901,7 +899,7 @@ public:

char* newbufptr = newbuf;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
const char c = fBuffer[i];

@@ -957,19 +955,16 @@ public:
return fBuffer;
}

char operator[](const std::size_t pos) const noexcept
char operator[](const size_t pos) const noexcept
{
if (pos < fBufferLen)
return fBuffer[pos];

d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);

static char fallback;
fallback = '\0';
return fallback;
return '\0';
}

char& operator[](const std::size_t pos) noexcept
char& operator[](const size_t pos) noexcept
{
if (pos < fBufferLen)
return fBuffer[pos];
@@ -1020,7 +1015,7 @@ public:
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;

const std::size_t strBufLen = std::strlen(strBuf);
const size_t strBufLen = std::strlen(strBuf);

// for empty strings, we can just take the appended string as our entire data
if (isEmpty())
@@ -1030,13 +1025,14 @@ public:
}

// we have some data ourselves, reallocate to add the new stuff
char* const newBuf = static_cast<char*>(std::realloc(fBuffer, fBufferLen + strBufLen + 1));
char* const newBuf = static_cast<char*>(std::realloc(fBufferAlloc ? fBuffer : nullptr, fBufferLen + strBufLen + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);

std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

fBuffer = newBuf;
fBufferLen += strBufLen;
fBufferAlloc = true;

return *this;
}
@@ -1053,8 +1049,8 @@ public:
if (isEmpty())
return String(strBuf);

const std::size_t strBufLen = std::strlen(strBuf);
const std::size_t newBufSize = fBufferLen + strBufLen;
const size_t strBufLen = std::strlen(strBuf);
const size_t newBufSize = fBufferLen + strBufLen;
char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

@@ -1079,7 +1075,7 @@ public:

private:
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
size_t fBufferLen; // string length
bool fBufferAlloc; // wherever the buffer is allocated, not using _null()

/*
@@ -1100,7 +1096,7 @@ private:
* - Allocates string only if 'strBuf' is not null and new string contents are different
* - If 'strBuf' is null, 'size' must be 0
*/
void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
void _dup(const char* const strBuf, const size_t size = 0) noexcept
{
if (strBuf != nullptr)
{
@@ -1157,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep
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;
const size_t strBeforeLen = strBefore.length();
const size_t strBufAfterLen = std::strlen(strBufAfter);
const size_t newBufSize = strBeforeLen + strBufAfterLen;
char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

@@ -1177,9 +1173,9 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep
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;
const size_t strBufBeforeLen = std::strlen(strBufBefore);
const size_t strAfterLen = strAfter.length();
const size_t newBufSize = strBufBeforeLen + strAfterLen;
char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());



+ 9
- 6
dpf/distrho/src/DistrhoPluginCLAP.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -286,7 +286,7 @@ public:
#else
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
fPlugin.getInstancePointer(), scaleFactor);
fPlugin.getInstancePointer(), scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
*width = tmpUI.getWidth();
*height = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();
@@ -306,13 +306,15 @@ public:
#if DISTRHO_UI_USER_RESIZABLE
if (UIExporter* const ui = fUI.get())
return ui->isResizable();
#endif
return true;
#else
return false;
#endif
}

bool getResizeHints(clap_gui_resize_hints_t* const hints) const
{
if (canResize())
if (fUI != nullptr && fUI->isResizable())
{
uint minimumWidth, minimumHeight;
bool keepAspectRatio;
@@ -344,7 +346,7 @@ public:

bool adjustSize(uint32_t* const width, uint32_t* const height) const
{
if (canResize())
if (fUI != nullptr && fUI->isResizable())
{
uint minimumWidth, minimumHeight;
bool keepAspectRatio;
@@ -582,7 +584,8 @@ private:
nullptr, // TODO fileRequestCallback,
d_nextBundlePath,
fPlugin.getInstancePointer(),
fScaleFactor);
fScaleFactor,
DGL_NAMESPACE::Application::kTypeClassic);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
fUI->programLoaded(fCurrentProgram);


+ 1
- 1
dpf/distrho/src/DistrhoPluginJACK.cpp View File

@@ -552,7 +552,7 @@ protected:
midiData[1] = note;
midiData[2] = velocity;
fNotesRingBuffer.writeCustomData(midiData, 3);
fNotesRingBuffer.commitWrite();
fNotesRingBuffer.commitWrite("PluginJack::sendNote");
}
# endif



+ 17
- 0
dpf/distrho/src/DistrhoPluginLV2export.cpp View File

@@ -367,6 +367,7 @@ void lv2_generate_ttl(const char* const basename)
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
#endif
pluginString += "@prefix dg: <http://www.darkglass.com/lv2/ns#> .\n";
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
@@ -997,6 +998,22 @@ void lv2_generate_ttl(const char* const basename)
}
}

// Darkglass Anagram
#ifdef DISTRHO_PLUGIN_ABBREVIATION
pluginString += " dg:abbreviation \"" DISTRHO_PLUGIN_ABBREVIATION "\" ;\n";
#endif
#ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF
pluginString += " dg:blockImageOff <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF "> ;\n";
#endif
#ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON
pluginString += " dg:blockImageOn <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON "> ;\n";
#endif
#if defined(DISTRHO_PLUGIN_ABBREVIATION) || \
defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF) || \
defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON)
pluginString += "\n";
#endif

#ifdef DISTRHO_PLUGIN_BRAND
// MOD
pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n";


+ 158
- 0
dpf/distrho/src/DistrhoPluginMAPI.cpp View File

@@ -0,0 +1,158 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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 "DistrhoPluginInternal.hpp"

#ifndef DISTRHO_NO_WARNINGS
# if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
# error Cannot use parameter value change request with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error Cannot use MIDI with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_FULL_STATE
# error Cannot use full state with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_TIMEPOS
# error Cannot use time position with MAPI
# endif
#endif

#include "mapi/mapi.h"

START_NAMESPACE_DISTRHO

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

class PluginMAPI
{
public:
PluginMAPI()
: fPlugin(nullptr, nullptr, nullptr, nullptr)
{
fPlugin.activate();
}

~PluginMAPI() noexcept
{
fPlugin.deactivate();
}

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

void process(const float* const* ins, float** outs, unsigned int frames)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fPlugin.run(const_cast<const float**>(ins), outs, frames, nullptr, 0);
#else
fPlugin.run(const_cast<const float**>(ins), outs, frames);
#endif

updateParameterOutputsAndTriggers();
}

void setParameter(unsigned int index, float value)
{
fPlugin.setParameterValue(index, fPlugin.getParameterRanges(index).getFixedValue(value));
}

void setState(const char* key, const char* value)
{
#if DISTRHO_PLUGIN_WANT_STATE
fPlugin.setState(key, value);
#else
// unused
(void)key;
(void)value;
#endif
}

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

private:
PluginExporter fPlugin;

void updateParameterOutputsAndTriggers()
{
float value;

for (uint32_t i = 0, count = fPlugin.getParameterCount(); i < count; ++i)
{
if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
{
// NOTE: no trigger support in MAPI, simulate it here
value = fPlugin.getParameterRanges(i).def;

if (d_isEqual(value, fPlugin.getParameterValue(i)))
continue;

fPlugin.setParameterValue(i, value);
}
}
}
};

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

MAPI_EXPORT
mapi_handle_t mapi_create(unsigned int sample_rate)
{
if (d_nextBufferSize == 0)
{
#if defined(_DARKGLASS_DEVICE_PABLITO)
d_nextBufferSize = 16;
#elif defined(__MOD_DEVICES__)
d_nextBufferSize = 128;
#else
d_nextBufferSize = 2048;
#endif
}

d_nextSampleRate = sample_rate;

return new PluginMAPI();
}

MAPI_EXPORT
void mapi_process(mapi_handle_t handle,
const float* const* ins,
float** outs,
unsigned int frames)
{
static_cast<PluginMAPI*>(handle)->process(ins, outs, frames);
}

MAPI_EXPORT
void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value)
{
static_cast<PluginMAPI*>(handle)->setParameter(index, value);
}

MAPI_EXPORT
void mapi_set_state(mapi_handle_t handle, const char* key, const char* value)
{
static_cast<PluginMAPI*>(handle)->setState(key, value);
}

MAPI_EXPORT
void mapi_destroy(mapi_handle_t handle)
{
delete static_cast<PluginMAPI*>(handle);
}

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

END_NAMESPACE_DISTRHO

+ 6
- 5
dpf/distrho/src/DistrhoPluginVST2.cpp View File

@@ -175,7 +175,8 @@ public:
nullptr, // TODO file request
d_nextBundlePath,
plugin->getInstancePointer(),
scaleFactor),
scaleFactor,
DGL_NAMESPACE::Application::kTypeClassic),
fKeyboardModifiers(0)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
, fNotesRingBuffer()
@@ -398,8 +399,8 @@ public:
{
parameterValues = new float[parameterCount];

for (uint32_t i=0; i < parameterCount; ++i)
parameterValues[i] = NAN;
for (uint32_t i = 0; i < parameterCount; ++i)
parameterValues[i] = fPlugin.getParameterDefault(i);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -600,7 +601,7 @@ public:
#else
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
fPlugin.getInstancePointer(), scaleFactor);
fPlugin.getInstancePointer(), scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
fVstRect.right = tmpUI.getWidth();
fVstRect.bottom = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();
@@ -1709,7 +1710,7 @@ const vst_effect* VSTPluginMain(const vst_host_callback audioMaster)
return effect;
}

#if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_UI_WEB_VIEW)
#if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || (defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW)))
DISTRHO_PLUGIN_EXPORT
const vst_effect* VSTPluginMainCompat(vst_host_callback) asm ("main");



+ 3
- 3
dpf/distrho/src/DistrhoUI.cpp View File

@@ -560,11 +560,11 @@ void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode)
{
}

void UI::uiReshape(const uint width, const uint height)
#if DGL_ALLOW_DEPRECATED_METHODS
void UI::uiReshape(uint, uint)
{
// NOTE this must be the same as Window::onReshape
pData->fallbackOnResize(width, height);
}
#endif

#if DISTRHO_UI_FILE_BROWSER
void UI::uiFileBrowserSelected(const char*)


+ 6
- 5
dpf/distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,18 +21,18 @@

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI exporter class

class UIExporter
{
// -------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------
// UI Widget and its private data

UI* ui;
UI::PrivateData* uiData;

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

public:
UIExporter(void* const callbacksPtr,
@@ -47,11 +47,12 @@ public:
const char* const bundlePath = nullptr,
void* const dspPtr = nullptr,
const double scaleFactor = 0.0,
const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff,
const char* const appClassName = nullptr)
: ui(nullptr),
uiData(new UI::PrivateData(appClassName))
uiData(new UI::PrivateData(appClassName, appType))
{
uiData->sampleRate = sampleRate;
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr;


+ 16
- 12
dpf/distrho/src/DistrhoUILV2.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -52,7 +52,7 @@ static constexpr const sendNoteFunc sendNoteCallback = nullptr;
// unwanted in LV2, resize extension is deprecated and hosts can do it without extensions
static constexpr const setSizeFunc setSizeCallback = nullptr;

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

template <class LV2F>
static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
@@ -80,6 +80,7 @@ public:
void* const dspPtr,
const float sampleRate,
const float scaleFactor,
const DGL_NAMESPACE::Application::Type appType,
const uint32_t bgColor,
const uint32_t fgColor,
const char* const appClassName)
@@ -101,7 +102,7 @@ public:
sendNoteCallback,
setSizeCallback,
fileRequestCallback,
bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName)
bundlePath, dspPtr, scaleFactor, appType, bgColor, fgColor, appClassName)
{
if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
@@ -483,7 +484,7 @@ private:
}
};

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

static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
const char* const uri,
@@ -499,6 +500,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
return nullptr;
}

// TODO allow classic vs modern ui type
static constexpr const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeClassic;

const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;
void* parentId = nullptr;
@@ -633,7 +637,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,

return new UiLv2(bundlePath, winId, options, uridMap, features,
controller, writeFunction, widget, instance,
sampleRate, scaleFactor, bgColor, fgColor, appClassName);
sampleRate, scaleFactor, appType, bgColor, fgColor, appClassName);
}

#define uiPtr ((UiLv2*)ui)
@@ -648,7 +652,7 @@ static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t buffe
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
}

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

static int lv2ui_idle(LV2UI_Handle ui)
{
@@ -665,7 +669,7 @@ static int lv2ui_hide(LV2UI_Handle ui)
return uiPtr->lv2ui_hide();
}

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

static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
{
@@ -677,7 +681,7 @@ static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* optio
return uiPtr->lv2_set_options(options);
}

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

#if DISTRHO_PLUGIN_WANT_PROGRAMS
static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
@@ -686,7 +690,7 @@ static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t progra
}
#endif

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

static const void* lv2ui_extension_data(const char* uri)
{
@@ -713,7 +717,7 @@ static const void* lv2ui_extension_data(const char* uri)

#undef instancePtr

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

static const LV2UI_Descriptor sLv2UiDescriptor = {
DISTRHO_UI_URI,
@@ -723,7 +727,7 @@ static const LV2UI_Descriptor sLv2UiDescriptor = {
lv2ui_extension_data
};

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

END_NAMESPACE_DISTRHO

@@ -929,4 +933,4 @@ void modgui_cleanup(const LV2UI_Handle handle)
}
#endif

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

+ 58
- 13
dpf/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -67,14 +67,14 @@ START_NAMESPACE_DISTRHO
int dpf_webview_start(int argc, char* argv[]);
#endif

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public DGL_NAMESPACE::Application
{
public:
explicit PluginApplication(const char* className)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE)
explicit PluginApplication(const char* className, const Application::Type type)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE, type)
{
#if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__)
if (className == nullptr)
@@ -108,14 +108,16 @@ public:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

class PluginWindow : public DGL_NAMESPACE::Window
{
UI* const ui;
bool initializing;
#if DGL_ALLOW_DEPRECATED_METHODS
bool receivedReshapeDuringInit;
#endif

public:
explicit PluginWindow(UI* const uiPtr,
@@ -130,15 +132,20 @@ public:
DISTRHO_UI_USES_SIZE_REQUEST,
false),
ui(uiPtr),
initializing(true),
receivedReshapeDuringInit(false)
initializing(true)
#if DGL_ALLOW_DEPRECATED_METHODS
, receivedReshapeDuringInit(false)
#endif
{
if (pData->view == nullptr)
return;

// this is called just before creating UI, ensuring proper context to it
if (pData->initPost())
{
puglBackendEnter(pData->view);
pData->createContextIfNeeded();
}
}

~PluginWindow() override
@@ -156,12 +163,31 @@ public:
initializing = false;
puglBackendLeave(pData->view);

#if DGL_ALLOW_DEPRECATED_METHODS
if (receivedReshapeDuringInit)
{
puglBackendEnter(pData->view);
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
ui->uiReshape(getWidth(), getHeight());
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic pop
#endif
puglBackendLeave(pData->view);
}
#endif
}

// used for temporary windows (VST/CLAP get size without active/visible view)
@@ -210,6 +236,17 @@ protected:
ui->uiFocus(focus, mode);
}

#if DGL_ALLOW_DEPRECATED_METHODS
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
void onReshape(const uint width, const uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
@@ -222,6 +259,14 @@ protected:

ui->uiReshape(width, height);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460
#pragma GCC diagnostic pop
#endif
#endif

void onScaleFactorChanged(const double scaleFactor) override
{
@@ -240,7 +285,7 @@ protected:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
@@ -250,7 +295,7 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
@@ -289,8 +334,8 @@ struct UI::PrivateData {
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData(const char* const appClassName) noexcept
: app(appClassName),
PrivateData(const char* const appClassName, const DGL_NAMESPACE::Application::Type appType) noexcept
: app(appClassName, appType),
window(nullptr),
#if DISTRHO_UI_USE_WEB_VIEW
webview(nullptr),
@@ -389,7 +434,7 @@ struct UI::PrivateData {
#endif
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

inline bool UI::PrivateData::fileRequestCallback(const char* const key)
@@ -416,7 +461,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
return false;
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

#if DISTRHO_UI_FILE_BROWSER
@@ -454,7 +499,7 @@ inline void PluginWindow::onFileSelected(const char* const filename)
}
#endif

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

END_NAMESPACE_DISTRHO



+ 3
- 2
dpf/distrho/src/DistrhoUIVST3.cpp View File

@@ -205,7 +205,8 @@ public:
nullptr, // TODO file request
d_nextBundlePath,
instancePointer,
scaleFactor)
scaleFactor,
DGL_NAMESPACE::Application::kTypeClassic)
{
}

@@ -1374,7 +1375,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
#else
UIExporter tmpUI(nullptr, 0, view->sampleRate,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
view->instancePointer, scaleFactor);
view->instancePointer, scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
rect->right = tmpUI.getWidth();
rect->bottom = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();


+ 133
- 70
dpf/distrho/src/DistrhoUtils.cpp View File

@@ -82,65 +82,124 @@ const char* getBinaryFilename()
return filename;
}

const char* getConfigDir()
const char* getPluginFormatName() noexcept
{
#if defined(DISTRHO_PLUGIN_TARGET_AU)
return "AudioUnit";
#elif defined(DISTRHO_PLUGIN_TARGET_CARLA)
return "Carla";
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
#if defined(DISTRHO_OS_WASM)
return "Wasm/Standalone";
#elif defined(HAVE_JACK)
return "JACK/Standalone";
#else
return "Standalone";
#endif
#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA)
return "LADSPA";
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI)
return "DSSI";
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
return "LV2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
return "VST2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
return "VST3";
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
return "CLAP";
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) && defined(DISTRHO_PLUGIN_TARGET_STATIC_NAME)
return DISTRHO_PLUGIN_TARGET_STATIC_NAME;
#else
return "Unknown";
#endif
}

#ifndef DISTRHO_OS_WINDOWS
static inline
void _createDirIfNeeded(const char* const dir)
{
try {
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
} DISTRHO_SAFE_EXCEPTION("createDirIfNeeded");
}
#endif

static const char* _getDocumentsDir();
static const char* _getDocumentsDirForPlugin();
static const char* _getHomeDir();

static const char* _getConfigDir()
{
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS)
return getDocumentsDir();
return _getDocumentsDir();
#else
static String dir;

if (dir.isEmpty())
{
if (const char* const xdgEnv = getenv("XDG_CONFIG_HOME"))
{
dir = xdgEnv;

if (dir.isNotEmpty() && ! dir.endsWith('/'))
dir += "/";
}

if (dir.isEmpty())
{
dir = getHomeDir();
dir += "/.config";
dir = _getHomeDir();
dir += ".config/";
}

// ensure main config dir exists
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
_createDirIfNeeded(dir);
}

// and also our custom subdir
dir += "/" DISTRHO_PLUGIN_NAME "/";
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
return dir;
#endif
}

static const char* _getConfigDirForPlugin()
{
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS)
return _getDocumentsDirForPlugin();
#else
static String dir;

if (dir.isEmpty())
{
dir = _getConfigDir();
dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR;
_createDirIfNeeded(dir);
}

return dir;
#endif
}

const char* getDocumentsDir()
static const char* _getDocumentsDir()
{
static String dir;

if (dir.isEmpty())
{
#if defined(DISTRHO_OS_MAC)
dir = getHomeDir();
dir += "/Documents/" DISTRHO_PLUGIN_NAME "/";
dir = _getHomeDir();
dir += "Documents/";
#elif defined(DISTRHO_OS_WASM)
dir = getHomeDir();
dir += "/";
dir = _getHomeDir();
#elif defined(DISTRHO_OS_WINDOWS)
WCHAR wpath[MAX_PATH];
if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK)
{
CHAR apath[MAX_PATH];
if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0)
{
dir = apath;
dir += "\\" DISTRHO_PLUGIN_NAME "\\";
wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\");
}
}
#else
String xdgDirsConfigPath(getConfigDir());
xdgDirsConfigPath += "/user-dirs.dirs";
String xdgDirsConfigPath(_getConfigDir());
xdgDirsConfigPath += "user-dirs.dirs";

if (FILE* const f = std::fopen(xdgDirsConfigPath, "r"))
{
@@ -178,17 +237,13 @@ const char* getDocumentsDir()

if (sdir.startsWith("$HOME"))
{
dir = getHomeDir();
dir += sdir.buffer() + 5;
dir = _getHomeDir();
dir += sdir.buffer() + 6;
}
else
{
dir = sdir;
}

// ensure main config dir exists
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
}
}

@@ -203,27 +258,48 @@ const char* getDocumentsDir()
// ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data
if (dir.isEmpty())
{
dir = getDocumentsDir();
dir += DISTRHO_PLUGIN_NAME "/";
dir = _getHomeDir();
dir += "Documents/";
}

_createDirIfNeeded(dir);
#endif

// ensure our custom subdir exists
if (dir.isNotEmpty())
if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP))
dir += DISTRHO_OS_SEP_STR;
}

return dir;
}

static const char* _getDocumentsDirForPlugin()
{
static String dir;

if (dir.isEmpty())
{
#ifdef DISTRHO_OS_WINDOWS
WCHAR wpath[MAX_PATH];
if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK)
{
#ifdef DISTRHO_OS_WINDOWS
wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\");
_wmkdir(wpath);
#else
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
#endif
CHAR apath[MAX_PATH];
if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0)
dir = apath;
}
#else
dir = _getDocumentsDir();
dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR;
_createDirIfNeeded(dir);
#endif
}

return dir;
}

const char* getHomeDir()
static const char* _getHomeDir()
{
static String dir;

@@ -245,45 +321,32 @@ const char* getHomeDir()
if (struct passwd* const pwd = getpwuid(getuid()))
dir = pwd->pw_dir;

if (dir.isNotEmpty() && ! dir.endsWith('/'))
dir += "/";
_createDirIfNeeded(dir);
#endif

if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP))
dir += DISTRHO_OS_SEP_STR;
}

return dir;
}

const char* getPluginFormatName() noexcept
const char* getSpecialDir(const SpecialDir dir)
{
#if defined(DISTRHO_PLUGIN_TARGET_AU)
return "AudioUnit";
#elif defined(DISTRHO_PLUGIN_TARGET_CARLA)
return "Carla";
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
#if defined(DISTRHO_OS_WASM)
return "Wasm/Standalone";
#elif defined(HAVE_JACK)
return "JACK/Standalone";
#else
return "Standalone";
#endif
#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA)
return "LADSPA";
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI)
return "DSSI";
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
return "LV2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
return "VST2";
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
return "VST3";
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
return "CLAP";
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) && defined(DISTRHO_PLUGIN_TARGET_STATIC_NAME)
return DISTRHO_PLUGIN_TARGET_STATIC_NAME;
#else
return "Unknown";
#endif
switch (dir)
{
case kSpecialDirHome:
return _getHomeDir();
case kSpecialDirConfig:
return _getConfigDir();
case kSpecialDirConfigForPlugin:
return _getConfigDirForPlugin();
case kSpecialDirDocuments:
return _getDocumentsDir();
case kSpecialDirDocumentsForPlugin:
return _getDocumentsDirForPlugin();
}
return nullptr;
}

const char* getResourcePath(const char* const bundlePath) noexcept


+ 28
- 17
dpf/distrho/src/jackbridge/JackBridge.cpp View File

@@ -1,6 +1,6 @@
/*
* JackBridge for DPF
* Copyright (C) 2013-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2025 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
@@ -68,6 +68,9 @@ typedef void* lib_t;
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# pragma clang diagnostic ignored "-Wunused-but-set-variable"
# if __clang_major__ >= 17
# pragma clang diagnostic ignored "-Wvla-cxx-extension"
# endif
# endif
# include "RtAudioBridge.hpp"
# ifdef RTAUDIO_API_TYPE
@@ -467,29 +470,37 @@ struct JackBridge {
#endif
{
#ifdef HAVE_JACK
#if defined(DISTRHO_OS_MAC)
const char* const filename = "libjack.dylib";
#elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64)
const char* const filename = "libjack64.dll";
#elif defined(DISTRHO_OS_WINDOWS)
const char* const filename = "libjack.dll";
#else
const char* const filename = "libjack.so.0";
#endif
static constexpr const char* const filenames[] = {
#if defined(DISTRHO_OS_MAC)
"libjack.0.dylib",
"/usr/local/lib/libjack.0.dylib",
#elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64)
"libjack64.dll",
#elif defined(DISTRHO_OS_WINDOWS)
"libjack.dll",
#else
"libjack.so.0",
#endif
};

USE_NAMESPACE_DISTRHO

lib = lib_open(filename);
for (uint i = 0; i < ARRAY_SIZE(filenames); ++i)
{
lib = lib_open(filenames[i]);

if (lib != nullptr)
{
d_stdout("%s loaded successfully!", filenames[i]);
break;
}
}

if (lib == nullptr)
{
d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filename));
d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filenames[0]));
return;
}
else
{
d_stdout("%s loaded successfully!", filename);
}

#define JOIN(a, b) a ## b
#define LIB_SYMBOL(NAME) JOIN(NAME, _ptr) = lib_symbol<jacksym_##NAME>(lib, "jack_" #NAME);
@@ -951,7 +962,7 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options,
return kValidClient;
delete nativeBridge;
#endif
#if defined(HAVE_RTAUDIO) && defined(RTAUDIO_API_TYPE)
nativeBridge = new RtAudioBridge;
if (nativeBridge->open(client_name))


+ 46
- 20
dpf/distrho/src/jackbridge/NativeBridge.hpp View File

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

#include "JackBridge.hpp"

#include "../../extra/Mutex.hpp"
#include "../../extra/RingBuffer.hpp"

#if DISTRHO_PLUGIN_NUM_INPUTS > 2
@@ -34,6 +35,8 @@
#endif

using DISTRHO_NAMESPACE::HeapRingBuffer;
using DISTRHO_NAMESPACE::RecursiveMutex;
using DISTRHO_NAMESPACE::RecursiveMutexLocker;

struct NativeBridge {
// Current status information
@@ -64,23 +67,25 @@ struct NativeBridge {
kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI,
kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI,
};
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS];
float* audioBufferStorage;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
bool midiAvailable;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
bool midiUsed;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static constexpr const uint32_t kMaxMIDIInputMessageSize = 3;
static constexpr const uint32_t kRingBufferMessageSize = 1u /*+ sizeof(double)*/ + kMaxMIDIInputMessageSize;
uint8_t midiDataStorage[kMaxMIDIInputMessageSize];
HeapRingBuffer midiInBufferCurrent;
HeapRingBuffer midiInBufferPending;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#endif
RecursiveMutex midiInLock;
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
HeapRingBuffer midiOutBuffer;
#endif
#endif

NativeBridge()
: bufferSize(0),
@@ -101,6 +106,7 @@ struct NativeBridge {
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
, midiAvailable(false)
, midiUsed(false)
#endif
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
@@ -132,8 +138,16 @@ struct NativeBridge {
#endif
}

virtual bool isMIDIEnabled() const
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
return midiUsed;
#else
return false;
#endif
}

virtual bool supportsBufferSizeChanges() const { return false; }
virtual bool isMIDIEnabled() const { return false; }
virtual bool requestAudioInput() { return false; }
virtual bool requestBufferSizeChange(uint32_t) { return false; }
virtual bool requestMIDI() { return false; }
@@ -158,7 +172,10 @@ struct NativeBridge {
if (midiAvailable)
{
// NOTE: this function is only called once per run
midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending);
{
const RecursiveMutexLocker cml(midiInLock);
midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending);
}
return midiInBufferCurrent.getReadableDataSize() / kRingBufferMessageSize;
}
#endif
@@ -212,10 +229,10 @@ struct NativeBridge {
case 2: fail |= !midiOutBuffer.writeByte(0);
}
fail |= !midiOutBuffer.writeUInt(time);
midiOutBuffer.commitWrite();
midiOutBuffer.commitWrite("NativeBridge::writeEvent (with data)");
return !fail;
}
midiOutBuffer.commitWrite();
midiOutBuffer.commitWrite("NativeBridge::writeEvent (without data)");
}
#endif

@@ -231,7 +248,7 @@ struct NativeBridge {

if (audio)
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)];

for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
@@ -245,6 +262,9 @@ struct NativeBridge {

if (midi)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiUsed = true;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
@@ -261,12 +281,18 @@ struct NativeBridge {
delete[] audioBufferStorage;
audioBufferStorage = nullptr;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.deleteBuffer();
midiInBufferPending.deleteBuffer();
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.deleteBuffer();
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if (midiUsed)
{
midiUsed = false;
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.deleteBuffer();
midiInBufferPending.deleteBuffer();
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.deleteBuffer();
#endif
}
#endif
}



+ 29
- 5
dpf/distrho/src/jackbridge/RtAudioBridge.hpp View File

@@ -1,6 +1,6 @@
/*
* RtAudio Bridge for DPF
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2025 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,7 @@

#include "NativeBridge.hpp"

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
#if (DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS) == 0
# error RtAudio without audio does not make sense
#endif

@@ -33,7 +33,7 @@
# define RTAUDIO_API_TYPE WINDOWS_WASAPI
# define RTMIDI_API_TYPE WINDOWS_MM
#else
# if defined(HAVE_PULSEAUDIO)
# if defined(HAVE_PULSEAUDIO) && !defined(DPF_JACK_STANDALONE_SKIP_PULSEAUDIO_FALLBACK)
# define __LINUX_PULSE__
# define RTAUDIO_API_TYPE LINUX_PULSE
# elif defined(HAVE_ALSA)
@@ -392,6 +392,28 @@ struct RtAudioBridge : NativeBridge {
const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if (self->midiOutBuffer.isDataAvailableForReading())
{
static_assert(kMaxMIDIInputMessageSize + 1u == 4, "change code if bumping this value");
uint8_t data[4] = {};

while (self->midiOutBuffer.isDataAvailableForReading() &&
self->midiOutBuffer.readCustomData(data, ARRAY_SIZE(data)))
{
// offset not used in RtMidiOut
self->midiOutBuffer.readUInt();

for (std::vector<RtMidiOut>::iterator it = self->midiOuts.begin(), end = self->midiOuts.end(); it != end; ++it)
{
static_cast<RtMidiOut&>(*it).sendMessage(data + 1, data[0]);
}
}

self->midiOutBuffer.flush();
}
#endif

return 0;
}

@@ -403,13 +425,15 @@ struct RtAudioBridge : NativeBridge {

RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData);

const RecursiveMutexLocker rml(self->midiInLock);

self->midiInBufferPending.writeByte(static_cast<uint8_t>(len));
// TODO timestamp
// self->midiInBufferPending.writeDouble(timestamp);
self->midiInBufferPending.writeCustomData(message->data(), len);
for (uint8_t i=len; i<kMaxMIDIInputMessageSize; ++i)
for (uint8_t i = len; i < kMaxMIDIInputMessageSize; ++i)
self->midiInBufferPending.writeByte(0);
self->midiInBufferPending.commitWrite();
self->midiInBufferPending.commitWrite("RtMidiCallback");
}
#endif
};


+ 47
- 44
dpf/distrho/src/jackbridge/WebBridge.hpp View File

@@ -224,55 +224,58 @@ struct WebBridge : NativeBridge {

var constraints = {};
// we need to use this weird awkward way for objects, otherwise build fails
constraints['audio'] = true;
constraints['video'] = false;
constraints['autoGainControl'] = {};
constraints['autoGainControl']['ideal'] = false;
constraints['echoCancellation'] = {};
constraints['echoCancellation']['ideal'] = false;
constraints['noiseSuppression'] = {};
constraints['noiseSuppression']['ideal'] = false;
constraints['channelCount'] = {};
constraints['channelCount']['min'] = 0;
constraints['channelCount']['ideal'] = numInputs;
constraints['latency'] = {};
constraints['latency']['min'] = 0;
constraints['latency']['ideal'] = 0;
constraints['sampleSize'] = {};
constraints['sampleSize']['min'] = 8;
constraints['sampleSize']['max'] = 32;
constraints['sampleSize']['ideal'] = 16;
// old property for chrome
constraints['googAutoGainControl'] = false;
constraints['audio'] = {};
constraints['audio']['autoGainControl'] = {};
constraints['audio']['autoGainControl']['ideal'] = false;
constraints['audio']['echoCancellation'] = {};
constraints['audio']['echoCancellation']['ideal'] = false;
constraints['audio']['noiseSuppression'] = {};
constraints['audio']['noiseSuppression']['ideal'] = false;
constraints['audio']['channelCount'] = {};
constraints['audio']['channelCount']['min'] = 0;
constraints['audio']['channelCount']['ideal'] = numInputs;
constraints['audio']['latency'] = {};
constraints['audio']['latency']['min'] = 0;
constraints['audio']['latency']['ideal'] = 0;
constraints['audio']['sampleSize'] = {};
constraints['audio']['sampleSize']['min'] = 8;
constraints['audio']['sampleSize']['max'] = 32;
constraints['audio']['sampleSize']['ideal'] = 16;
// old properties for chrome
constraints['audio']['googAudioMirroring'] = {};
constraints['audio']['googAudioMirroring']['ideal'] = false;
constraints['audio']['googAutoGainControl'] = {};
constraints['audio']['googAutoGainControl']['ideal'] = false;
constraints['audio']['googAutoGainControl2'] = {};
constraints['audio']['googAutoGainControl2']['ideal'] = false;
constraints['audio']['googDAEchoCancellation'] = {};
constraints['audio']['googDAEchoCancellation']['ideal'] = false;
constraints['audio']['googEchoCancellation'] = {};
constraints['audio']['googEchoCancellation']['ideal'] = false;
constraints['audio']['googEchoCancellation2'] = {};
constraints['audio']['googEchoCancellation2']['ideal'] = false;
constraints['audio']['googHighpassFilter'] = {};
constraints['audio']['googHighpassFilter']['ideal'] = false;
constraints['audio']['googNoiseSuppression'] = {};
constraints['audio']['googNoiseSuppression']['ideal'] = false;
constraints['audio']['googNoiseSuppression2'] = {};
constraints['audio']['googNoiseSuppression2']['ideal'] = false;
constraints['audio']['googTypingNoiseDetection'] = {};
constraints['audio']['googTypingNoiseDetection']['ideal'] = false;
constraints['audio']['intelligibilityEnhancer'] = {};
constraints['audio']['intelligibilityEnhancer']['ideal'] = false;
constraints['audio']['levelControl'] = {};
constraints['audio']['levelControl']['ideal'] = false;
constraints['audio']['levelControlInitialPeakLevelDBFS'] = {};
constraints['audio']['levelControlInitialPeakLevelDBFS']['ideal'] = false;

var success = function(stream) {
var tracks = stream.getAudioTracks();

// try to force as much as we can
for (var i in tracks) {
var track = tracks[i];

track.applyConstraints({'autoGainControl': { 'exact': false } })
.then(function(){console.log("Mic/Input auto-gain control has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input auto-gain")});

track.applyConstraints({'echoCancellation': { 'exact': false } })
.then(function(){console.log("Mic/Input echo-cancellation has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input echo-cancellation")});

track.applyConstraints({'noiseSuppression': { 'exact': false } })
.then(function(){console.log("Mic/Input noise-suppression has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input noise-suppression")});

track.applyConstraints({'googAutoGainControl': { 'exact': false } })
.then(function(){})
.catch(function(){});
}

WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream);
WAB.captureStreamNode.connect(WAB.processor);
};
var fail = function() {
var fail = function(err) {
console.error(err);
};

if (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined) {
@@ -494,7 +497,7 @@ struct WebBridge : NativeBridge {
}, offset, bytes[0], bytes[1], bytes[2], bytes[3], timestamp);
}

self->midiOutBuffer.clearData();
self->midiOutBuffer.flush();
}
#endif
}


+ 73
- 0
dpf/distrho/src/mapi/mapi.h View File

@@ -0,0 +1,73 @@
// Copyright 2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

#pragma once

#ifdef __cplusplus
extern "C" {
# define MAPI_API extern "C"
#else
# define MAPI_API
#endif

/* Define MAPI_EXPORT for exporting function symbols */
#ifdef _WIN32
# define MAPI_EXPORT MAPI_API __declspec (dllexport)
#else
# define MAPI_EXPORT MAPI_API __attribute__ ((visibility("default")))
#endif

/** Handle used through-out this API. */
typedef void* mapi_handle_t;

/**
Create an effect.
@param sample_rate Sample rate in Hz to use for the effect.
@return A handle for the new effect, or NULL if creation failed.
*/
MAPI_EXPORT
mapi_handle_t mapi_create(unsigned int sample_rate);

/**
Process an effect.
@param handle A previously created effect.
@param ins An array of audio buffers used for audio input.
@param outs An array of audio buffers used for audio output.
@param frames How many frames to process.
@note Input and output buffers might share the same location in memory,
typically referred to as "in-place processing".
*/
MAPI_EXPORT
void mapi_process(mapi_handle_t handle,
const float* const* ins,
float** outs,
unsigned int frames);

/**
Set an effect parameter.
@param handle A previously created effect.
@param index A known index for this effect.
@param value A full-ranged value.
*/
MAPI_EXPORT
void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value);

/**
Set an effect state, using strings for both key and value.
@param handle A previously created effect.
@param key A known key for this effect, must not be NULL or empty.
@param value A non-NULL value, allowed to be empty.
*/
MAPI_EXPORT
void mapi_set_state(mapi_handle_t handle, const char* key, const char* value);

/**
Destroy a previously created effect.
@param handle A previously created effect.
*/
MAPI_EXPORT
void mapi_destroy(mapi_handle_t handle);

#ifdef __cplusplus
}
#endif

+ 203
- 0
dpf/utils/emscripten.html.in View File

@@ -0,0 +1,203 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="description" content="@NAME@" />
<meta name="theme-color" content="#111111">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0" user-scalable="no">
<title>@NAME@</title>
<style>
html {
background-color: #111111;
color: #eee;
overflow: hidden;
}
body, canvas {
padding: 0;
margin: 0;
}

#canvas_file_open {
display: none;
}

#canvas_wrapper {
--device-pixel-ratio: 1;
display: none;
image-rendering: pixelated;
image-rendering: crisp-edges;
position: fixed;
transform-origin: 0 0 0;
transform: scale(calc(1 / var(--device-pixel-ratio)));
width: 100vw;
height: 100vh;
/* center */
margin: auto auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

#error {
background: rgba(0,0,0,0.75);
display: none;
position: fixed;
padding: 0.5em;
left: 0;
right: 0;
width: 100%;
z-index: 2;
}

.emscripten {
display: block;
margin-left: auto;
margin-right: auto;
padding-right: 0;
text-align: center;
}

.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
margin-top: 100px;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}

</style>
</head>
<body>
<figure style="overflow:visible;" id="spinner">
<div class="spinner"></div>
<center style="margin-top:0.5em"><strong>@NAME@</strong></center>
</figure>
<div class="emscripten" id="error"></div>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div id="canvas_wrapper">
<input type="file" id="canvas_file_open" ></input>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>

<script type='text/javascript'>
'use strict';

var wasmErrors = [];
var errorElement = document.getElementById('error');
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');

if (typeof(WebAssembly) === "undefined") {
wasmErrors.push('WebAssembly unsupported');
} else {
if (!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1]))) {
wasmErrors.push('Importable/Exportable mutable globals unsupported');
}
}

if (wasmErrors.length !== 0) {
errorElement.innerHTML = 'Cannot start @NAME@:<br>' + wasmErrors.join('<br>') + '<br><br>Perhaps try a different browser?';
errorElement.style.display = 'block';
statusElement.style.display = 'none';
progressElement.style.display = 'none';
spinnerElement.style.display = 'none';
} else {
var canvasWrapper = document.getElementById('canvas_wrapper');

var Module = {
preRun: [],
postRun: function() {
statusElement.style.display = 'none';
progressElement.style.display = 'none';
spinnerElement.style.display = 'none';
canvasWrapper.style.display = 'block';
window.dispatchEvent(new Event('resize'));
},
canvas: (function() {
var canvas = document.getElementById('canvas');

// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);

return canvas;
})(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
Module.setStatus('Downloading...');
window.onerror = function(err) {
errorElement.innerHTML = 'Exception thrown:<br>' + err
errorElement.style.display = 'block';
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if (text) console.error('[post-exception status] ' + text);
};
};

var jsModuleScript = document.createElement('script');
jsModuleScript.setAttribute('async', true);
jsModuleScript.setAttribute('src', "@NAME@.js");
jsModuleScript.setAttribute('type','text/javascript');
document.head.appendChild(jsModuleScript);
}
</script>
</body>
</html>

+ 6
- 0
dpf/utils/symbols/mapi.def View File

@@ -0,0 +1,6 @@
EXPORTS
mapi_create
mapi_process
mapi_set_parameter
mapi_set_state
mapi_destroy

+ 5
- 0
dpf/utils/symbols/mapi.exp View File

@@ -0,0 +1,5 @@
_mapi_create
_mapi_process
_mapi_set_parameter
_mapi_set_state
_mapi_destroy

+ 4
- 0
dpf/utils/symbols/mapi.version View File

@@ -0,0 +1,4 @@
{
global: mapi_create; mapi_process; mapi_set_parameter; mapi_set_state; mapi_destroy;
local: *;
};

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save