From ab2b7ada54720efac26423aa32ff2b0abd570af0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Oct 2025 16:39:17 +0200 Subject: [PATCH] Update to latest dpf Signed-off-by: falkTX --- dpf/CMakeLists.txt | 6 +- dpf/LICENSE | 2 +- dpf/Makefile.base.mk | 48 +- dpf/Makefile.plugins.mk | 166 ++- dpf/dgl/Application.hpp | 19 +- dpf/dgl/Base.hpp | 4 + dpf/dgl/Cairo.hpp | 7 +- dpf/dgl/Color.hpp | 13 +- dpf/dgl/Geometry.hpp | 18 +- dpf/dgl/Image.hpp | 16 +- dpf/dgl/Makefile | 76 ++ dpf/dgl/OpenGL-include.hpp | 6 + dpf/dgl/OpenGL.hpp | 157 ++- dpf/dgl/Window.hpp | 15 +- dpf/dgl/src/Application.cpp | 11 +- dpf/dgl/src/ApplicationPrivateData.cpp | 20 +- dpf/dgl/src/ApplicationPrivateData.hpp | 13 +- dpf/dgl/src/Cairo.cpp | 148 ++- dpf/dgl/src/Color.cpp | 10 +- dpf/dgl/src/EventHandlers.cpp | 9 +- dpf/dgl/src/Geometry.cpp | 2 + dpf/dgl/src/NanoVG.cpp | 7 +- dpf/dgl/src/OpenGL.cpp | 597 +-------- dpf/dgl/src/OpenGL2.cpp | 585 +++++++++ dpf/dgl/src/OpenGL3.cpp | 1033 ++++++++++++++++ dpf/dgl/src/TopLevelWidgetPrivateData.cpp | 5 - dpf/dgl/src/TopLevelWidgetPrivateData.hpp | 1 - dpf/dgl/src/Window.cpp | 75 +- dpf/dgl/src/WindowPrivateData.cpp | 73 +- dpf/dgl/src/WindowPrivateData.hpp | 6 +- dpf/dgl/src/nanovg/nanovg_gl.h | 44 +- dpf/dgl/src/pugl-extra/haiku.cpp | 2 +- dpf/dgl/src/pugl-extra/wasm.c | 128 +- dpf/dgl/src/pugl-upstream/.clang-format | 17 +- .../src/pugl-upstream/.clang-format-ignore | 4 + dpf/dgl/src/pugl-upstream/.clang-tidy | 7 +- dpf/dgl/src/pugl-upstream/.clant.json | 4 - dpf/dgl/src/pugl-upstream/.reuse/dep5 | 2 +- dpf/dgl/src/pugl-upstream/README.md | 6 +- .../pugl-upstream/include/pugl/attributes.h | 19 +- .../src/pugl-upstream/include/pugl/cairo.h | 7 +- dpf/dgl/src/pugl-upstream/include/pugl/gl.h | 16 +- dpf/dgl/src/pugl-upstream/include/pugl/pugl.h | 1077 +++++------------ dpf/dgl/src/pugl-upstream/include/pugl/stub.h | 7 +- .../src/pugl-upstream/include/pugl/vulkan.h | 25 +- dpf/dgl/src/pugl-upstream/src/.clang-tidy | 1 - dpf/dgl/src/pugl-upstream/src/common.c | 151 ++- dpf/dgl/src/pugl-upstream/src/internal.c | 129 +- dpf/dgl/src/pugl-upstream/src/internal.h | 40 +- dpf/dgl/src/pugl-upstream/src/mac.h | 2 +- dpf/dgl/src/pugl-upstream/src/mac.m | 369 +++--- dpf/dgl/src/pugl-upstream/src/mac_cairo.m | 2 +- dpf/dgl/src/pugl-upstream/src/mac_gl.m | 2 +- dpf/dgl/src/pugl-upstream/src/mac_stub.m | 2 +- dpf/dgl/src/pugl-upstream/src/mac_vulkan.m | 6 +- dpf/dgl/src/pugl-upstream/src/platform.h | 29 +- dpf/dgl/src/pugl-upstream/src/stub.h | 3 +- dpf/dgl/src/pugl-upstream/src/types.h | 42 +- dpf/dgl/src/pugl-upstream/src/win.c | 539 ++++----- dpf/dgl/src/pugl-upstream/src/win.h | 21 +- dpf/dgl/src/pugl-upstream/src/win_cairo.c | 15 +- dpf/dgl/src/pugl-upstream/src/win_gl.c | 62 +- dpf/dgl/src/pugl-upstream/src/win_stub.c | 2 +- dpf/dgl/src/pugl-upstream/src/win_vulkan.c | 2 +- dpf/dgl/src/pugl-upstream/src/x11.c | 431 +++---- dpf/dgl/src/pugl-upstream/src/x11.h | 10 +- dpf/dgl/src/pugl-upstream/src/x11_cairo.c | 18 +- dpf/dgl/src/pugl-upstream/src/x11_gl.c | 11 +- dpf/dgl/src/pugl-upstream/src/x11_stub.c | 4 +- dpf/dgl/src/pugl-upstream/src/x11_vulkan.c | 4 +- dpf/dgl/src/pugl.cpp | 213 ++-- dpf/dgl/src/pugl.hpp | 9 +- dpf/distrho/DistrhoInfo.hpp | 18 + dpf/distrho/DistrhoPluginMain.cpp | 7 +- dpf/distrho/DistrhoPluginUtils.hpp | 49 +- dpf/distrho/DistrhoUI.hpp | 34 +- dpf/distrho/DistrhoUIMain.cpp | 19 +- dpf/distrho/extra/RingBuffer.hpp | 8 +- dpf/distrho/extra/ScopedPointer.hpp | 5 + dpf/distrho/extra/String.hpp | 112 +- dpf/distrho/src/DistrhoPluginCLAP.cpp | 15 +- dpf/distrho/src/DistrhoPluginJACK.cpp | 2 +- dpf/distrho/src/DistrhoPluginLV2export.cpp | 17 + dpf/distrho/src/DistrhoPluginMAPI.cpp | 158 +++ dpf/distrho/src/DistrhoPluginVST2.cpp | 11 +- dpf/distrho/src/DistrhoUI.cpp | 6 +- dpf/distrho/src/DistrhoUIInternal.hpp | 11 +- dpf/distrho/src/DistrhoUILV2.cpp | 28 +- dpf/distrho/src/DistrhoUIPrivateData.hpp | 71 +- dpf/distrho/src/DistrhoUIVST3.cpp | 5 +- dpf/distrho/src/DistrhoUtils.cpp | 203 ++-- dpf/distrho/src/jackbridge/JackBridge.cpp | 45 +- dpf/distrho/src/jackbridge/NativeBridge.hpp | 66 +- dpf/distrho/src/jackbridge/RtAudioBridge.hpp | 34 +- dpf/distrho/src/jackbridge/WebBridge.hpp | 91 +- dpf/distrho/src/mapi/mapi.h | 73 ++ dpf/utils/emscripten.html.in | 203 ++++ dpf/utils/symbols/mapi.def | 6 + dpf/utils/symbols/mapi.exp | 5 + dpf/utils/symbols/mapi.version | 4 + dpf/utils/symbols/shared.def | 2 - dpf/utils/symbols/shared.exp | 1 - dpf/utils/symbols/shared.version | 4 - 103 files changed, 4732 insertions(+), 3213 deletions(-) create mode 100644 dpf/dgl/src/OpenGL2.cpp create mode 100644 dpf/dgl/src/OpenGL3.cpp create mode 100644 dpf/dgl/src/pugl-upstream/.clang-format-ignore create mode 100644 dpf/distrho/src/DistrhoPluginMAPI.cpp create mode 100644 dpf/distrho/src/mapi/mapi.h create mode 100644 dpf/utils/emscripten.html.in create mode 100644 dpf/utils/symbols/mapi.def create mode 100644 dpf/utils/symbols/mapi.exp create mode 100644 dpf/utils/symbols/mapi.version delete mode 100644 dpf/utils/symbols/shared.def delete mode 100644 dpf/utils/symbols/shared.exp delete mode 100644 dpf/utils/symbols/shared.version diff --git a/dpf/CMakeLists.txt b/dpf/CMakeLists.txt index 02c7707..5f0d7b9 100644 --- a/dpf/CMakeLists.txt +++ b/dpf/CMakeLists.txt @@ -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)) diff --git a/dpf/LICENSE b/dpf/LICENSE index 652264a..c8f3a8b 100644 --- a/dpf/LICENSE +++ b/dpf/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2012-2024 Filipe Coelho +Copyright (C) 2012-2025 Filipe Coelho Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/dpf/Makefile.base.mk b/dpf/Makefile.base.mk index 7498ea8..f09cea9 100644 --- a/dpf/Makefile.base.mk +++ b/dpf/Makefile.base.mk @@ -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 diff --git a/dpf/Makefile.plugins.mk b/dpf/Makefile.plugins.mk index 16d959d..f539580 100644 --- a/dpf/Makefile.plugins.mk +++ b/dpf/Makefile.plugins.mk @@ -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 # --------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/dgl/Application.hpp b/dpf/dgl/Application.hpp index c028b53..fac5fcc 100644 --- a/dpf/dgl/Application.hpp +++ b/dpf/dgl/Application.hpp @@ -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(). diff --git a/dpf/dgl/Base.hpp b/dpf/dgl/Base.hpp index c60a737..cc2200a 100644 --- a/dpf/dgl/Base.hpp +++ b/dpf/dgl/Base.hpp @@ -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 diff --git a/dpf/dgl/Cairo.hpp b/dpf/dgl/Cairo.hpp index 87970c9..9976f51 100644 --- a/dpf/dgl/Cairo.hpp +++ b/dpf/dgl/Cairo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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(BaseWidget::getGraphicsContext())); } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget); diff --git a/dpf/dgl/Color.hpp b/dpf/dgl/Color.hpp index 0b8aaed..ce3c6fc 100644 --- a/dpf/dgl/Color.hpp +++ b/dpf/dgl/Color.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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. */ diff --git a/dpf/dgl/Geometry.hpp b/dpf/dgl/Geometry.hpp index 9ac9483..ca93000 100644 --- a/dpf/dgl/Geometry.hpp +++ b/dpf/dgl/Geometry.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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& operator=(const Line& line) noexcept; bool operator==(const Line& line) const noexcept; bool operator!=(const Line& 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 posStart, posEnd; @@ -489,7 +487,7 @@ public: bool operator==(const Circle& cir) const noexcept; bool operator!=(const Circle& 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 fPos; @@ -582,7 +580,7 @@ public: bool operator==(const Triangle& tri) const noexcept; bool operator!=(const Triangle& 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 pos1, pos2, pos3; @@ -813,6 +811,7 @@ public: bool operator==(const Rectangle& size) const noexcept; bool operator!=(const Rectangle& 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 pos; diff --git a/dpf/dgl/Image.hpp b/dpf/dgl/Image.hpp index 2e51536..6e5f3ce 100644 --- a/dpf/dgl/Image.hpp +++ b/dpf/dgl/Image.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 diff --git a/dpf/dgl/Makefile b/dpf/dgl/Makefile index 8776c4d..f026549 100644 --- a/dpf/dgl/Makefile +++ b/dpf/dgl/Makefile @@ -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) diff --git a/dpf/dgl/OpenGL-include.hpp b/dpf/dgl/OpenGL-include.hpp index 117e228..d0b5dc1 100644 --- a/dpf/dgl/OpenGL-include.hpp +++ b/dpf/dgl/OpenGL-include.hpp @@ -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 # include # else diff --git a/dpf/dgl/OpenGL.hpp b/dpf/dgl/OpenGL.hpp index 4d2e9ce..03d0ad1 100644 --- a/dpf/dgl/OpenGL.hpp +++ b/dpf/dgl/OpenGL.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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& 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(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 OpenGLImageAboutWindow; typedef ImageBaseButton OpenGLImageButton; @@ -215,7 +338,7 @@ typedef ImageBaseKnob OpenGLImageKnob; typedef ImageBaseSlider OpenGLImageSlider; typedef ImageBaseSwitch OpenGLImageSwitch; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dpf/dgl/Window.hpp b/dpf/dgl/Window.hpp index 64d0f56..2a811b0 100644 --- a/dpf/dgl/Window.hpp +++ b/dpf/dgl/Window.hpp @@ -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; diff --git a/dpf/dgl/src/Application.cpp b/dpf/dgl/src/Application.cpp index 09f57e1..705b10f 100644 --- a/dpf/dgl/src/Application.cpp +++ b/dpf/dgl/src/Application.cpp @@ -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,) diff --git a/dpf/dgl/src/ApplicationPrivateData.cpp b/dpf/dgl/src/ApplicationPrivateData.cpp index 0e353a7..3a145e9 100644 --- a/dpf/dgl/src/ApplicationPrivateData.cpp +++ b/dpf/dgl/src/ApplicationPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 diff --git a/dpf/dgl/src/ApplicationPrivateData.hpp b/dpf/dgl/src/ApplicationPrivateData.hpp index 59afaae..b5ef662 100644 --- a/dpf/dgl/src/ApplicationPrivateData.hpp +++ b/dpf/dgl/src/ApplicationPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 idleCallbacks; /** Constructor and destructor */ - explicit PrivateData(bool standalone); + explicit PrivateData(bool standalone, Type type); ~PrivateData(); /** Flag one window as shown, which increments @a visibleWindows. diff --git a/dpf/dgl/src/Cairo.cpp b/dpf/dgl/src/Cairo.cpp index 612029a..f0a5b30 100644 --- a/dpf/dgl/src/Cairo.cpp +++ b/dpf/dgl/src/Cairo.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * Copyright (C) 2019-2021 Jean Pierre Cimalando * * 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(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 @@ -63,7 +85,7 @@ void Line::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(context).handle; cairo_set_line_width(handle, width); cairo_move_to(handle, posStart.getX(), posStart.getY()); @@ -71,18 +93,13 @@ void Line::draw(const GraphicsContext& context, const T width) cairo_stroke(handle); } +#if DGL_ALLOW_DEPRECATED_METHODS template void Line::draw() { notImplemented("Line::draw"); } - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; +#endif // ----------------------------------------------------------------------- // Circle @@ -129,7 +146,7 @@ static void drawCircle(cairo_t* const handle, template void Circle::draw(const GraphicsContext& context) { - cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + cairo_t* const handle = static_cast(context).handle; drawCircle(handle, fPos, fNumSegments, fSize, fSin, fCos, false); } @@ -139,12 +156,13 @@ void Circle::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(context).handle; cairo_set_line_width(handle, lineWidth); drawCircle(handle, fPos, fNumSegments, fSize, fSin, fCos, true); } +#if DGL_ALLOW_DEPRECATED_METHODS template void Circle::draw() { @@ -156,13 +174,7 @@ void Circle::drawOutline() { notImplemented("Circle::drawOutline"); } - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; +#endif // ----------------------------------------------------------------------- // Triangle @@ -190,7 +202,7 @@ static void drawTriangle(cairo_t* const handle, template void Triangle::draw(const GraphicsContext& context) { - cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + cairo_t* const handle = static_cast(context).handle; drawTriangle(handle, pos1, pos2, pos3, false); } @@ -200,12 +212,13 @@ void Triangle::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(context).handle; cairo_set_line_width(handle, lineWidth); drawTriangle(handle, pos1, pos2, pos3, true); } +#if DGL_ALLOW_DEPRECATED_METHODS template void Triangle::draw() { @@ -217,13 +230,7 @@ void Triangle::drawOutline() { notImplemented("Triangle::drawOutline"); } - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; +#endif // ----------------------------------------------------------------------- // Rectangle @@ -244,7 +251,7 @@ void Rectangle::draw(const GraphicsContext& context) { DISTRHO_SAFE_ASSERT_RETURN(isValid(),); - cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + cairo_t* const handle = static_cast(context).handle; drawRectangle(handle, *this, false); } @@ -255,12 +262,13 @@ void Rectangle::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(context).handle; cairo_set_line_width(handle, lineWidth); drawRectangle(handle, *this, true); } +#if DGL_ALLOW_DEPRECATED_METHODS template void Rectangle::draw() { @@ -272,13 +280,7 @@ void Rectangle::drawOutline() { notImplemented("Rectangle::drawOutline"); } - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; +#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(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& pos) { DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,); - cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + cairo_t* const handle = static_cast(context).handle; cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); cairo_paint(handle); @@ -623,7 +625,7 @@ void ImageBaseKnob::PrivateData::init() template <> void ImageBaseKnob::PrivateData::cleanup() { - cairo_surface_destroy((cairo_surface_t*)cairoSurface); + cairo_surface_destroy(static_cast(cairoSurface)); cairoSurface = nullptr; } @@ -672,10 +674,10 @@ template <> void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); - cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; + cairo_t* const handle = static_cast(context).handle; const double normValue = getNormalizedValue(); - cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; + cairo_surface_t* surface = static_cast(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(graphicsContext).handle = static_cast(puglGetContext(view)); +} + +void Window::PrivateData::endContext() +{ +} + +// -------------------------------------------------------------------------------------------------------------------- + +#ifndef DGL_GEOMETRY_CPP_INCLUDED +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +#endif + +// -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/dpf/dgl/src/Color.cpp b/dpf/dgl/src/Color.cpp index 525c651..109910b 100644 --- a/dpf/dgl/src/Color.cpp +++ b/dpf/dgl/src/Color.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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(color >> 24) & 0xff, + static_cast(color >> 16) & 0xff, + static_cast(color >> 8) & 0xff, + alpha); +} + void Color::interpolate(const Color& other, float u) noexcept { fixRange(u); diff --git a/dpf/dgl/src/EventHandlers.cpp b/dpf/dgl/src/EventHandlers.cpp index 9ae82ae..30456b0 100644 --- a/dpf/dgl/src/EventHandlers.cpp +++ b/dpf/dgl/src/EventHandlers.cpp @@ -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 diff --git a/dpf/dgl/src/Geometry.cpp b/dpf/dgl/src/Geometry.cpp index eb8df6e..3af7842 100644 --- a/dpf/dgl/src/Geometry.cpp +++ b/dpf/dgl/src/Geometry.cpp @@ -23,6 +23,8 @@ #include "../Geometry.hpp" +#define DGL_GEOMETRY_CPP_INCLUDED + #include START_NAMESPACE_DGL diff --git a/dpf/dgl/src/NanoVG.cpp b/dpf/dgl/src/NanoVG.cpp index 47781cc..6203ebb 100644 --- a/dpf/dgl/src/NanoVG.cpp +++ b/dpf/dgl/src/NanoVG.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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::onDisplay() onNanoDisplay(); displayChildren(); NanoVG::endFrame(); + #ifdef DGL_USE_OPENGL3 + glUseProgram(reinterpret_cast(getGraphicsContext()).program); + #endif } } diff --git a/dpf/dgl/src/OpenGL.cpp b/dpf/dgl/src/OpenGL.cpp index fa387e4..5eac3eb 100644 --- a/dpf/dgl/src/OpenGL.cpp +++ b/dpf/dgl/src/OpenGL.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 -static void drawLine(const Point& posStart, const Point& posEnd) -{ - DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); - - glBegin(GL_LINES); - - { - glVertex2d(posStart.getX(), posStart.getY()); - glVertex2d(posEnd.getX(), posEnd.getY()); - } - - glEnd(); -} -#endif - -template -void Line::draw(const GraphicsContext&, const T width) -{ -#ifdef DGL_USE_COMPAT_OPENGL - DISTRHO_SAFE_ASSERT_RETURN(width != 0,); - - glLineWidth(static_cast(width)); - drawLine(posStart, posEnd); -#else - notImplemented("Line::draw"); -#endif -} - -// deprecated calls -template -void Line::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawLine(posStart, posEnd); -#else - notImplemented("Line::draw"); -#endif -} - -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; -template class Line; - -// ----------------------------------------------------------------------- -// Circle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawCircle(const Point& 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 -void Circle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); -#else - notImplemented("Circle::draw"); -#endif -} - -template -void Circle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); -#else - notImplemented("Circle::drawOutline"); -#endif -} - -// deprecated calls -template -void Circle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); -#else - notImplemented("Circle::draw"); -#endif -} - -template -void Circle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); -#else - notImplemented("Circle::drawOutline"); -#endif -} - -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; -template class Circle; - -// ----------------------------------------------------------------------- -// Triangle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawTriangle(const Point& pos1, - const Point& pos2, - const Point& 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 -void Triangle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, false); -#else - notImplemented("Triangle::draw"); -#endif -} - -template -void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, true); -#else - notImplemented("Triangle::drawOutline"); -#endif -} - -// deprecated calls -template -void Triangle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, false); -#else - notImplemented("Triangle::draw"); -#endif -} - -template -void Triangle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawTriangle(pos1, pos2, pos3, true); -#else - notImplemented("Triangle::drawOutline"); -#endif -} - -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; -template class Triangle; - -// ----------------------------------------------------------------------- -// Rectangle - -#ifdef DGL_USE_COMPAT_OPENGL -template -static void drawRectangle(const Rectangle& 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 -void Rectangle::draw(const GraphicsContext&) -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, false); -#else - notImplemented("Rectangle::draw"); -#endif -} - -template -void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) -{ - DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); - - glLineWidth(static_cast(lineWidth)); -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, true); -#else - notImplemented("Rectangle::drawOutline"); -#endif -} - -// deprecated calls -template -void Rectangle::draw() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, false); -#else - notImplemented("Rectangle::draw"); -#endif -} - -template -void Rectangle::drawOutline() -{ -#ifdef DGL_USE_COMPAT_OPENGL - drawRectangle(*this, true); -#else - notImplemented("Rectangle::drawOutline"); -#endif -} - -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; -template class Rectangle; - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // 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(image.getWidth()), - static_cast(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& 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(image.getWidth()); - const int h = static_cast(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& 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& s, const ImageFormat fmt) noexcept @@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size& s, c ImageBase::loadFromMemory(rdata, s, fmt); } -void OpenGLImage::drawAt(const GraphicsContext&, const Point& 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& 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(0, 0), textureId, setupCalled); -} - -void OpenGLImage::drawAt(const int x, const int y) -{ - drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); -} - -void OpenGLImage::drawAt(const Point& pos) -{ - drawOpenGLImage(*this, pos, textureId, setupCalled); -} - -// ----------------------------------------------------------------------- -// ImageBaseAboutWindow - -#if 0 -template <> -void ImageBaseAboutWindow::onDisplay() -{ - const GraphicsContext& context(getGraphicsContext()); - img.draw(context); -} -#endif - -template class ImageBaseAboutWindow; - -// ----------------------------------------------------------------------- -// ImageBaseButton - -template class ImageBaseButton; - -// ----------------------------------------------------------------------- -// ImageBaseKnob - -template <> -void ImageBaseKnob::PrivateData::init() -{ - glTextureId = 0; - glGenTextures(1, &glTextureId); -} - -template <> -void ImageBaseKnob::PrivateData::cleanup() -{ - if (glTextureId == 0) - return; - - glDeleteTextures(1, &glTextureId); - glTextureId = 0; -} - -template <> -void ImageBaseKnob::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(getWidth()), static_cast(getHeight()), 0, - asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); - - pData->isReady = true; - } - - const int w = static_cast(getWidth()); - const int h = static_cast(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(w2), static_cast(h2), 0.0f); - glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); #endif - Rectangle(-w2, -h2, w, h).draw(context); - -#ifdef DGL_USE_COMPAT_OPENGL - glPopMatrix(); -#endif - } - else - { - Rectangle(0, 0, w, h).draw(context); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} - -template class ImageBaseKnob; - -// ----------------------------------------------------------------------- -// ImageBaseSlider - -template class ImageBaseSlider; - -// ----------------------------------------------------------------------- -// ImageBaseSwitch - -template class ImageBaseSwitch; - -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- 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 diff --git a/dpf/dgl/src/OpenGL2.cpp b/dpf/dgl/src/OpenGL2.cpp new file mode 100644 index 0000000..bdfd900 --- /dev/null +++ b/dpf/dgl/src/OpenGL2.cpp @@ -0,0 +1,585 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2025 Filipe Coelho + * + * 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 +static void drawLine(const Point& posStart, const Point& posEnd) +{ + DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); + + glBegin(GL_LINES); + + { + glVertex2d(posStart.getX(), posStart.getY()); + glVertex2d(posEnd.getX(), posEnd.getY()); + } + + glEnd(); +} + +template +void Line::draw(const GraphicsContext&, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + glLineWidth(static_cast(width)); + drawLine(posStart, posEnd); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Line::draw() +{ + drawLine(posStart, posEnd); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Circle + +template +static void drawCircle(const Point& 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(pos.getX()); + const double origy = static_cast(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 +static void drawCircle(const GraphicsContext&, + const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + drawCircle(pos, numSegments, size, sin, cos, outline); +} + +template +void Circle::draw(const GraphicsContext& context) +{ + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Circle::draw() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline() +{ + drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Triangle + +template +static void drawTriangle(const Point& pos1, + const Point& pos2, + const Point& 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 +static void drawTriangle(const GraphicsContext&, + const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + drawTriangle(pos1, pos2, pos3, outline); +} + +template +void Triangle::draw(const GraphicsContext&) +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawTriangle(pos1, pos2, pos3, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Triangle::draw() +{ + drawTriangle(pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline() +{ + drawTriangle(pos1, pos2, pos3, true); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Rectangle + +template +static void drawRectangle(const Rectangle& 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 +void Rectangle::draw(const GraphicsContext&) +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawRectangle(*this, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Rectangle::draw() +{ + drawRectangle(*this, false); +} + +template +void Rectangle::drawOutline() +{ + drawRectangle(*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(image.getWidth()), + static_cast(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& 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(image.getWidth()); + const int h = static_cast(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& pos) +{ + drawOpenGLImage(*this, pos, textureId, setupCalled); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +void OpenGLImage::draw() +{ + drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); +} + +void OpenGLImage::drawAt(const int x, const int y) +{ + drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); +} + +void OpenGLImage::drawAt(const Point& pos) +{ + drawOpenGLImage(*this, pos, textureId, setupCalled); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseAboutWindow + +#if 0 +template <> +void ImageBaseAboutWindow::onDisplay() +{ +const GraphicsContext& context(getGraphicsContext()); +img.draw(context); +} +#endif + +template class ImageBaseAboutWindow; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseButton + +template class ImageBaseButton; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + glTextureId = 0; + glGenTextures(1, &glTextureId); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + if (glTextureId == 0) + return; + + glDeleteTextures(1, &glTextureId); + glTextureId = 0; +} + +template <> +void ImageBaseKnob::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(getWidth()), static_cast(getHeight()), 0, + asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); + + pData->isReady = true; + } + + const int w = static_cast(getWidth()); + const int h = static_cast(getHeight()); + + if (pData->rotationAngle != 0) + { + glPushMatrix(); + + const int w2 = w/2; + const int h2 = h/2; + + glTranslatef(static_cast(w2), static_cast(h2), 0.0f); + glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); + + Rectangle(-w2, -h2, w, h).draw(context); + + glPopMatrix(); + } + else + { + Rectangle(0, 0, w, h).draw(context); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +template class ImageBaseKnob; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + +// -------------------------------------------------------------------------------------------------------------------- + +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(size.width), static_cast(size.height)); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, static_cast(size.width), static_cast(size.height), 0.0, 0.0, 1.0); + glViewport(0, 0, static_cast(size.width), static_cast(size.height)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void Window::PrivateData::endContext() +{ +} + +// -------------------------------------------------------------------------------------------------------------------- + +#ifndef DGL_GEOMETRY_CPP_INCLUDED +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dpf/dgl/src/OpenGL3.cpp b/dpf/dgl/src/OpenGL3.cpp new file mode 100644 index 0000000..69b970e --- /dev/null +++ b/dpf/dgl/src/OpenGL3.cpp @@ -0,0 +1,1033 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2025 Filipe Coelho + * + * 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 "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 OpenGL3 code +#endif +#ifndef DGL_USE_OPENGL3 +# error Build config error, OpenGL3 not requested while building OpenGL3 code +#endif +#ifdef DGL_CAIRO +# error Build config error, Cairo requested while building OpenGL3 code +#endif +#ifdef DGL_VULKAN +# error Build config error, Vulkan requested while building OpenGL3 code +#endif +#if defined(DGL_USE_GLES2) && defined(DGL_USE_GLES3) +# error Build config error, both GLESv2 and GLESv3 requested at the same time +#endif +#if defined(DGL_USE_GLES2) && !defined(DGL_USE_GLES) +# error Build config error, DGL_USE_GLES2 is defined but DGL_USE_GLES is not +#endif +#if defined(DGL_USE_GLES3) && !defined(DGL_USE_GLES) +# error Build config error, DGL_USE_GLES3 is defined but DGL_USE_GLES is not +#endif +#if defined(DGL_USE_GLES) && !(defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3)) +# error Build config error, DGL_USE_GLES is defined which requires either DGL_USE_GLES2 or DGL_USE_GLES3 +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Load OpenGL3 symbols on Windows + +#if defined(DISTRHO_OS_WINDOWS) +# include +# define DGL_EXT(PROC, func) static PROC func; +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +# undef DGL_EXT +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#if DGL_ALLOW_DEPRECATED_METHODS +static void notImplemented(const char* const name) +{ + d_stderr2("OpenGL3 function not implemented: %s", name); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Color + +void Color::setFor(const GraphicsContext& context, const bool includeAlpha) +{ + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat color[4] = { red, green, blue, includeAlpha ? alpha : 1.f }; + glUniform4fv(gl3context.color, 1, color); +} + +// -------------------------------------------------------------------------------------------------------------------- +// Line + +template +void Line::draw(const GraphicsContext& context, const T width) +{ + DISTRHO_SAFE_ASSERT_RETURN(width != 0,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x1 = (static_cast(posStart.x) / gl3context.width) * 2 - 1; + const GLfloat y1 = (static_cast(posStart.y) / gl3context.height) * -2 + 1; + const GLfloat x2 = (static_cast(posEnd.x) / gl3context.width) * 2 - 1; + const GLfloat y2 = (static_cast(posEnd.y) / gl3context.height) * -2 + 1; + + glLineWidth(static_cast(width)); + + const GLfloat vertices[] = { x1, y1, x2, y2, }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + const GLubyte order[] = { 0, 1 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Line::draw() +{ + notImplemented("Line::draw"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Circle + +template +static void drawCircle(const GraphicsContext& context, + const Point& pos, + const uint numSegments, + const float size, + const float sin, + const float cos, + const bool outline) +{ + #define MAX_CIRCLE_SEGMENTS 512 + DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); + DISTRHO_SAFE_ASSERT_RETURN(numSegments <= MAX_CIRCLE_SEGMENTS,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const double origx = static_cast(pos.getX()); + const double origy = static_cast(pos.getY()); + double t; + double x = size; + double y = 0.0; + + GLfloat vertices[(MAX_CIRCLE_SEGMENTS + 1) * 2]; + for (uint i = 0; i < numSegments; ++i) + { + vertices[i * 2 + 0] = ((x + origx) / gl3context.width) * 2 - 1; + vertices[i * 2 + 1] = ((y + origy) / gl3context.height) * -2 + 1; + + t = x; + x = cos * x - sin * y; + y = sin * t + cos * y; + } + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (numSegments + 1), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + + if (outline) + { + GLushort order[MAX_CIRCLE_SEGMENTS * 2]; + for (uint i = 0; i < numSegments; ++i) + { + order[i * 2 + 0] = i; + order[i * 2 + 1] = i + 1; + } + order[numSegments * 2 - 1] = 0; + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numSegments * 2, order, GL_DYNAMIC_DRAW); + glDrawElements(GL_LINES, numSegments * 2, GL_UNSIGNED_SHORT, nullptr); + } + else + { + // center position + vertices[numSegments * 2 + 0] = (origx / gl3context.width) * 2 - 1; + vertices[numSegments * 2 + 1] = (origy / gl3context.height) * -2 + 1; + + GLushort order[MAX_CIRCLE_SEGMENTS * 3]; + for (uint i = 0; i < numSegments; ++i) + { + order[i * 3 + 0] = i; + order[i * 3 + 1] = i + 1; + order[i * 3 + 2] = numSegments; + } + order[numSegments * 3 - 2] = 0; + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numSegments * 3, order, GL_DYNAMIC_DRAW); + glDrawElements(GL_TRIANGLES, numSegments * 3, GL_UNSIGNED_SHORT, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Circle::draw(const GraphicsContext& context) +{ + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, false); +} + +template +void Circle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawCircle(context, fPos, fNumSegments, fSize, fSin, fCos, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Circle::draw() +{ + notImplemented("Circle::draw"); +} + +template +void Circle::drawOutline() +{ + notImplemented("Circle::drawOutline"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Triangle + +template +static void drawTriangle(const GraphicsContext& context, + const Point& pos1, + const Point& pos2, + const Point& pos3, + const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x1 = (static_cast(pos1.getX()) / gl3context.width) * 2 - 1; + const GLfloat y1 = (static_cast(pos1.getY()) / gl3context.height) * -2 + 1; + const GLfloat x2 = (static_cast(pos2.getX()) / gl3context.width) * 2 - 1; + const GLfloat y2 = (static_cast(pos2.getY()) / gl3context.height) * -2 + 1; + const GLfloat x3 = (static_cast(pos3.getX()) / gl3context.width) * 2 - 1; + const GLfloat y3 = (static_cast(pos3.getY()) / gl3context.height) * -2 + 1; + + const GLfloat vertices[] = { x1, y1, x2, y2, x3, y3 }; + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + if (outline) + { + static constexpr const GLubyte order[] = { 0, 1, 1, 2, 2, 0 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else + { + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Triangle::draw(const GraphicsContext& context) +{ + drawTriangle(context, pos1, pos2, pos3, false); +} + +template +void Triangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawTriangle(context, pos1, pos2, pos3, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Triangle::draw() +{ + notImplemented("Triangle::draw"); +} + +template +void Triangle::drawOutline() +{ + notImplemented("Triangle::drawOutline"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Rectangle + +template +static void drawRectangle(const GraphicsContext& context, const Rectangle& rect, const bool outline) +{ + DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + const GLfloat x = (static_cast(rect.getX()) / gl3context.width) * 2 - 1; + const GLfloat y = (static_cast(rect.getY()) / gl3context.height) * -2 + 1; + const GLfloat w = (static_cast(rect.getWidth()) / gl3context.width) * 2; + const GLfloat h = (static_cast(rect.getHeight()) / gl3context.height) * -2; + + const GLfloat vertices[] = { x, y, x, y + h, x + w, y + h, x + w, y }; + + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + + if (outline) + { + static constexpr const GLubyte order[] = { 0, 1, 1, 2, 2, 3, 3, 0 }; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_LINES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + } + else + { + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.bounds); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +template +void Rectangle::draw(const GraphicsContext& context) +{ + drawRectangle(context, *this, false); +} + +template +void Rectangle::drawOutline(const GraphicsContext& context, const T lineWidth) +{ + DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); + + glLineWidth(static_cast(lineWidth)); + drawRectangle(context, *this, true); +} + +#if DGL_ALLOW_DEPRECATED_METHODS +template +void Rectangle::draw() +{ + notImplemented("Rectangle::draw"); +} + +template +void Rectangle::drawOutline() +{ + notImplemented("Rectangle::drawOutline"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// OpenGLImage + +static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) +{ + DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); + + const ImageFormat imageFormat = image.getFormat(); + GLint intformat; + + #ifdef DGL_USE_GLES + // GLES does not support BGR + DISTRHO_SAFE_ASSERT_RETURN(imageFormat != kImageFormatBGR && imageFormat != kImageFormatBGRA,); + #endif + + glBindTexture(GL_TEXTURE_2D, textureId); + + switch (imageFormat) + { + case kImageFormatBGR: + case kImageFormatRGB: + intformat = GL_RGB; + break; + case kImageFormatGrayscale: + #ifdef DGL_USE_GLES2 + intformat = GL_LUMINANCE; + #else + intformat = GL_RED; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + #endif + break; + default: + intformat = GL_RGBA; + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + #ifndef DGL_USE_GLES + 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); + #endif + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, + 0, + intformat, + static_cast(image.getWidth()), + static_cast(image.getHeight()), + 0, + asOpenGLImageFormat(imageFormat), + GL_UNSIGNED_BYTE, + image.getRawData()); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void OpenGLImage::drawAt(const GraphicsContext& context, const Point& pos) +{ + if (textureId == 0 || isInvalid()) + return; + + const OpenGL3GraphicsContext& gl3context = static_cast(context); + + if (gl3context.program == 0) + return; + + if (! setupCalled) + { + setupOpenGLImage(*this, textureId); + setupCalled = true; + } + + const GLfloat x = (static_cast(pos.getX()) / gl3context.width) * 2 - 1; + const GLfloat y = (static_cast(pos.getY()) / gl3context.height) * -2 + 1; + const GLfloat w = (static_cast(getWidth()) / gl3context.width) * 2; + const GLfloat h = (static_cast(getHeight()) / gl3context.height) * -2; + + const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + glUniform4fv(gl3context.color, 1, color); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + glUniform1i(gl3context.usingTexture, 1); + + const GLfloat vertices[] = { + x, y, x, y + h, x + w, y + h, x + w, y, + 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glEnableVertexAttribArray(gl3context.textureMap); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glVertexAttribPointer(gl3context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(sizeof(GLfloat) * 8)); + + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.textureMap); + glDisableVertexAttribArray(gl3context.bounds); + glUniform1i(gl3context.usingTexture, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +#ifdef DGL_USE_GLES +ImageFormat OpenGLImage::getFormat() const noexcept +{ + switch (format) + { + case kImageFormatBGR: + return kImageFormatRGB; + case kImageFormatBGRA: + return kImageFormatRGBA; + default: + return format; + } +} + +const char* OpenGLImage::getRawData() const noexcept +{ + if (rawDataLast != rawData) + { + if (format == kImageFormatBGR || format == kImageFormatBGRA) + { + const uint w = size.getWidth(); + const uint h = size.getHeight(); + + std::free(convertedData); + + if (format == kImageFormatBGR) + { + convertedData = static_cast(std::malloc(sizeof(char) * 3 * w * h)); + for (int i = 0; i < w * h; ++i) + { + convertedData[i * 3 + 0] = rawData[i * 3 + 2]; + convertedData[i * 3 + 1] = rawData[i * 3 + 1]; + convertedData[i * 3 + 2] = rawData[i * 3 + 0]; + } + } + else + { + convertedData = static_cast(std::malloc(sizeof(char) * 4 * w * h)); + for (int i = 0; i < w * h; ++i) + { + convertedData[i * 4 + 0] = rawData[i * 4 + 2]; + convertedData[i * 4 + 1] = rawData[i * 4 + 1]; + convertedData[i * 4 + 2] = rawData[i * 4 + 0]; + convertedData[i * 4 + 3] = rawData[i * 4 + 3]; + } + } + } + else + { + std::free(convertedData); + convertedData = nullptr; + } + + rawDataLast = rawData; + } + + return + #ifdef DGL_USE_GLES + convertedData != nullptr ? convertedData : + #endif + rawData; +} +#endif + +#if DGL_ALLOW_DEPRECATED_METHODS +void OpenGLImage::draw() +{ + notImplemented("OpenGLImage::draw"); +} + +void OpenGLImage::drawAt(int, int) +{ + notImplemented("OpenGLImage::drawAt"); +} + +void OpenGLImage::drawAt(const Point&) +{ + notImplemented("OpenGLImage::drawAt"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseAboutWindow + +#if 0 +template <> +void ImageBaseAboutWindow::onDisplay() +{ +const GraphicsContext& context(getGraphicsContext()); +img.draw(context); +} +#endif + +template class ImageBaseAboutWindow; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseButton + +template class ImageBaseButton; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseKnob + +template <> +void ImageBaseKnob::PrivateData::init() +{ + glTextureId = 0; + glGenTextures(1, &glTextureId); +} + +template <> +void ImageBaseKnob::PrivateData::cleanup() +{ + if (glTextureId == 0) + return; + + glDeleteTextures(1, &glTextureId); + glTextureId = 0; +} + +template <> +void ImageBaseKnob::onDisplay() +{ + const OpenGL3GraphicsContext& gl3context = static_cast(getGraphicsContext()); + + if (gl3context.program == 0) + return; + + const ImageFormat imageFormat = pData->image.getFormat(); + const float normValue = getNormalizedValue(); + + #ifdef DGL_USE_GLES + // GLES does not support BGR + DISTRHO_SAFE_ASSERT_RETURN(imageFormat != kImageFormatBGR && imageFormat != kImageFormatBGRA,); + #endif + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + glUniform4fv(gl3context.color, 1, color); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, pData->glTextureId); + glUniform1i(gl3context.usingTexture, 1); + + if (! pData->isReady) + { + GLint intformat; + + switch (imageFormat) + { + case kImageFormatBGR: + case kImageFormatRGB: + intformat = GL_RGB; + break; + case kImageFormatGrayscale: + #ifdef DGL_USE_GLES2 + intformat = GL_LUMINANCE; + #else + intformat = GL_RED; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + #endif + break; + default: + intformat = GL_RGBA; + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + #ifndef DGL_USE_GLES + 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); + #endif + + 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 * ((imageFormat == kImageFormatBGRA || + imageFormat == kImageFormatRGBA) ? 4 : 3); + /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount - 1)); + } + + glTexImage2D(GL_TEXTURE_2D, + 0, + intformat, + static_cast(getWidth()), + static_cast(getHeight()), + 0, + asOpenGLImageFormat(imageFormat), + GL_UNSIGNED_BYTE, + pData->image.getRawData() + imageDataOffset); + + pData->isReady = true; + } + + GLfloat x, y, w, h; + + if (pData->rotationAngle != 0) + { + // TODO + x = -1; + y = 1; + w = (static_cast(getWidth()) / gl3context.width) * 2; + h = (static_cast(getHeight()) / gl3context.height) * -2; + } + else + { + x = -1; + y = 1; + w = (static_cast(getWidth()) / gl3context.width) * 2; + h = (static_cast(getHeight()) / gl3context.height) * -2; + } + + const GLfloat vertices[] = { + x, y, x, y + h, x + w, y + h, x + w, y, + 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, gl3context.buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(gl3context.bounds); + glEnableVertexAttribArray(gl3context.textureMap); + glVertexAttribPointer(gl3context.bounds, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glVertexAttribPointer(gl3context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(sizeof(GLfloat) * 8)); + + static constexpr const GLubyte order[] = { 0, 1, 2, 0, 2, 3 }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl3context.buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(order), order, GL_STATIC_DRAW); + + glDrawElements(GL_TRIANGLES, ARRAY_SIZE(order), GL_UNSIGNED_BYTE, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(gl3context.textureMap); + glDisableVertexAttribArray(gl3context.bounds); + glUniform1i(gl3context.usingTexture, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glDisable(GL_BLEND); +} + +template class ImageBaseKnob; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSlider + +template class ImageBaseSlider; + +// -------------------------------------------------------------------------------------------------------------------- +// ImageBaseSwitch + +template class ImageBaseSwitch; + +// -------------------------------------------------------------------------------------------------------------------- + +static void shaderCreationFail(const GLuint shaderErr, const GLuint shader2 = 0) +{ + if (shaderErr != 0) + { + GLint len = 0; + glGetShaderiv(shaderErr, GL_INFO_LOG_LENGTH, &len); + + std::vector errorLog(len); + glGetShaderInfoLog(shaderErr, len, &len, errorLog.data()); + + d_stderr2("OpenGL3 shader compilation error: %s", errorLog.data()); + + glDeleteShader(shaderErr); + } + + glDeleteShader(shader2); +} + +static void contextCreationFail(const GLuint program, const GLuint shader1, const GLuint shader2) +{ + glDeleteProgram(program); + glDeleteShader(shader1); + glDeleteShader(shader2); +} + +void Window::PrivateData::createContextIfNeeded() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + + if (gl3context.program != 0) + return; + +#if defined(DISTRHO_OS_WINDOWS) +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +# endif + static bool needsInit = true; +# define DGL_EXT(PROC, func) \ + if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ + DISTRHO_SAFE_ASSERT_RETURN(func != nullptr,); +# define DGL_EXT2(PROC, func, fallback) \ + if (needsInit) { \ + func = (PROC) wglGetProcAddress ( #func ); \ + if (func == nullptr) func = (PROC) wglGetProcAddress ( #fallback ); \ + } DISTRHO_SAFE_ASSERT_RETURN(func != nullptr,); +DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) +DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) +DGL_EXT(PFNGLBINDBUFFERPROC, glBindBuffer) +DGL_EXT(PFNGLBUFFERDATAPROC, glBufferData) +DGL_EXT(PFNGLCOMPILESHADERPROC, glCompileShader) +DGL_EXT(PFNGLCREATEPROGRAMPROC, glCreateProgram) +DGL_EXT(PFNGLCREATESHADERPROC, glCreateShader) +DGL_EXT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers) +DGL_EXT(PFNGLDELETEPROGRAMPROC, glDeleteProgram) +DGL_EXT(PFNGLDELETESHADERPROC, glDeleteShader) +DGL_EXT(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray) +DGL_EXT(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray) +DGL_EXT(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation) +DGL_EXT(PFNGLGENBUFFERSPROC, glGenBuffers) +DGL_EXT(PFNGLGETPROGRAMIVPROC, glGetProgramiv) +DGL_EXT(PFNGLGETSHADERIVPROC, glGetShaderiv) +DGL_EXT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog) +DGL_EXT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation) +DGL_EXT(PFNGLLINKPROGRAMPROC, glLinkProgram) +DGL_EXT(PFNGLSHADERSOURCEPROC, glShaderSource) +DGL_EXT(PFNGLUNIFORM1IPROC, glUniform1i) +DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) +DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) +DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) +# undef DGL_EXT +# undef DGL_EXT2 + needsInit = false; +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic pop +# endif +#endif + + int status; + + const GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER); + DISTRHO_SAFE_ASSERT_RETURN(fragment != 0, shaderCreationFail(fragment)); + + const GLuint vertex = glCreateShader(GL_VERTEX_SHADER); + DISTRHO_SAFE_ASSERT_RETURN(vertex != 0, shaderCreationFail(vertex, fragment)); + + const GLuint program = glCreateProgram(); + DISTRHO_SAFE_ASSERT_RETURN(program != 0,); + + #if defined(DGL_USE_GLES2) + #define DGL_SHADER_HEADER "#version 100\n" + #elif defined(DGL_USE_GLES3) + #define DGL_SHADER_HEADER "#version 300 es\n" + #else + #define DGL_SHADER_HEADER "#version 150 core\n" + #endif + + { + static constexpr const char* const src = DGL_SHADER_HEADER + "precision mediump float;" + "uniform vec4 color;" + "uniform sampler2D stex;" + "uniform bool texok;" + #ifdef DGL_USE_GLES3 + "in vec2 vtex;" + "out vec4 FragColor;" + "void main() { FragColor = texok ? texture(stex, vtex) : color; }"; + #else + "varying vec2 vtex;" + "void main() { gl_FragColor = texok ? texture2D(stex, vtex) : color; }"; + #endif + + glShaderSource(fragment, 1, &src, nullptr); + glCompileShader(fragment); + + glGetShaderiv(fragment, GL_COMPILE_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + } + + { + static constexpr const char* const src = DGL_SHADER_HEADER + #ifdef DGL_USE_GLES3 + "in vec4 pos;" + "in vec2 tex;" + "out vec2 vtex;" + #else + "attribute vec4 pos;" + "attribute vec2 tex;" + "varying vec2 vtex;" + #endif + "void main() { gl_Position = pos; vtex = tex; }"; + + glShaderSource(vertex, 1, &src, nullptr); + glCompileShader(vertex); + + glGetShaderiv(vertex, GL_COMPILE_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + } + + glAttachShader(program, fragment); + glAttachShader(program, vertex); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + DISTRHO_SAFE_ASSERT_RETURN(status != 0, contextCreationFail(program, fragment, vertex)); + + glGenBuffers(2, gl3context.buffers); + DISTRHO_SAFE_ASSERT_RETURN(gl3context.buffers[0] != 0, contextCreationFail(program, fragment, vertex)); + DISTRHO_SAFE_ASSERT_RETURN(gl3context.buffers[1] != 0, contextCreationFail(program, fragment, vertex)); + + glDeleteShader(fragment); + glDeleteShader(vertex); + + gl3context.program = program; + gl3context.color = glGetUniformLocation(gl3context.program, "color"); + gl3context.usingTexture = glGetUniformLocation(gl3context.program, "texok"); + gl3context.bounds = glGetAttribLocation(gl3context.program, "pos"); + gl3context.textureMap = glGetAttribLocation(gl3context.program, "tex"); +} + +void Window::PrivateData::destroyContext() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + + if (gl3context.program == 0) + return; + + glDeleteBuffers(2, gl3context.buffers); + glDeleteProgram(gl3context.program); + gl3context.program = 0; +} + +void Window::PrivateData::startContext() +{ + OpenGL3GraphicsContext& gl3context = reinterpret_cast(graphicsContext); + const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); + + gl3context.width = size.width; + gl3context.height = size.height; + glUseProgram(gl3context.program); + + glViewport(0, 0, static_cast(size.width), static_cast(size.height)); +} + +void Window::PrivateData::endContext() +{ + glUseProgram(0); +} + +// -------------------------------------------------------------------------------------------------------------------- + +#ifndef DGL_GEOMETRY_CPP_INCLUDED +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; +template class Line; + +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; +template class Circle; + +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; +template class Triangle; + +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +template class Rectangle; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dpf/dgl/src/TopLevelWidgetPrivateData.cpp b/dpf/dgl/src/TopLevelWidgetPrivateData.cpp index ebdce51..adcd71b 100644 --- a/dpf/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dpf/dgl/src/TopLevelWidgetPrivateData.cpp @@ -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 diff --git a/dpf/dgl/src/TopLevelWidgetPrivateData.hpp b/dpf/dgl/src/TopLevelWidgetPrivateData.hpp index 21060d2..7cbf2c3 100644 --- a/dpf/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dpf/dgl/src/TopLevelWidgetPrivateData.hpp @@ -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) }; diff --git a/dpf/dgl/src/Window.cpp b/dpf/dgl/src/Window.cpp index 57ae257..01c285e 100644 --- a/dpf/dgl/src/Window.cpp +++ b/dpf/dgl/src/Window.cpp @@ -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 Window::getOffset() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point()); - const PuglRect rect = puglGetFrame(pData->view); - return Point(rect.x, rect.y); + const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION); + return Point(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& 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(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(height + 0.5); + const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height; + DISTRHO_SAFE_ASSERT(height > 0); + return height; } Size Window::getSize() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size()); - const PuglRect rect = puglGetFrame(pData->view); - DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size()); - DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size()); - return Size(static_cast(rect.width + 0.5), - static_cast(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(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& rect) noexcept @@ -454,22 +457,22 @@ void Window::repaint(const Rectangle& rect) noexcept if (pData->usesScheduledRepaints) pData->appData->needsRepaint = true; - PuglRect prect = { - static_cast(rect.getX()), - static_cast(rect.getY()), - static_cast(rect.getWidth()), - static_cast(rect.getHeight()), - }; + int x = static_cast(rect.getX()); + int y = static_cast(rect.getY()); + uint width = rect.getWidth(); + uint height = rect.getHeight(); + if (pData->autoScaling) { const double autoScaleFactor = pData->autoScaleFactor; - prect.x = static_cast(prect.x * autoScaleFactor); - prect.y = static_cast(prect.y * autoScaleFactor); - prect.width = static_cast(prect.width * autoScaleFactor + 0.5); - prect.height = static_cast(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 size(getSize()); - setSize(static_cast(size.getWidth() * scaleFactor + 0.5), - static_cast(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) { diff --git a/dpf/dgl/src/WindowPrivateData.cpp b/dpf/dgl/src/WindowPrivateData.cpp index be1a51d..563d2b4 100644 --- a/dpf/dgl/src/WindowPrivateData.cpp +++ b/dpf/dgl/src/WindowPrivateData.cpp @@ -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(width), static_cast(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(rect.width), static_cast(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(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(rect.width) - options.offset.x; - uint initialHeight = static_cast(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(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(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(rect.width), static_cast(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; diff --git a/dpf/dgl/src/WindowPrivateData.hpp b/dpf/dgl/src/WindowPrivateData.hpp index 31b1481..3883184 100644 --- a/dpf/dgl/src/WindowPrivateData.hpp +++ b/dpf/dgl/src/WindowPrivateData.hpp @@ -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 topLevelWidgets; diff --git a/dpf/dgl/src/nanovg/nanovg_gl.h b/dpf/dgl/src/nanovg/nanovg_gl.h index 27e1283..feb5027 100644 --- a/dpf/dgl/src/nanovg/nanovg_gl.h +++ b/dpf/dgl/src/nanovg/nanovg_gl.h @@ -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; idata[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; idata[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); diff --git a/dpf/dgl/src/pugl-extra/haiku.cpp b/dpf/dgl/src/pugl-extra/haiku.cpp index 13db156..bc96cca 100644 --- a/dpf/dgl/src/pugl-extra/haiku.cpp +++ b/dpf/dgl/src/pugl-extra/haiku.cpp @@ -246,7 +246,7 @@ puglPostRedisplayRect(PuglView* const view, const PuglRect rect) } PuglNativeView -puglGetNativeView(PuglView* const view) +puglGetNativeView(const PuglView* const view) { return 0; } diff --git a/dpf/dgl/src/pugl-extra/wasm.c b/dpf/dgl/src/pugl-extra/wasm.c index 0772bef..7b8f43d 100644 --- a/dpf/dgl/src/pugl-extra/wasm.c +++ b/dpf/dgl/src/pugl-extra/wasm.c @@ -1,10 +1,11 @@ // Copyright 2012-2022 David Robillard -// Copyright 2021-2022 Filipe Coelho +// Copyright 2021-2025 Filipe Coelho // SPDX-License-Identifier: ISC #include "wasm.h" #include "../pugl-upstream/src/internal.h" +#include "../pugl-upstream/src/platform.h" #include @@ -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); -} diff --git a/dpf/dgl/src/pugl-upstream/.clang-format b/dpf/dgl/src/pugl-upstream/.clang-format index 26ada71..299c8a2 100644 --- a/dpf/dgl/src/pugl-upstream/.clang-format +++ b/dpf/dgl/src/pugl-upstream/.clang-format @@ -1,10 +1,17 @@ -# Copyright 2020-2022 David Robillard +# Copyright 2020-2025 David Robillard # 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 ... diff --git a/dpf/dgl/src/pugl-upstream/.clang-format-ignore b/dpf/dgl/src/pugl-upstream/.clang-format-ignore new file mode 100644 index 0000000..8e58ff4 --- /dev/null +++ b/dpf/dgl/src/pugl-upstream/.clang-format-ignore @@ -0,0 +1,4 @@ +# Copyright 2025 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +examples/glad/ diff --git a/dpf/dgl/src/pugl-upstream/.clang-tidy b/dpf/dgl/src/pugl-upstream/.clang-tidy index 9af40ce..5ce7497 100644 --- a/dpf/dgl/src/pugl-upstream/.clang-tidy +++ b/dpf/dgl/src/pugl-upstream/.clang-tidy @@ -1,4 +1,4 @@ -# Copyright 2020-2024 David Robillard +# Copyright 2020-2025 David Robillard # 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/.*' diff --git a/dpf/dgl/src/pugl-upstream/.clant.json b/dpf/dgl/src/pugl-upstream/.clant.json index cb178c7..27ce960 100644 --- a/dpf/dgl/src/pugl-upstream/.clant.json +++ b/dpf/dgl/src/pugl-upstream/.clant.json @@ -1,9 +1,5 @@ { "version": "1.0.0", - "include_dirs": [ - "bindings/cpp/include", - "include" - ], "exclude_patterns": [ "glad\\.c" ], diff --git a/dpf/dgl/src/pugl-upstream/.reuse/dep5 b/dpf/dgl/src/pugl-upstream/.reuse/dep5 index 7fea6b9..6585c88 100644 --- a/dpf/dgl/src/pugl-upstream/.reuse/dep5 +++ b/dpf/dgl/src/pugl-upstream/.reuse/dep5 @@ -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 +Copyright: 2021-2025 David Robillard Comment: Documentation License: ISC diff --git a/dpf/dgl/src/pugl-upstream/README.md b/dpf/dgl/src/pugl-upstream/README.md index f8e9aba..839c52a 100644 --- a/dpf/dgl/src/pugl-upstream/README.md +++ b/dpf/dgl/src/pugl-upstream/README.md @@ -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 ------------- diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/attributes.h b/dpf/dgl/src/pugl-upstream/include/pugl/attributes.h index 75322ee..1e2264c 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/attributes.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/attributes.h @@ -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 diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/cairo.h b/dpf/dgl/src/pugl-upstream/include/pugl/cairo.h index fda1191..8a5ae4e 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/cairo.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/cairo.h @@ -4,8 +4,8 @@ #ifndef PUGL_CAIRO_H #define PUGL_CAIRO_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include 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); /** diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/gl.h b/dpf/dgl/src/pugl-upstream/include/pugl/gl.h index ac7c557..f4d5f3d 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/gl.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/gl.h @@ -4,8 +4,8 @@ #ifndef PUGL_GL_H #define PUGL_GL_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include // 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 diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/pugl.h b/dpf/dgl/src/pugl-upstream/include/pugl/pugl.h index ac5f51c..ef7320b 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/pugl.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/pugl.h @@ -4,7 +4,7 @@ #ifndef PUGL_PUGL_H #define PUGL_PUGL_H -#include "pugl/attributes.h" +#include #include #include @@ -46,22 +46,17 @@ typedef int16_t PuglCoord; */ typedef uint16_t PuglSpan; -/** - A rectangle in a view or on the screen. - - This type is used to describe two things: the position and size of a view - (for configuring), or a rectangle within a view (for exposing). - - The coordinate (0, 0) represents the top-left pixel of the parent window (or - display if there isn't one), or the top-left pixel of the view, - respectively. -*/ +/// A 2-dimensional position within/of a view typedef struct { PuglCoord x; PuglCoord y; - PuglSpan width; - PuglSpan height; -} PuglRect; +} PuglPoint; + +/// A 2-dimensional size within/of a view +typedef struct { + PuglSpan width; + PuglSpan height; +} PuglArea; /// A string property for configuration typedef enum { @@ -298,9 +293,9 @@ typedef PuglAnyEvent PuglCloseEvent; This event is sent to every view near the end of a main loop iteration when any pending exposures are about to be redrawn. It is typically used to mark - regions to expose with puglPostRedisplay() or puglPostRedisplayRect(). For - example, to continuously animate, a view calls puglPostRedisplay() when an - update event is received, and it will then shortly receive an expose event. + regions to expose with puglObscureView() or puglObscureRegion(). For + example, to continuously animate, obscure the view when an update event is + received, and it will receive an expose event shortly afterwards. */ typedef PuglAnyEvent PuglUpdateEvent; @@ -341,76 +336,77 @@ typedef struct { mapping used here is arbitrary and specific to Pugl. */ typedef enum { - PUGL_KEY_BACKSPACE = 0x00000008U, ///< Backspace - PUGL_KEY_TAB = 0x00000009U, ///< Tab - PUGL_KEY_ENTER = 0x0000000DU, ///< Enter - PUGL_KEY_ESCAPE = 0x0000001BU, ///< Escape - PUGL_KEY_DELETE = 0x0000007FU, ///< Delete - PUGL_KEY_SPACE = 0x00000020U, ///< Space - PUGL_KEY_F1 = 0x0000E000U, ///< F1 - PUGL_KEY_F2, ///< F2 - PUGL_KEY_F3, ///< F3 - PUGL_KEY_F4, ///< F4 - PUGL_KEY_F5, ///< F5 - PUGL_KEY_F6, ///< F6 - PUGL_KEY_F7, ///< F7 - PUGL_KEY_F8, ///< F8 - PUGL_KEY_F9, ///< F9 - PUGL_KEY_F10, ///< F10 - PUGL_KEY_F11, ///< F11 - PUGL_KEY_F12, ///< F12 - PUGL_KEY_PAGE_UP = 0xE031, ///< Page Up - PUGL_KEY_PAGE_DOWN, ///< Page Down - PUGL_KEY_END, ///< End - PUGL_KEY_HOME, ///< Home - PUGL_KEY_LEFT, ///< Left - PUGL_KEY_UP, ///< Up - PUGL_KEY_RIGHT, ///< Right - PUGL_KEY_DOWN, ///< Down - PUGL_KEY_PRINT_SCREEN = 0xE041U, ///< Print Screen - PUGL_KEY_INSERT, ///< Insert - PUGL_KEY_PAUSE, ///< Pause/Break - PUGL_KEY_MENU, ///< Menu - PUGL_KEY_NUM_LOCK, ///< Num Lock - PUGL_KEY_SCROLL_LOCK, ///< Scroll Lock - PUGL_KEY_CAPS_LOCK, ///< Caps Lock - PUGL_KEY_SHIFT_L = 0xE051U, ///< Left Shift - PUGL_KEY_SHIFT_R, ///< Right Shift - PUGL_KEY_CTRL_L, ///< Left Control - PUGL_KEY_CTRL_R, ///< Right Control - PUGL_KEY_ALT_L, ///< Left Alt - PUGL_KEY_ALT_R, ///< Right Alt / AltGr - PUGL_KEY_SUPER_L, ///< Left Super - PUGL_KEY_SUPER_R, ///< Right Super - PUGL_KEY_PAD_0 = 0xE060U, ///< Keypad 0 - PUGL_KEY_PAD_1, ///< Keypad 1 - PUGL_KEY_PAD_2, ///< Keypad 2 - PUGL_KEY_PAD_3, ///< Keypad 3 - PUGL_KEY_PAD_4, ///< Keypad 4 - PUGL_KEY_PAD_5, ///< Keypad 5 - PUGL_KEY_PAD_6, ///< Keypad 6 - PUGL_KEY_PAD_7, ///< Keypad 7 - PUGL_KEY_PAD_8, ///< Keypad 8 - PUGL_KEY_PAD_9, ///< Keypad 9 - PUGL_KEY_PAD_ENTER, ///< Keypad Enter - PUGL_KEY_PAD_PAGE_UP = 0xE071U, ///< Keypad Page Up - PUGL_KEY_PAD_PAGE_DOWN, ///< Keypad Page Down - PUGL_KEY_PAD_END, ///< Keypad End - PUGL_KEY_PAD_HOME, ///< Keypad Home - PUGL_KEY_PAD_LEFT, ///< Keypad Left - PUGL_KEY_PAD_UP, ///< Keypad Up - PUGL_KEY_PAD_RIGHT, ///< Keypad Right - PUGL_KEY_PAD_DOWN, ///< Keypad Down - PUGL_KEY_PAD_CLEAR = 0xE09DU, ///< Keypad Clear/Begin - PUGL_KEY_PAD_INSERT, ///< Keypad Insert - PUGL_KEY_PAD_DELETE, ///< Keypad Delete - PUGL_KEY_PAD_EQUAL, ///< Keypad Equal - PUGL_KEY_PAD_MULTIPLY = 0xE0AAU, ///< Keypad Multiply - PUGL_KEY_PAD_ADD, ///< Keypad Add - PUGL_KEY_PAD_SEPARATOR, ///< Keypad Separator - PUGL_KEY_PAD_SUBTRACT, ///< Keypad Subtract - PUGL_KEY_PAD_DECIMAL, ///< Keypad Decimal - PUGL_KEY_PAD_DIVIDE, ///< Keypad Divide + PUGL_KEY_NONE = 0U, ///< Sentinel value for no key + PUGL_KEY_BACKSPACE = 0x0008U, ///< Backspace + PUGL_KEY_TAB = 0x0009U, ///< Tab + PUGL_KEY_ENTER = 0x000DU, ///< Enter + PUGL_KEY_ESCAPE = 0x001BU, ///< Escape + PUGL_KEY_DELETE = 0x007FU, ///< Delete + PUGL_KEY_SPACE = 0x0020U, ///< Space + PUGL_KEY_F1 = 0xE000U, ///< F1 + PUGL_KEY_F2 = 0xE001U, ///< F2 + PUGL_KEY_F3 = 0xE002U, ///< F3 + PUGL_KEY_F4 = 0xE003U, ///< F4 + PUGL_KEY_F5 = 0xE004U, ///< F5 + PUGL_KEY_F6 = 0xE005U, ///< F6 + PUGL_KEY_F7 = 0xE006U, ///< F7 + PUGL_KEY_F8 = 0xE007U, ///< F8 + PUGL_KEY_F9 = 0xE008U, ///< F9 + PUGL_KEY_F10 = 0xE009U, ///< F10 + PUGL_KEY_F11 = 0xE010U, ///< F11 + PUGL_KEY_F12 = 0xE011U, ///< F12 + PUGL_KEY_PAGE_UP = 0xE031U, ///< Page Up + PUGL_KEY_PAGE_DOWN = 0xE032U, ///< Page Down + PUGL_KEY_END = 0xE033U, ///< End + PUGL_KEY_HOME = 0xE034U, ///< Home + PUGL_KEY_LEFT = 0xE035U, ///< Left + PUGL_KEY_UP = 0xE036U, ///< Up + PUGL_KEY_RIGHT = 0xE037U, ///< Right + PUGL_KEY_DOWN = 0xE038U, ///< Down + PUGL_KEY_PRINT_SCREEN = 0xE041U, ///< Print Screen + PUGL_KEY_INSERT = 0xE042U, ///< Insert + PUGL_KEY_PAUSE = 0xE043U, ///< Pause/Break + PUGL_KEY_MENU = 0xE044U, ///< Menu + PUGL_KEY_NUM_LOCK = 0xE045U, ///< Num Lock + PUGL_KEY_SCROLL_LOCK = 0xE046U, ///< Scroll Lock + PUGL_KEY_CAPS_LOCK = 0xE047U, ///< Caps Lock + PUGL_KEY_SHIFT_L = 0xE051U, ///< Left Shift + PUGL_KEY_SHIFT_R = 0xE052U, ///< Right Shift + PUGL_KEY_CTRL_L = 0xE053U, ///< Left Control + PUGL_KEY_CTRL_R = 0xE054U, ///< Right Control + PUGL_KEY_ALT_L = 0xE055U, ///< Left Alt + PUGL_KEY_ALT_R = 0xE056U, ///< Right Alt / AltGr + PUGL_KEY_SUPER_L = 0xE057U, ///< Left Super + PUGL_KEY_SUPER_R = 0xE058U, ///< Right Super + PUGL_KEY_PAD_0 = 0xE060U, ///< Keypad 0 + PUGL_KEY_PAD_1 = 0xE061U, ///< Keypad 1 + PUGL_KEY_PAD_2 = 0xE062U, ///< Keypad 2 + PUGL_KEY_PAD_3 = 0xE063U, ///< Keypad 3 + PUGL_KEY_PAD_4 = 0xE064U, ///< Keypad 4 + PUGL_KEY_PAD_5 = 0xE065U, ///< Keypad 5 + PUGL_KEY_PAD_6 = 0xE066U, ///< Keypad 6 + PUGL_KEY_PAD_7 = 0xE067U, ///< Keypad 7 + PUGL_KEY_PAD_8 = 0xE068U, ///< Keypad 8 + PUGL_KEY_PAD_9 = 0xE069U, ///< Keypad 9 + PUGL_KEY_PAD_ENTER = 0xE070U, ///< Keypad Enter + PUGL_KEY_PAD_PAGE_UP = 0xE071U, ///< Keypad Page Up + PUGL_KEY_PAD_PAGE_DOWN = 0xE072U, ///< Keypad Page Down + PUGL_KEY_PAD_END = 0xE073U, ///< Keypad End + PUGL_KEY_PAD_HOME = 0xE074U, ///< Keypad Home + PUGL_KEY_PAD_LEFT = 0xE075U, ///< Keypad Left + PUGL_KEY_PAD_UP = 0xE076U, ///< Keypad Up + PUGL_KEY_PAD_RIGHT = 0xE077U, ///< Keypad Right + PUGL_KEY_PAD_DOWN = 0xE078U, ///< Keypad Down + PUGL_KEY_PAD_CLEAR = 0xE09DU, ///< Keypad Clear/Begin + PUGL_KEY_PAD_INSERT = 0xE09EU, ///< Keypad Insert + PUGL_KEY_PAD_DELETE = 0xE09FU, ///< Keypad Delete + PUGL_KEY_PAD_EQUAL = 0xE0A0U, ///< Keypad Equal + PUGL_KEY_PAD_MULTIPLY = 0xE0AAU, ///< Keypad Multiply + PUGL_KEY_PAD_ADD = 0xE0ABU, ///< Keypad Add + PUGL_KEY_PAD_SEPARATOR = 0xE0ACU, ///< Keypad Separator + PUGL_KEY_PAD_SUBTRACT = 0xE0ADU, ///< Keypad Subtract + PUGL_KEY_PAD_DECIMAL = 0xE0AEU, ///< Keypad Decimal + PUGL_KEY_PAD_DIVIDE = 0xE0AFU, ///< Keypad Divide } PuglKey; /// Keyboard modifier flags @@ -443,8 +439,8 @@ typedef struct { Key press or release event. This event represents low-level key presses and releases. This can be used - for "direct" keyboard handing like key bindings, but must not be interpreted - as text input. + for "direct" keyboard handling like key bindings, but must not be + interpreted as text input. Keys are represented portably as Unicode code points, using the "natural" code point for the key where possible (see #PuglKey for details). The `key` @@ -684,9 +680,8 @@ typedef struct { to the appropriate type, or the union members used. The graphics system may only be accessed when handling certain events. The - graphics context is active for #PUGL_REALIZE, #PUGL_UNREALIZE, - #PUGL_CONFIGURE, and #PUGL_EXPOSE, but only enabled for drawing for - #PUGL_EXPOSE. + graphics context is active for #PUGL_REALIZE, #PUGL_UNREALIZE, and + #PUGL_EXPOSE, but only enabled for drawing for #PUGL_EXPOSE. */ typedef union { PuglAnyEvent any; ///< Valid for all event types @@ -709,9 +704,7 @@ typedef union { /** @} @defgroup pugl_status Status - Most functions return a status code which can be used to check for errors. - @{ */ @@ -723,6 +716,7 @@ typedef enum { PUGL_BAD_BACKEND, ///< Invalid or missing backend PUGL_BAD_CONFIGURATION, ///< Invalid view configuration PUGL_BAD_PARAMETER, ///< Invalid parameter + PUGL_BAD_CALL, ///< Invalid call PUGL_BACKEND_FAILED, ///< Backend initialization failed PUGL_REGISTRATION_FAILED, ///< Class registration failed PUGL_REALIZE_FAILED, ///< System view realization failed @@ -733,8 +727,7 @@ typedef enum { } PuglStatus; /// Return a string describing a status code -PUGL_CONST_API -const char* +PUGL_CONST_API const char* puglStrerror(PuglStatus status); /** @@ -790,13 +783,11 @@ typedef uint32_t PuglWorldFlags; @param flags Flags to control world features. @return A new world, which must be later freed with puglFreeWorld(). */ -PUGL_MALLOC_API -PuglWorld* +PUGL_MALLOC_API PuglWorld* puglNewWorld(PuglWorldType type, PuglWorldFlags flags); /// Free a world allocated with puglNewWorld() -PUGL_API -void +PUGL_API void puglFreeWorld(PuglWorld* world); /** @@ -807,17 +798,19 @@ puglFreeWorld(PuglWorld* world); The handle is opaque to Pugl and is not interpreted in any way. */ -PUGL_API -void +PUGL_API void puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle); /// Get the user data for the world -PUGL_API -PuglWorldHandle +PUGL_API PuglWorldHandle puglGetWorldHandle(PuglWorld* world); /** - Return a pointer to the native handle of the world. + Return the native world handle. + + The "native world" is a system-specific handle that's shared across an + entire program or module. It serves as a common denominator between pugl, + host applications, the windowing system API, and other APIs that use it. X11: Returns a pointer to the `Display`. @@ -825,8 +818,7 @@ puglGetWorldHandle(PuglWorld* world); Windows: Returns the `HMODULE` of the calling process. */ -PUGL_API -void* +PUGL_API void* puglGetNativeWorld(PuglWorld* world); /** @@ -835,8 +827,7 @@ puglGetNativeWorld(PuglWorld* world); The string value only needs to be valid for the duration of this call, it will be copied if necessary. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetWorldString(PuglWorld* world, PuglStringHint key, const char* value); /** @@ -845,8 +836,7 @@ puglSetWorldString(PuglWorld* world, PuglStringHint key, const char* value); The returned string should be accessed immediately, or copied. It may become invalid upon any call to any function that manipulates the same view. */ -PUGL_API -const char* +PUGL_API const char* puglGetWorldString(const PuglWorld* world, PuglStringHint key); /** @@ -856,8 +846,7 @@ puglGetWorldString(const PuglWorld* world, PuglStringHint key); time is only useful to compare against other times returned by this function, its absolute value has no meaning. */ -PUGL_API -double +PUGL_API double puglGetTime(const PuglWorld* world); /** @@ -882,8 +871,7 @@ puglGetTime(const PuglWorld* world); @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if no events are read, or an error. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglUpdate(PuglWorld* world, double timeout); /** @@ -919,6 +907,11 @@ typedef struct PuglBackendImpl PuglBackend; /** A native view handle. + A "native view" is a system-specific handle for a view (or "window"). A + #PuglView is itself a native view, which may be embedded in another. Like + the native world handle, this serves as a common denominator between this + and other APIs. + X11: This is a `Window`. MacOS: This is a pointer to an `NSView*`. @@ -959,9 +952,9 @@ typedef enum { /// A special view hint value typedef enum { - PUGL_DONT_CARE = -1, ///< Generic trinary: Use best default - PUGL_FALSE = 0, ///< Generic trinary: Explicitly false - PUGL_TRUE = 1, ///< Generic trinary: Explicitly true + PUGL_DONT_CARE = -1, ///< Generic trinary: unset + PUGL_FALSE = 0, ///< Generic trinary: false + PUGL_TRUE = 1, ///< Generic trinary: true PUGL_OPENGL_API = 2, ///< For #PUGL_CONTEXT_API PUGL_OPENGL_ES_API = 3, ///< For #PUGL_CONTEXT_API PUGL_OPENGL_CORE_PROFILE = 4, ///< For #PUGL_CONTEXT_PROFILE @@ -975,6 +968,49 @@ typedef enum { PUGL_VIEW_TYPE_DIALOG, ///< A dialog window } PuglViewType; +/** + A hint for configuring/constraining the position of a view. + + The system will attempt to make the view's window adhere to these, but they + are suggestions, not hard constraints. Applications should handle any view + position gracefully. + + An unset position has `INT16_MIN` (-32768) for both `x` and `y`. In + practice, set positions should be between -16000 and 16000 for portability. + Usually, the origin is the top left of the display, although negative + coordinates are possible, particularly on multi-display system. +*/ +typedef enum { + /** + Default position. + + This is used as the position during window creation as a default, if no + other position is specified. It isn't necessary to set a default position + (unlike the default size, which is required). If not even a default + position is set, then the window will be created at an arbitrary position. + This position is a best-effort attempt to do the most reasonable thing for + the initial display of the window, for example, by centering. Note that + it is implementation-defined, subject to change, platform-specific, and + for embedded views, may no longer make sense if the parent's size is + adjusted. Code that wants to make assumptions about the initial position + must set the default to a specific valid one, such as `{0, 0}`. + */ + PUGL_DEFAULT_POSITION, + + /** + Current position. + + This reflects the current position of the view, which may be different from + the default position if the view has been moved by the user, window + manager, or for any other reason. Typically, it overrides the + default position. + */ + PUGL_CURRENT_POSITION, +} PuglPositionHint; + +/// The number of #PuglPositionHint values +#define PUGL_NUM_POSITION_HINTS ((unsigned)PUGL_CURRENT_POSITION + 1U) + /** A hint for configuring/constraining the size of a view. @@ -991,6 +1027,15 @@ typedef enum { */ PUGL_DEFAULT_SIZE, + /** + Current size. + + This reflects the current size of the view, which may be different from + the default size if the view is resizable. Typically, it overrides the + default size. + */ + PUGL_CURRENT_SIZE, + /** Minimum size. @@ -1049,18 +1094,15 @@ typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); It must first be configured, then the system view can be created with puglRealize(). */ -PUGL_MALLOC_API -PuglView* +PUGL_MALLOC_API PuglView* puglNewView(PuglWorld* world); /// Free a view created with puglNewView() -PUGL_API -void +PUGL_API void puglFreeView(PuglView* view); /// Return the world that `view` is a part of -PUGL_API -PuglWorld* +PUGL_API PuglWorld* puglGetWorld(PuglView* view); /** @@ -1072,13 +1114,11 @@ puglGetWorld(PuglView* view); The handle is opaque to Pugl and is not interpreted in any way. */ -PUGL_API -void +PUGL_API void puglSetHandle(PuglView* view, PuglHandle handle); /// Get the user data for a view -PUGL_API -PuglHandle +PUGL_API PuglHandle puglGetHandle(PuglView* view); /** @@ -1098,18 +1138,15 @@ puglGetHandle(PuglView* view); applications must link against the appropriate backend library, or be sure to compile in the appropriate code if using a local copy of Pugl. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetBackend(PuglView* view, const PuglBackend* backend); /// Return the graphics backend used by a view -PUGL_API -const PuglBackend* +PUGL_API const PuglBackend* puglGetBackend(const PuglView* view); /// Set the function to call when an event occurs -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); /** @@ -1117,8 +1154,7 @@ puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); This only has an effect when called before puglRealize(). */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewHint(PuglView* view, PuglViewHint hint, int value); /** @@ -1128,8 +1164,7 @@ puglSetViewHint(PuglView* view, PuglViewHint hint, int value); hint which was initially set to PUGL_DONT_CARE, or has been adjusted from the suggested value. */ -PUGL_API -int +PUGL_API int puglGetViewHint(const PuglView* view, PuglViewHint hint); /** @@ -1139,8 +1174,7 @@ puglGetViewHint(const PuglView* view, PuglViewHint hint); string value only needs to be valid for the duration of this call, it will be copied if necessary. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewString(PuglView* view, PuglStringHint key, const char* value); /** @@ -1149,8 +1183,7 @@ puglSetViewString(PuglView* view, PuglStringHint key, const char* value); The returned string should be accessed immediately, or copied. It may become invalid upon any call to any function that manipulates the same view. */ -PUGL_API -const char* +PUGL_API const char* puglGetViewString(const PuglView* view, PuglStringHint key); /** @@ -1165,8 +1198,7 @@ puglGetViewString(const PuglView* view, PuglStringHint key); that is reasonably sized on a 96 DPI display, and the scale 2.0 should have text twice that large. */ -PUGL_API -double +PUGL_API double puglGetScaleFactor(const PuglView* view); /** @@ -1177,64 +1209,56 @@ puglGetScaleFactor(const PuglView* view); */ /** - Get the current position and size of the view. + Get a position hint for the view. - The position is in screen coordinates with an upper left origin. + This can be used to get the default or current position of a view, in screen + coordinates with an upper left origin. */ -PUGL_API -PuglRect -puglGetFrame(const PuglView* view); +PUGL_API PuglPoint +puglGetPositionHint(const PuglView* view, PuglPositionHint hint); /** - Set the current position and size of the view. + Set a position hint for the view. - The position is in screen coordinates with an upper left origin. + This can be used to set the default or current position of a view. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. -*/ -PUGL_API -PuglStatus -puglSetFrame(PuglView* view, PuglRect frame); - -/** - Set the current position of the view. + This should be called before puglRealize() so the initial window for the + view can be configured correctly. It may also be used dynamically after the + window is realized, for some hints. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. + @return An error code on failure, but always succeeds if the view is not yet + realized. */ -PUGL_API -PuglStatus -puglSetPosition(PuglView* view, int x, int y); +PUGL_API PuglStatus +puglSetPositionHint(PuglView* view, PuglPositionHint hint, int x, int y); /** - Set the current size of the view. + Get a size hint for the view. - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. + This can be used to get the default, current, minimum, and maximum size of a + view, as well as the supported range of aspect ratios. */ -PUGL_API -PuglStatus -puglSetSize(PuglView* view, unsigned width, unsigned height); +PUGL_API PuglArea +puglGetSizeHint(const PuglView* view, PuglSizeHint hint); /** Set a size hint for the view. - This can be used to set the default, minimum, and maximum size of a view, - as well as the supported range of aspect ratios. + This can be used to set the default, current, minimum, and maximum size of a + view, as well as the supported range of aspect ratios. This should be called before puglRealize() so the initial window for the - view can be configured correctly. + view can be configured correctly. It may also be used dynamically after the + window is realized, for some hints. - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. + @return An error code on failure, but always succeeds if the view is not yet + realized. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetSizeHint(PuglView* view, PuglSizeHint hint, - PuglSpan width, - PuglSpan height); + unsigned width, + unsigned height); /** @} @@ -1244,18 +1268,21 @@ puglSetSizeHint(PuglView* view, */ /** - Set the parent window for embedding a view in an existing window. + Set the native view the view will be embedded in. This must be called before puglRealize(), reparenting is not supported. */ -PUGL_API -PuglStatus -puglSetParentWindow(PuglView* view, PuglNativeView parent); +PUGL_API PuglStatus +puglSetParent(PuglView* view, PuglNativeView parent); -/// Return the parent window this view is embedded in, or null -PUGL_API -PuglNativeView -puglGetParentWindow(const PuglView* view); +/** + Return the native view that the view is embedded in. + + @return The native view handle of the parent that `view` is embedded in, or + zero (if view is top-level or unrealized). +*/ +PUGL_API PuglNativeView +puglGetParent(const PuglView* view); /** Set the transient parent of the window. @@ -1267,8 +1294,7 @@ puglGetParentWindow(const PuglView* view); A view can either have a parent (for embedding) or a transient parent (for top-level windows like dialogs), but not both. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetTransientParent(PuglView* view, PuglNativeView parent); /** @@ -1277,8 +1303,7 @@ puglSetTransientParent(PuglView* view, PuglNativeView parent); @return The native handle to the window this view is a transient child of, or null. */ -PUGL_API -PuglNativeView +PUGL_API PuglNativeView puglGetTransientParent(const PuglView* view); /** @@ -1290,8 +1315,7 @@ puglGetTransientParent(const PuglView* view); The view should be fully configured using the above functions before this is called. This function may only be called once per view. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglRealize(PuglView* view); /** @@ -1300,8 +1324,7 @@ puglRealize(PuglView* view); This is the inverse of puglRealize(). After this call, the view no longer corresponds to a real system view, and can be realized again later. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglUnrealize(PuglView* view); /// A command to control the behaviour of puglShow() @@ -1340,22 +1363,26 @@ typedef enum { Show the view. If the view has not yet been realized, the first call to this function will - do so automatically. + do so automatically. If the view is currently hidden, it will be shown and + possibly raised to the top depending on the platform. - If the view is currently hidden, it will be shown and possibly raised to the - top depending on the platform. + @return #PUGL_SUCCESS, an error from realization, or #PUGL_FAILURE if the + window was shown but not raised. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglShow(PuglView* view, PuglShowCommand command); -/// Hide the current window -PUGL_API -PuglStatus +/** + Hide the view. + + This is the counterpart to puglShow(). If the view is currently visible, it + will be hidden, otherwise, this does nothing. +*/ +PUGL_API PuglStatus puglHide(PuglView* view); /** - Set a view state, if supported by the system. + Set a view style, if supported by the system. This can be used to manipulate the window into various special states, but note that not all states are supported on all systems. This function may @@ -1364,29 +1391,20 @@ puglHide(PuglView* view); used to determine if the state has actually been set. Any changes to the actual state of the view will arrive in later configure events. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetViewStyle(PuglView* view, PuglViewStyleFlags flags); -/** - Return true if the view currently has a state flag set. - - The result is determined based on the state announced in the last configure - event. -*/ -PUGL_API -PuglViewStyleFlags +/// Return the style flags currently set for a view +PUGL_API PuglViewStyleFlags puglGetViewStyle(const PuglView* view); /// Return true iff the view is currently visible -PUGL_API -bool +PUGL_API bool puglGetVisible(const PuglView* view); /// Return the native window handle -PUGL_API -PuglNativeView -puglGetNativeView(PuglView* view); +PUGL_API PuglNativeView +puglGetNativeView(const PuglView* view); /** @} @@ -1406,8 +1424,7 @@ puglGetNativeView(PuglView* view); All other backends: returns null. */ -PUGL_API -void* +PUGL_API void* puglGetContext(PuglView* view); /** @@ -1419,19 +1436,32 @@ puglGetContext(PuglView* view); platforms. If called elsewhere, an expose will be enqueued to be processed in the next event loop iteration. */ -PUGL_API -PuglStatus -puglPostRedisplay(PuglView* view); +PUGL_API PuglStatus +puglObscureView(PuglView* view); /** - Request a redisplay of the given rectangle within the view. + "Obscure" a region so it will be exposed in the next render. - This has the same semantics as puglPostRedisplay(), but allows giving a - precise region for redrawing only a portion of the view. + This will cause an expose event to be dispatched later. If called from + within the event handler, the expose should arrive at the end of the current + event loop iteration, though this is not strictly guaranteed on all + platforms. If called elsewhere, an expose will be enqueued to be processed + in the next event loop iteration. + + The region is clamped to the size of the view if necessary. + + @param view The view to expose later. + @param x The top-left X coordinate of the rectangle to obscure. + @param y The top-left Y coordinate of the rectangle to obscure. + @param width The width of the rectangle to obscure. + @param height The height coordinate of the rectangle to obscure. */ -PUGL_API -PuglStatus -puglPostRedisplayRect(PuglView* view, PuglRect rect); +PUGL_API PuglStatus +puglObscureRegion(PuglView* view, + int x, + int y, + unsigned width, + unsigned height); /** @} @@ -1470,13 +1500,15 @@ typedef enum { @return #PUGL_SUCCESS if the focus was successfully grabbed, or an error. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglGrabFocus(PuglView* view); -/// Return whether `view` has the keyboard input focus -PUGL_API -bool +/** + Return whether `view` has the keyboard input focus. + + @return True if the view is realized and focused for keyboard input. +*/ +PUGL_API bool puglHasFocus(const PuglView* view); /** @@ -1484,8 +1516,7 @@ puglHasFocus(const PuglView* view); A #PUGL_DATA_OFFER event will be sent if data is available. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglPaste(PuglView* view); /** @@ -1493,8 +1524,7 @@ puglPaste(PuglView* view); Returns zero if the clipboard is empty. */ -PUGL_API -uint32_t +PUGL_API uint32_t puglGetNumClipboardTypes(const PuglView* view); /** @@ -1506,8 +1536,7 @@ puglGetNumClipboardTypes(const PuglView* view); Returns null if `typeIndex` is out of bounds according to puglGetNumClipboardTypes(). */ -PUGL_API -const char* +PUGL_API const char* puglGetClipboardType(const PuglView* view, uint32_t typeIndex); /** @@ -1526,8 +1555,7 @@ puglGetClipboardType(const PuglView* view, uint32_t typeIndex); the `typeIndex` argument to the call of puglGetClipboardType() that returned the accepted type. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglAcceptOffer(PuglView* view, const PuglDataOfferEvent* offer, uint32_t typeIndex); @@ -1543,8 +1571,7 @@ puglAcceptOffer(PuglView* view, @param data The data to copy to the clipboard. @param len The length of data in bytes (including terminator if necessary). */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetClipboard(PuglView* view, const char* type, const void* data, @@ -1561,8 +1588,7 @@ puglSetClipboard(PuglView* view, @param[out] len Set to the length of the data in bytes. @return The clipboard contents, or null. */ -PUGL_API -const void* +PUGL_API const void* puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); /** @@ -1576,8 +1602,7 @@ puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); #PUGL_UNSUPPORTED if setting the cursor is not supported on this system, or another error if the cursor is known but loading it fails. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSetCursor(PuglView* view, PuglCursor cursor); /** @@ -1606,8 +1631,7 @@ puglSetCursor(PuglView* view, PuglCursor cursor); @return #PUGL_FAILURE if timers are not supported by the system, #PUGL_UNKNOWN_ERROR if setting the timer failed. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglStartTimer(PuglView* view, uintptr_t id, double timeout); /** @@ -1619,8 +1643,7 @@ puglStartTimer(PuglView* view, uintptr_t id, double timeout); @return #PUGL_FAILURE if timers are not supported by this system, #PUGL_UNKNOWN_ERROR if stopping the timer failed. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglStopTimer(PuglView* view, uintptr_t id); /** @@ -1633,553 +1656,17 @@ puglStopTimer(PuglView* view, uintptr_t id); Currently, only #PUGL_CLIENT events are supported on all platforms. X11: A #PUGL_EXPOSE event can be sent, which is similar to calling - puglPostRedisplayRect(), but will always send a message to the X server, - even when called in an event handler. + puglObscureRegion(), but will always send a message to the X server, even + when called in an event handler. - @return #PUGL_UNSUPPORTED if sending events of this type is not supported, - #PUGL_UNKNOWN_ERROR if sending the event failed. + @return #PUGL_UNSUPPORTED if sending this type of event isn't supported in + general, #PUGL_FAILURE if the event can't be sent now. */ -PUGL_API -PuglStatus +PUGL_API PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event); /** @} -*/ - -#ifndef PUGL_DISABLE_DEPRECATED - -/** - @} - @defgroup pugl_deprecated Deprecated API - @{ -*/ - -PUGL_DEPRECATED_BY("PuglRealizeEvent") -typedef PuglRealizeEvent PuglCreateEvent; - -PUGL_DEPRECATED_BY("PuglUnrealizeEvent") -typedef PuglUnrealizeEvent PuglDestroyEvent; - -PUGL_DEPRECATED_BY("PuglRealizeEvent") -typedef PuglRealizeEvent PuglEventCreate; - -PUGL_DEPRECATED_BY("PuglUnrealizeEvent") -typedef PuglUnrealizeEvent PuglEventDestroy; - -PUGL_DEPRECATED_BY("PuglConfigureEvent") -typedef PuglConfigureEvent PuglEventConfigure; - -PUGL_DEPRECATED_BY("PuglUpdateEvent") -typedef PuglUpdateEvent PuglEventUpdate; - -PUGL_DEPRECATED_BY("PuglExposeEvent") -typedef PuglExposeEvent PuglEventExpose; - -PUGL_DEPRECATED_BY("PuglCloseEvent") -typedef PuglCloseEvent PuglEventClose; - -PUGL_DEPRECATED_BY("PuglFocusEvent") -typedef PuglFocusEvent PuglEventFocus; - -PUGL_DEPRECATED_BY("PuglKeyEvent") -typedef PuglKeyEvent PuglEventKey; - -PUGL_DEPRECATED_BY("PuglTextEvent") -typedef PuglTextEvent PuglEventText; - -PUGL_DEPRECATED_BY("PuglCrossingEvent") -typedef PuglCrossingEvent PuglEventCrossing; - -PUGL_DEPRECATED_BY("PuglButtonEvent") -typedef PuglButtonEvent PuglEventButton; - -PUGL_DEPRECATED_BY("PuglMotionEvent") -typedef PuglMotionEvent PuglEventMotion; - -PUGL_DEPRECATED_BY("PuglScrollEvent") -typedef PuglScrollEvent PuglEventScroll; - -PUGL_DEPRECATED_BY("PuglClientEvent") -typedef PuglClientEvent PuglEventClient; - -PUGL_DEPRECATED_BY("PuglTimerEvent") -typedef PuglTimerEvent PuglEventTimer; - -PUGL_DEPRECATED_BY("PuglLoopEnterEvent") -typedef PuglLoopEnterEvent PuglEventLoopEnter; - -PUGL_DEPRECATED_BY("PuglLoopLeaveEvent") -typedef PuglLoopLeaveEvent PuglEventLoopLeave; - -/** - A native window handle. - - X11: This is a `Window`. - - MacOS: This is a pointer to an `NSView*`. - - Windows: This is a `HWND`. -*/ -PUGL_DEPRECATED_BY("PuglNativeView") -typedef uintptr_t PuglNativeWindow; - -/** - Create a Pugl application and view. - - To create a window, call the various puglInit* functions as necessary, then - call puglRealize(). - - @deprecated Use puglNewApp() and puglNewView(). - - @param pargc Pointer to argument count (currently unused). - @param argv Arguments (currently unused). - @return A newly created view. -*/ -static inline PUGL_DEPRECATED_BY("puglNewView") -PuglView* -puglInit(const int* pargc, char** argv) -{ - (void)pargc; - (void)argv; - - return puglNewView(puglNewWorld(PUGL_MODULE, 0)); -} - -/** - Destroy an app and view created with `puglInit()`. - - @deprecated Use puglFreeApp() and puglFreeView(). -*/ -static inline PUGL_DEPRECATED_BY("puglFreeView") -void -puglDestroy(PuglView* view) -{ - PuglWorld* const world = puglGetWorld(view); - - puglFreeView(view); - puglFreeWorld(world); -} - -/** - Set the class name of the application. - - This is a stable identifier for the application, used as the window - class/instance name on X11 and Windows. It is not displayed to the user, - but can be used in scripts and by window managers, so it should be the same - for every instance of the application, but different from other - applications. -*/ -static inline PUGL_DEPRECATED_BY("puglSetWorldString") -PuglStatus -puglSetClassName(PuglWorld* world, const char* name) -{ - return puglSetWorldString(world, PUGL_CLASS_NAME, name); -} - -/// Get the class name of the application, or null -static inline PUGL_DEPRECATED_BY("puglGetWorldString") -const char* -puglGetClassName(const PuglWorld* world) -{ - return puglGetWorldString(world, PUGL_CLASS_NAME); -} - -/** - Set the window class name before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetClassName") -void -puglInitWindowClass(PuglView* view, const char* name) -{ - puglSetWorldString(puglGetWorld(view), PUGL_CLASS_NAME, name); -} - -/** - Set the window size before creating a window. - - @deprecated Use puglSetFrame(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetFrame") -void -puglInitWindowSize(PuglView* view, int width, int height) -{ - PuglRect frame = puglGetFrame(view); - - frame.width = (PuglSpan)width; - frame.height = (PuglSpan)height; - - puglSetFrame(view, frame); -} - -/** - Set the minimum window size before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetMinSize") -void -puglInitWindowMinSize(PuglView* view, int width, int height) -{ - puglSetSizeHint(view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the window aspect ratio range before creating a window. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currently work on MacOS (the minimum is used), so only setting a fixed - aspect ratio works properly across all platforms. -*/ -static inline PUGL_DEPRECATED_BY("puglSetAspectRatio") -void -puglInitWindowAspectRatio(PuglView* view, - int minX, - int minY, - int maxX, - int maxY) -{ - puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); - puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); -} - -/** - Set transient parent before creating a window. - - On X11, parent must be a Window. - On OSX, parent must be an NSView*. -*/ -static inline PUGL_DEPRECATED_BY("puglSetTransientParent") -void -puglInitTransientFor(PuglView* view, uintptr_t parent) -{ - puglSetTransientParent(view, (PuglNativeView)parent); -} - -/** - Set transient parent before creating a window. - - @deprecated Use puglSetTransientParent(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetTransientParent") -PuglStatus -puglSetTransientFor(PuglView* view, uintptr_t parent) -{ - return puglSetTransientParent(view, (PuglNativeView)parent); -} - -/** - Enable or disable resizing before creating a window. - - @deprecated Use puglSetViewHint() with #PUGL_RESIZABLE. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglInitResizable(PuglView* view, bool resizable) -{ - puglSetViewHint(view, PUGL_RESIZABLE, resizable); -} - -/** - Get the current size of the view. - - @deprecated Use puglGetFrame(). - -*/ -static inline PUGL_DEPRECATED_BY("puglGetFrame") -void -puglGetSize(PuglView* view, int* width, int* height) -{ - const PuglRect frame = puglGetFrame(view); - - *width = (int)frame.width; - *height = (int)frame.height; -} - -/** - Ignore synthetic repeated key events. - - @deprecated Use puglSetViewHint() with #PUGL_IGNORE_KEY_REPEAT. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglIgnoreKeyRepeat(PuglView* view, bool ignore) -{ - puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, ignore); -} - -/** - Set a hint before creating a window. - - @deprecated Use puglSetWindowHint(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") -void -puglInitWindowHint(PuglView* view, PuglViewHint hint, int value) -{ - puglSetViewHint(view, hint, value); -} - -/** - Set the parent window before creating a window (for embedding). - - @deprecated Use puglSetWindowParent(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetParentWindow") -void -puglInitWindowParent(PuglView* view, PuglNativeView parent) -{ - puglSetParentWindow(view, parent); -} - -/** - Set the graphics backend to use. - - @deprecated Use puglSetBackend(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetBackend") -int -puglInitBackend(PuglView* view, const PuglBackend* backend) -{ - return (int)puglSetBackend(view, backend); -} - -/** - Set the title of the window. - - This only makes sense for non-embedded views that will have a corresponding - top-level window, and sets the title, typically displayed in the title bar - or in window switchers. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewString") -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) -{ - return puglSetViewString(view, PUGL_WINDOW_TITLE, title); -} - -/// Return the title of the window, or null -static inline PUGL_DEPRECATED_BY("puglGetViewString") -const char* -puglGetWindowTitle(const PuglView* view) -{ - return puglGetViewString(view, PUGL_WINDOW_TITLE); -} - -/** - Realize a view by creating a corresponding system view or window. - - The view should be fully configured using the above functions before this is - called. This function may only be called once per view. - - @deprecated Use puglRealize(), or just show the view. -*/ -static inline PUGL_DEPRECATED_BY("puglRealize") -PuglStatus -puglCreateWindow(PuglView* view, const char* title) -{ - puglSetViewString(view, PUGL_WINDOW_TITLE, title); - return puglRealize(view); -} - -/** - Block and wait for an event to be ready. - - This can be used in a loop to only process events via puglProcessEvents when - necessary. This function will block indefinitely if no events are - available, so is not appropriate for use in programs that need to perform - regular updates (e.g. animation). - - @deprecated Use puglPollEvents(). -*/ -PUGL_API -PUGL_DEPRECATED_BY("puglPollEvents") -PuglStatus -puglWaitForEvent(PuglView* view); - -/** - Process all pending window events. - - This handles input events as well as rendering, so it should be called - regularly and rapidly enough to keep the UI responsive. This function does - not block if no events are pending. - - @deprecated Use puglDispatchEvents(). -*/ -PUGL_API -PUGL_DEPRECATED_BY("puglDispatchEvents") -PuglStatus -puglProcessEvents(PuglView* view); - -/** - Poll for events that are ready to be processed. - - This polls for events that are ready for any view in the world, potentially - blocking depending on `timeout`. - - @param world The world to poll for events. - - @param timeout Maximum time to wait, in seconds. If zero, the call returns - immediately, if negative, the call blocks indefinitely. - - @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error. - - @deprecated Use puglUpdate(). -*/ -static inline PUGL_DEPRECATED_BY("puglUpdate") -PuglStatus -puglPollEvents(PuglWorld* world, double timeout) -{ - return puglUpdate(world, timeout); -} - -/** - Dispatch any pending events to views. - - This processes all pending events, dispatching them to the appropriate - views. View event handlers will be called in the scope of this call. This - function does not block, if no events are pending then it will return - immediately. - - @deprecated Use puglUpdate(). -*/ -static inline PUGL_DEPRECATED_BY("puglUpdate") -PuglStatus -puglDispatchEvents(PuglWorld* world) -{ - return puglUpdate(world, 0.0); -} - -static inline PUGL_DEPRECATED_BY("puglShow") -PuglStatus -puglShowWindow(PuglView* view) -{ - return puglShow(view, PUGL_SHOW_RAISE); -} - -static inline PUGL_DEPRECATED_BY("puglHide") -PuglStatus -puglHideWindow(PuglView* view) -{ - return puglHide(view); -} - -/** - Set the default size of the view. - - This should be called before puglRealize() to set the default size of the - view, which will be the initial size of the window if this is a top level - view. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetDefaultSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_DEFAULT_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the minimum size of the view. - - If an initial minimum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetMinSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_MIN_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the maximum size of the view. - - If an initial maximum size is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetMaxSize(PuglView* view, int width, int height) -{ - return puglSetSizeHint( - view, PUGL_MAX_SIZE, (PuglSpan)width, (PuglSpan)height); -} - -/** - Set the view aspect ratio range. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currently work on MacOS (the minimum is used), so only setting a fixed - aspect ratio works properly across all platforms. - - If an initial aspect ratio is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -static inline PUGL_DEPRECATED_BY("puglSetSizeHint") -PuglStatus -puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY) -{ - const PuglStatus st0 = - puglSetSizeHint(view, PUGL_MIN_ASPECT, (PuglSpan)minX, (PuglSpan)minY); - - const PuglStatus st1 = - puglSetSizeHint(view, PUGL_MAX_ASPECT, (PuglSpan)maxX, (PuglSpan)maxY); - - return st0 ? st0 : st1; -} - -/// Return the native window handle -static inline PUGL_DEPRECATED_BY("puglGetNativeView") -PuglNativeView -puglGetNativeWindow(PuglView* view) -{ - return puglGetNativeView(view); -} - -/** - Request user attention. - - This hints to the system that the window or application requires attention - from the user. The exact effect depends on the platform, but is usually - something like a flashing task bar entry or bouncing application icon. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewStyle") -PuglStatus -puglRequestAttention(PuglView* view) -{ - return puglSetViewStyle(view, - puglGetViewStyle(view) | PUGL_VIEW_STYLE_DEMANDING); -} - -# define PUGL_KEY_SHIFT PUGL_KEY_SHIFT_L - -# define PUGL_KEY_CTRL PUGL_KEY_CTRL_L - -# define PUGL_KEY_ALT PUGL_KEY_ALT_L - -# define PUGL_KEY_SUPER PUGL_KEY_SUPER_L - -#endif // PUGL_DISABLE_DEPRECATED - -/** @} @} */ diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/stub.h b/dpf/dgl/src/pugl-upstream/include/pugl/stub.h index 8bfe4d8..73f20aa 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/stub.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/stub.h @@ -4,8 +4,8 @@ #ifndef PUGL_STUB_H #define PUGL_STUB_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include 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); /** diff --git a/dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h b/dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h index a26ca8a..325c39d 100644 --- a/dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h +++ b/dpf/dgl/src/pugl-upstream/include/pugl/vulkan.h @@ -10,8 +10,8 @@ #ifndef PUGL_VULKAN_H #define PUGL_VULKAN_H -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include #include @@ -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); /** diff --git a/dpf/dgl/src/pugl-upstream/src/.clang-tidy b/dpf/dgl/src/pugl-upstream/src/.clang-tidy index 6d51f06..cfc824d 100644 --- a/dpf/dgl/src/pugl-upstream/src/.clang-tidy +++ b/dpf/dgl/src/pugl-upstream/src/.clang-tidy @@ -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, diff --git a/dpf/dgl/src/pugl-upstream/src/common.c b/dpf/dgl/src/pugl-upstream/src/common.c index 46b2f3d..700ad92 100644 --- a/dpf/dgl/src/pugl-upstream/src/common.c +++ b/dpf/dgl/src/pugl-upstream/src/common.c @@ -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 -#include #include #include #include @@ -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; } diff --git a/dpf/dgl/src/pugl-upstream/src/internal.c b/dpf/dgl/src/pugl-upstream/src/internal.c index 3c4d304..3e0a1c8 100644 --- a/dpf/dgl/src/pugl-upstream/src/internal.c +++ b/dpf/dgl/src/pugl-upstream/src/internal.c @@ -1,23 +1,84 @@ -// Copyright 2012-2023 David Robillard +// Copyright 2012-2025 David Robillard // SPDX-License-Identifier: ISC #include "internal.h" #include "types.h" -#include "pugl/pugl.h" +#include #include #include +#include #include #include +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; diff --git a/dpf/dgl/src/pugl-upstream/src/internal.h b/dpf/dgl/src/pugl-upstream/src/internal.h index 3721c10..9f7a1d7 100644 --- a/dpf/dgl/src/pugl-upstream/src/internal.h +++ b/dpf/dgl/src/pugl-upstream/src/internal.h @@ -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 +#include #include #include @@ -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); diff --git a/dpf/dgl/src/pugl-upstream/src/mac.h b/dpf/dgl/src/pugl-upstream/src/mac.h index 119e7c8..305cf30 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac.h +++ b/dpf/dgl/src/pugl-upstream/src/mac.h @@ -5,7 +5,7 @@ #ifndef PUGL_SRC_MAC_H #define PUGL_SRC_MAC_H -#include "pugl/pugl.h" +#include #import diff --git a/dpf/dgl/src/pugl-upstream/src/mac.m b/dpf/dgl/src/pugl-upstream/src/mac.m index 205e695..cfb45a9 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac.m +++ b/dpf/dgl/src/pugl-upstream/src/mac.m @@ -7,9 +7,10 @@ #include "mac.h" #include "internal.h" +#include "macros.h" #include "platform.h" -#include "pugl/pugl.h" +#include #import @@ -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* 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* diff --git a/dpf/dgl/src/pugl-upstream/src/mac_cairo.m b/dpf/dgl/src/pugl-upstream/src/mac_cairo.m index e4d2f5f..112b727 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac_cairo.m +++ b/dpf/dgl/src/pugl-upstream/src/mac_cairo.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/cairo.h" +#include #include diff --git a/dpf/dgl/src/pugl-upstream/src/mac_gl.m b/dpf/dgl/src/pugl-upstream/src/mac_gl.m index f177bf9..57dfc41 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac_gl.m +++ b/dpf/dgl/src/pugl-upstream/src/mac_gl.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/gl.h" +#include #ifndef __MAC_10_10 # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core diff --git a/dpf/dgl/src/pugl-upstream/src/mac_stub.m b/dpf/dgl/src/pugl-upstream/src/mac_stub.m index 9ebedca..c7e4512 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac_stub.m +++ b/dpf/dgl/src/pugl-upstream/src/mac_stub.m @@ -5,7 +5,7 @@ #include "mac.h" #include "stub.h" -#include "pugl/stub.h" +#include #import diff --git a/dpf/dgl/src/pugl-upstream/src/mac_vulkan.m b/dpf/dgl/src/pugl-upstream/src/mac_vulkan.m index 4fdbaf6..f3563e2 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac_vulkan.m +++ b/dpf/dgl/src/pugl-upstream/src/mac_vulkan.m @@ -8,9 +8,9 @@ #include "stub.h" #include "types.h" -#include "pugl/pugl.h" -#include "pugl/stub.h" -#include "pugl/vulkan.h" +#include +#include +#include #include #include diff --git a/dpf/dgl/src/pugl-upstream/src/platform.h b/dpf/dgl/src/pugl-upstream/src/platform.h index cfdb1b7..378c122 100644 --- a/dpf/dgl/src/pugl-upstream/src/platform.h +++ b/dpf/dgl/src/pugl-upstream/src/platform.h @@ -1,20 +1,20 @@ -// Copyright 2012-2022 David Robillard +// Copyright 2012-2025 David Robillard // 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 +#include 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 diff --git a/dpf/dgl/src/pugl-upstream/src/stub.h b/dpf/dgl/src/pugl-upstream/src/stub.h index 699bbb9..2926768 100644 --- a/dpf/dgl/src/pugl-upstream/src/stub.h +++ b/dpf/dgl/src/pugl-upstream/src/stub.h @@ -4,7 +4,8 @@ #ifndef PUGL_SRC_STUB_H #define PUGL_SRC_STUB_H -#include "pugl/pugl.h" +#include +#include #include diff --git a/dpf/dgl/src/pugl-upstream/src/types.h b/dpf/dgl/src/pugl-upstream/src/types.h index 9817474..7179193 100644 --- a/dpf/dgl/src/pugl-upstream/src/types.h +++ b/dpf/dgl/src/pugl-upstream/src/types.h @@ -6,7 +6,7 @@ #include "attributes.h" -#include "pugl/pugl.h" +#include #include #include @@ -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*); diff --git a/dpf/dgl/src/pugl-upstream/src/win.c b/dpf/dgl/src/pugl-upstream/src/win.c index 73c8583..a443d0e 100644 --- a/dpf/dgl/src/pugl-upstream/src/win.c +++ b/dpf/dgl/src/pugl-upstream/src/win.c @@ -4,9 +4,10 @@ #include "win.h" #include "internal.h" +#include "macros.h" #include "platform.h" -#include "pugl/pugl.h" +#include #include #include @@ -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; diff --git a/dpf/dgl/src/pugl-upstream/src/win.h b/dpf/dgl/src/pugl-upstream/src/win.h index 84f1d4e..b37db3f 100644 --- a/dpf/dgl/src/pugl-upstream/src/win.h +++ b/dpf/dgl/src/pugl-upstream/src/win.h @@ -6,7 +6,7 @@ #include "internal.h" -#include "pugl/pugl.h" +#include #include @@ -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 diff --git a/dpf/dgl/src/pugl-upstream/src/win_cairo.c b/dpf/dgl/src/pugl-upstream/src/win_cairo.c index 0aab254..328b6f0 100644 --- a/dpf/dgl/src/pugl-upstream/src/win_cairo.c +++ b/dpf/dgl/src/pugl-upstream/src/win_cairo.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/cairo.h" +#include #include #include @@ -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, diff --git a/dpf/dgl/src/pugl-upstream/src/win_gl.c b/dpf/dgl/src/pugl-upstream/src/win_gl.c index 01321d4..4f33e36 100644 --- a/dpf/dgl/src/pugl-upstream/src/win_gl.c +++ b/dpf/dgl/src/pugl-upstream/src/win_gl.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/gl.h" +#include #include @@ -14,30 +14,42 @@ #include #include -#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*); diff --git a/dpf/dgl/src/pugl-upstream/src/win_stub.c b/dpf/dgl/src/pugl-upstream/src/win_stub.c index e98357c..d2dc3f3 100644 --- a/dpf/dgl/src/pugl-upstream/src/win_stub.c +++ b/dpf/dgl/src/pugl-upstream/src/win_stub.c @@ -5,7 +5,7 @@ #include "types.h" #include "win.h" -#include "pugl/stub.h" +#include static PuglStatus puglWinStubConfigure(PuglView* view) diff --git a/dpf/dgl/src/pugl-upstream/src/win_vulkan.c b/dpf/dgl/src/pugl-upstream/src/win_vulkan.c index 2927447..bd3bf45 100644 --- a/dpf/dgl/src/pugl-upstream/src/win_vulkan.c +++ b/dpf/dgl/src/pugl-upstream/src/win_vulkan.c @@ -7,7 +7,7 @@ #include "types.h" #include "win.h" -#include "pugl/vulkan.h" +#include #include #include diff --git a/dpf/dgl/src/pugl-upstream/src/x11.c b/dpf/dgl/src/pugl-upstream/src/x11.c index 217838f..e134ccd 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11.c +++ b/dpf/dgl/src/pugl-upstream/src/x11.c @@ -11,7 +11,7 @@ #include "platform.h" #include "types.h" -#include "pugl/pugl.h" +#include #include #include @@ -69,13 +69,11 @@ #include #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 diff --git a/dpf/dgl/src/pugl-upstream/src/x11.h b/dpf/dgl/src/pugl-upstream/src/x11.h index bc93630..42e8baa 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11.h +++ b/dpf/dgl/src/pugl-upstream/src/x11.h @@ -7,8 +7,8 @@ #include "attributes.h" #include "types.h" -#include "pugl/attributes.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -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 diff --git a/dpf/dgl/src/pugl-upstream/src/x11_cairo.c b/dpf/dgl/src/pugl-upstream/src/x11_cairo.c index d6fbaec..bc7b133 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11_cairo.c +++ b/dpf/dgl/src/pugl-upstream/src/x11_cairo.c @@ -5,8 +5,8 @@ #include "types.h" #include "x11.h" -#include "pugl/cairo.h" -#include "pugl/pugl.h" +#include +#include #include #include @@ -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)) { diff --git a/dpf/dgl/src/pugl-upstream/src/x11_gl.c b/dpf/dgl/src/pugl-upstream/src/x11_gl.c index de22b34..c5b5302 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11_gl.c +++ b/dpf/dgl/src/pugl-upstream/src/x11_gl.c @@ -6,15 +6,14 @@ #include "types.h" #include "x11.h" -#include "pugl/gl.h" -#include "pugl/pugl.h" +#include +#include #include #include #include #include -#include #include #include @@ -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; diff --git a/dpf/dgl/src/pugl-upstream/src/x11_stub.c b/dpf/dgl/src/pugl-upstream/src/x11_stub.c index 844b4db..11143fe 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11_stub.c +++ b/dpf/dgl/src/pugl-upstream/src/x11_stub.c @@ -1,13 +1,13 @@ // Copyright 2012-2021 David Robillard // SPDX-License-Identifier: ISC -#include "pugl/stub.h" +#include #include "stub.h" #include "types.h" #include "x11.h" -#include "pugl/pugl.h" +#include const PuglBackend* puglStubBackend(void) diff --git a/dpf/dgl/src/pugl-upstream/src/x11_vulkan.c b/dpf/dgl/src/pugl-upstream/src/x11_vulkan.c index 834ac37..078e524 100644 --- a/dpf/dgl/src/pugl-upstream/src/x11_vulkan.c +++ b/dpf/dgl/src/pugl-upstream/src/x11_vulkan.c @@ -8,8 +8,8 @@ #include "types.h" #include "x11.h" -#include "pugl/pugl.h" -#include "pugl/vulkan.h" +#include +#include #include #include diff --git a/dpf/dgl/src/pugl.cpp b/dpf/dgl/src/pugl.cpp index a2585c7..fda302d 100644 --- a/dpf/dgl/src/pugl.cpp +++ b/dpf/dgl/src/pugl.cpp @@ -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(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(width); - view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast(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(width); - view->lastConfigure.height = static_cast(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(width), static_cast(height)); - #else - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, static_cast(width), static_cast(height), 0.0, 0.0, 1.0); - glViewport(0, 0, static_cast(width), static_cast(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(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(&windowTypes), + numWindowTypes); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/dgl/src/pugl.hpp b/dpf/dgl/src/pugl.hpp index 76a266f..41dc36a 100644 --- a/dpf/dgl/src/pugl.hpp +++ b/dpf/dgl/src/pugl.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2023 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 diff --git a/dpf/distrho/DistrhoInfo.hpp b/dpf/distrho/DistrhoInfo.hpp index 5016f31..334aed0 100644 --- a/dpf/distrho/DistrhoInfo.hpp +++ b/dpf/distrho/DistrhoInfo.hpp @@ -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" + /** @} */ /* ------------------------------------------------------------------------------------------------------------ diff --git a/dpf/distrho/DistrhoPluginMain.cpp b/dpf/distrho/DistrhoPluginMain.cpp index 254da24..d5650b0 100644 --- a/dpf/distrho/DistrhoPluginMain.cpp +++ b/dpf/distrho/DistrhoPluginMain.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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(); } diff --git a/dpf/distrho/DistrhoPluginUtils.hpp b/dpf/distrho/DistrhoPluginUtils.hpp index aea47cc..c3fa3ce 100644 --- a/dpf/distrho/DistrhoPluginUtils.hpp +++ b/dpf/distrho/DistrhoPluginUtils.hpp @@ -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.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; diff --git a/dpf/distrho/DistrhoUI.hpp b/dpf/distrho/DistrhoUI.hpp index ca41257..2401197 100644 --- a/dpf/distrho/DistrhoUI.hpp +++ b/dpf/distrho/DistrhoUI.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 */ diff --git a/dpf/distrho/DistrhoUIMain.cpp b/dpf/distrho/DistrhoUIMain.cpp index 194e984..9a80087 100644 --- a/dpf/distrho/DistrhoUIMain.cpp +++ b/dpf/distrho/DistrhoUIMain.cpp @@ -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 diff --git a/dpf/distrho/extra/RingBuffer.hpp b/dpf/distrho/extra/RingBuffer.hpp index f23104d..f0f2061 100644 --- a/dpf/distrho/extra/RingBuffer.hpp +++ b/dpf/distrho/extra/RingBuffer.hpp @@ -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; diff --git a/dpf/distrho/extra/ScopedPointer.hpp b/dpf/distrho/extra/ScopedPointer.hpp index 6349199..5e04fb1 100644 --- a/dpf/distrho/extra/ScopedPointer.hpp +++ b/dpf/distrho/extra/ScopedPointer.hpp @@ -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. diff --git a/dpf/distrho/extra/String.hpp b/dpf/distrho/extra/String.hpp index 8a34e98..7364662 100644 --- a/dpf/distrho/extra/String.hpp +++ b/dpf/distrho/extra/String.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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(ret); + return static_cast(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(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(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(dataSize/3)), 65536U); - #else - constexpr std::size_t kTmpBufSize = 65536U; - #endif + const size_t strBufSize = std::min(d_nextPowerOf2(static_cast(dataSize/3)), 65536U); + char* strBuf = static_cast(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(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> 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(std::realloc(fBuffer, fBufferLen + strBufLen + 1)); + char* const newBuf = static_cast(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(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(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(malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); diff --git a/dpf/distrho/src/DistrhoPluginCLAP.cpp b/dpf/distrho/src/DistrhoPluginCLAP.cpp index 68fb9e4..5939092 100644 --- a/dpf/distrho/src/DistrhoPluginCLAP.cpp +++ b/dpf/distrho/src/DistrhoPluginCLAP.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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); diff --git a/dpf/distrho/src/DistrhoPluginJACK.cpp b/dpf/distrho/src/DistrhoPluginJACK.cpp index 8b8d295..0539807 100644 --- a/dpf/distrho/src/DistrhoPluginJACK.cpp +++ b/dpf/distrho/src/DistrhoPluginJACK.cpp @@ -552,7 +552,7 @@ protected: midiData[1] = note; midiData[2] = velocity; fNotesRingBuffer.writeCustomData(midiData, 3); - fNotesRingBuffer.commitWrite(); + fNotesRingBuffer.commitWrite("PluginJack::sendNote"); } # endif diff --git a/dpf/distrho/src/DistrhoPluginLV2export.cpp b/dpf/distrho/src/DistrhoPluginLV2export.cpp index 725022a..dd5cc92 100644 --- a/dpf/distrho/src/DistrhoPluginLV2export.cpp +++ b/dpf/distrho/src/DistrhoPluginLV2export.cpp @@ -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: .\n"; pluginString += "@prefix doap: .\n"; pluginString += "@prefix foaf: .\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"; diff --git a/dpf/distrho/src/DistrhoPluginMAPI.cpp b/dpf/distrho/src/DistrhoPluginMAPI.cpp new file mode 100644 index 0000000..9de5088 --- /dev/null +++ b/dpf/distrho/src/DistrhoPluginMAPI.cpp @@ -0,0 +1,158 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2025 Filipe Coelho + * + * 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(ins), outs, frames, nullptr, 0); + #else + fPlugin.run(const_cast(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(handle)->process(ins, outs, frames); +} + +MAPI_EXPORT +void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value) +{ + static_cast(handle)->setParameter(index, value); +} + +MAPI_EXPORT +void mapi_set_state(mapi_handle_t handle, const char* key, const char* value) +{ + static_cast(handle)->setState(key, value); +} + +MAPI_EXPORT +void mapi_destroy(mapi_handle_t handle) +{ + delete static_cast(handle); +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/dpf/distrho/src/DistrhoPluginVST2.cpp b/dpf/distrho/src/DistrhoPluginVST2.cpp index 686341a..4f3c4b5 100644 --- a/dpf/distrho/src/DistrhoPluginVST2.cpp +++ b/dpf/distrho/src/DistrhoPluginVST2.cpp @@ -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"); diff --git a/dpf/distrho/src/DistrhoUI.cpp b/dpf/distrho/src/DistrhoUI.cpp index 0241b59..09b4a4d 100644 --- a/dpf/distrho/src/DistrhoUI.cpp +++ b/dpf/distrho/src/DistrhoUI.cpp @@ -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*) diff --git a/dpf/distrho/src/DistrhoUIInternal.hpp b/dpf/distrho/src/DistrhoUIInternal.hpp index ea3f56a..f7b578e 100644 --- a/dpf/distrho/src/DistrhoUIInternal.hpp +++ b/dpf/distrho/src/DistrhoUIInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2024 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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; diff --git a/dpf/distrho/src/DistrhoUILV2.cpp b/dpf/distrho/src/DistrhoUILV2.cpp index d7b0af6..7f5e414 100644 --- a/dpf/distrho/src/DistrhoUILV2.cpp +++ b/dpf/distrho/src/DistrhoUILV2.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2025 Filipe Coelho * * 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 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 -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoUIPrivateData.hpp b/dpf/distrho/src/DistrhoUIPrivateData.hpp index 416f084..7a1b436 100644 --- a/dpf/distrho/src/DistrhoUIPrivateData.hpp +++ b/dpf/distrho/src/DistrhoUIPrivateData.hpp @@ -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 diff --git a/dpf/distrho/src/DistrhoUIVST3.cpp b/dpf/distrho/src/DistrhoUIVST3.cpp index a326c4b..744551c 100644 --- a/dpf/distrho/src/DistrhoUIVST3.cpp +++ b/dpf/distrho/src/DistrhoUIVST3.cpp @@ -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(); diff --git a/dpf/distrho/src/DistrhoUtils.cpp b/dpf/distrho/src/DistrhoUtils.cpp index 37083c9..f029cac 100644 --- a/dpf/distrho/src/DistrhoUtils.cpp +++ b/dpf/distrho/src/DistrhoUtils.cpp @@ -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 diff --git a/dpf/distrho/src/jackbridge/JackBridge.cpp b/dpf/distrho/src/jackbridge/JackBridge.cpp index ba5ecb1..c4c44b5 100644 --- a/dpf/distrho/src/jackbridge/JackBridge.cpp +++ b/dpf/distrho/src/jackbridge/JackBridge.cpp @@ -1,6 +1,6 @@ /* * JackBridge for DPF - * Copyright (C) 2013-2024 Filipe Coelho + * Copyright (C) 2013-2025 Filipe Coelho * * 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(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)) diff --git a/dpf/distrho/src/jackbridge/NativeBridge.hpp b/dpf/distrho/src/jackbridge/NativeBridge.hpp index 33793e8..c1fc610 100644 --- a/dpf/distrho/src/jackbridge/NativeBridge.hpp +++ b/dpf/distrho/src/jackbridge/NativeBridge.hpp @@ -1,6 +1,6 @@ /* * Native Bridge for DPF - * Copyright (C) 2021-2023 Filipe Coelho + * Copyright (C) 2021-2025 Filipe Coelho * * 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 + * Copyright (C) 2021-2025 Filipe Coelho * * 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::iterator it = self->midiOuts.begin(), end = self->midiOuts.end(); it != end; ++it) + { + static_cast(*it).sendMessage(data + 1, data[0]); + } + } + + self->midiOutBuffer.flush(); + } + #endif + return 0; } @@ -403,13 +425,15 @@ struct RtAudioBridge : NativeBridge { RtAudioBridge* const self = static_cast(userData); + const RecursiveMutexLocker rml(self->midiInLock); + self->midiInBufferPending.writeByte(static_cast(len)); // TODO timestamp // self->midiInBufferPending.writeDouble(timestamp); self->midiInBufferPending.writeCustomData(message->data(), len); - for (uint8_t i=len; imidiInBufferPending.writeByte(0); - self->midiInBufferPending.commitWrite(); + self->midiInBufferPending.commitWrite("RtMidiCallback"); } #endif }; diff --git a/dpf/distrho/src/jackbridge/WebBridge.hpp b/dpf/distrho/src/jackbridge/WebBridge.hpp index afb2c4b..be96483 100644 --- a/dpf/distrho/src/jackbridge/WebBridge.hpp +++ b/dpf/distrho/src/jackbridge/WebBridge.hpp @@ -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 } diff --git a/dpf/distrho/src/mapi/mapi.h b/dpf/distrho/src/mapi/mapi.h new file mode 100644 index 0000000..88c3aa8 --- /dev/null +++ b/dpf/distrho/src/mapi/mapi.h @@ -0,0 +1,73 @@ +// Copyright 2025 Filipe Coelho +// 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 diff --git a/dpf/utils/emscripten.html.in b/dpf/utils/emscripten.html.in new file mode 100644 index 0000000..2e42206 --- /dev/null +++ b/dpf/utils/emscripten.html.in @@ -0,0 +1,203 @@ + + + + + + + + + @NAME@ + + + +
+
+
@NAME@
+
+
+
Downloading...
+
+ +
+
+ + +
+ + + + diff --git a/dpf/utils/symbols/mapi.def b/dpf/utils/symbols/mapi.def new file mode 100644 index 0000000..d764067 --- /dev/null +++ b/dpf/utils/symbols/mapi.def @@ -0,0 +1,6 @@ +EXPORTS +mapi_create +mapi_process +mapi_set_parameter +mapi_set_state +mapi_destroy diff --git a/dpf/utils/symbols/mapi.exp b/dpf/utils/symbols/mapi.exp new file mode 100644 index 0000000..3298399 --- /dev/null +++ b/dpf/utils/symbols/mapi.exp @@ -0,0 +1,5 @@ +_mapi_create +_mapi_process +_mapi_set_parameter +_mapi_set_state +_mapi_destroy diff --git a/dpf/utils/symbols/mapi.version b/dpf/utils/symbols/mapi.version new file mode 100644 index 0000000..740aa0c --- /dev/null +++ b/dpf/utils/symbols/mapi.version @@ -0,0 +1,4 @@ +{ + global: mapi_create; mapi_process; mapi_set_parameter; mapi_set_state; mapi_destroy; + local: *; +}; diff --git a/dpf/utils/symbols/shared.def b/dpf/utils/symbols/shared.def deleted file mode 100644 index 1413cae..0000000 --- a/dpf/utils/symbols/shared.def +++ /dev/null @@ -1,2 +0,0 @@ -EXPORTS -createSharedPlugin diff --git a/dpf/utils/symbols/shared.exp b/dpf/utils/symbols/shared.exp deleted file mode 100644 index 80f6251..0000000 --- a/dpf/utils/symbols/shared.exp +++ /dev/null @@ -1 +0,0 @@ -_createSharedPlugin diff --git a/dpf/utils/symbols/shared.version b/dpf/utils/symbols/shared.version deleted file mode 100644 index e60a663..0000000 --- a/dpf/utils/symbols/shared.version +++ /dev/null @@ -1,4 +0,0 @@ -{ - global: createSharedPlugin; - local: *; -};