| @@ -4,7 +4,7 @@ | |||
| # | |||
| # SPDX-License-Identifier: ISC | |||
| cmake_minimum_required(VERSION 3.8) | |||
| cmake_minimum_required(VERSION 3.8...3.31) | |||
| project(DPF) | |||
| @@ -30,7 +30,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") | |||
| include(DPF-plugin) | |||
| if(DPF_LIBRARIES) | |||
| find_package(PkgConfig) | |||
| find_package(PkgConfig QUIET) | |||
| if(PKG_CONFIG_FOUND) | |||
| pkg_check_modules(CAIRO "cairo") | |||
| if(CAIRO_FOUND AND (NOT HAIKU)) | |||
| @@ -42,7 +42,7 @@ if(DPF_LIBRARIES) | |||
| endif() | |||
| if(DPF_EXAMPLES) | |||
| find_package(PkgConfig) | |||
| find_package(PkgConfig QUIET) | |||
| if(PKG_CONFIG_FOUND) | |||
| pkg_check_modules(CAIRO "cairo") | |||
| if(CAIRO_FOUND AND (NOT HAIKU)) | |||
| @@ -1,4 +1,4 @@ | |||
| Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| @@ -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 | |||
| @@ -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 | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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(). | |||
| @@ -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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -165,10 +165,9 @@ private: | |||
| Widget display function. | |||
| Implemented internally to pass context into the drawing function. | |||
| */ | |||
| void onDisplay() override | |||
| void onDisplay() final | |||
| { | |||
| const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext()); | |||
| onCairoDisplay(context); | |||
| onCairoDisplay(static_cast<const CairoGraphicsContext&>(BaseWidget::getGraphicsContext())); | |||
| } | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget); | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -115,6 +115,17 @@ struct Color { | |||
| */ | |||
| static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept; | |||
| /** | |||
| Create a color from a RGB unsigned integer. | |||
| Basically doing: | |||
| ``` | |||
| uint8_t red = (color >> 24) & 0xff; | |||
| uint8_t green = (color >> 16) & 0xff; | |||
| uint8_t blue = (color >> 8) & 0xff; | |||
| ``` | |||
| */ | |||
| static Color fromRGB(uint color, float alpha = 1.0f) noexcept; | |||
| /** | |||
| Linearly interpolate this color against another. | |||
| */ | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -360,25 +360,23 @@ public: | |||
| */ | |||
| bool isNotNull() const noexcept; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| /** | |||
| Draw this line using the provided graphics context, optionally specifying line width. | |||
| */ | |||
| void draw(const GraphicsContext& context, T width = 1); | |||
| #endif | |||
| Line<T>& operator=(const Line<T>& line) noexcept; | |||
| bool operator==(const Line<T>& line) const noexcept; | |||
| bool operator!=(const Line<T>& line) const noexcept; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this line using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)") | |||
| void draw(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| Point<T> posStart, posEnd; | |||
| @@ -489,7 +487,7 @@ public: | |||
| bool operator==(const Circle<T>& cir) const noexcept; | |||
| bool operator!=(const Circle<T>& cir) const noexcept; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this circle using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -503,7 +501,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| Point<T> fPos; | |||
| @@ -582,7 +580,7 @@ public: | |||
| bool operator==(const Triangle<T>& tri) const noexcept; | |||
| bool operator!=(const Triangle<T>& tri) const noexcept; | |||
| #ifndef DPF_TEST_POINT_CPP | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this triangle using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -596,7 +594,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| #endif | |||
| private: | |||
| Point<T> pos1, pos2, pos3; | |||
| @@ -813,6 +811,7 @@ public: | |||
| bool operator==(const Rectangle<T>& size) const noexcept; | |||
| bool operator!=(const Rectangle<T>& size) const noexcept; | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Draw this rectangle using the current OpenGL state.@n | |||
| DEPRECATED Please use draw(const GraphicsContext&) instead. | |||
| @@ -826,6 +825,7 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)") | |||
| void drawOutline(); | |||
| #endif | |||
| private: | |||
| Point<T> pos; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -17,17 +17,21 @@ | |||
| #ifndef DGL_IMAGE_HPP_INCLUDED | |||
| #define DGL_IMAGE_HPP_INCLUDED | |||
| #ifdef DGL_CAIRO | |||
| #include "Cairo.hpp" | |||
| #if defined(DGL_CAIRO) | |||
| # include "Cairo.hpp" | |||
| #elif defined(DGL_OPENGL) | |||
| # include "OpenGL.hpp" | |||
| #elif defined(DGL_VULKAN) | |||
| # include "Vulkan.hpp" | |||
| #else | |||
| #include "OpenGL.hpp" | |||
| # include "Base.hpp" | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| #ifdef DGL_CAIRO | |||
| #if defined(DGL_CAIRO) | |||
| typedef CairoImage Image; | |||
| #else | |||
| #elif defined(DGL_OPENGL) | |||
| typedef OpenGLImage Image; | |||
| #endif | |||
| @@ -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) | |||
| @@ -53,6 +53,12 @@ | |||
| #ifdef DISTRHO_OS_MAC | |||
| # ifdef DGL_USE_OPENGL3 | |||
| // NOTE GLES with macOS is not supported | |||
| # ifdef DGL_USE_GLES | |||
| # undef DGL_USE_GLES | |||
| # undef DGL_USE_GLES2 | |||
| # undef DGL_USE_GLES3 | |||
| # endif | |||
| # include <OpenGL/gl3.h> | |||
| # include <OpenGL/gl3ext.h> | |||
| # else | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -24,34 +24,131 @@ | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifdef DGL_USE_OPENGL3 | |||
| /** | |||
| OpenGL Graphics context. | |||
| OpenGL3 Graphics context. | |||
| This provides access to the program, shaders and uniforms used by the underlying DPF implementation. | |||
| */ | |||
| struct OpenGLGraphicsContext : GraphicsContext | |||
| struct OpenGL3GraphicsContext : GraphicsContext | |||
| { | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #endif | |||
| /** | |||
| The OpenGL3 program used for this context. | |||
| It is activated automatically before any widget onDisplay() is called. | |||
| If changing the current OpenGL program make sure to revert back to this one at the end of your pipeline. | |||
| @code | |||
| // use custom program | |||
| glUseProgram(context.program); | |||
| // custom stuff here | |||
| // revert back | |||
| glUseProgram(context.program); | |||
| @endcode | |||
| */ | |||
| GLuint program; | |||
| /** | |||
| A vec4 uniform used to set the next drawing color. | |||
| @code | |||
| const GLfloat color[4] = { red, green, blue, alpha }; | |||
| glUniform4fv(context.color, 1, color); | |||
| @endcode | |||
| */ | |||
| GLuint color; | |||
| /** | |||
| A vertex shader attribute directly linked to gl_Position. | |||
| Use this to set the bounds for drawing, normalized as -1.0 to +1.0. | |||
| The @a width and @a height provide the total window size for convenience. | |||
| @code | |||
| const GLfloat triangle[] = { x1, y1, x2, y2, x3, y3 }; | |||
| glEnableVertexAttribArray(context.bounds); | |||
| glVertexAttribPointer(context.bounds, 2, GL_FLOAT, GL_FALSE, 0, triangle); | |||
| @endcode | |||
| */ | |||
| GLuint bounds; | |||
| /** | |||
| A vertex shader attribute directly linked to GL_TEXTURE0 map. | |||
| // TODO find the correct wording, map??. | |||
| @code | |||
| const GLfloat map[] = { 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f }; | |||
| glEnableVertexAttribArray(context.textureMap); | |||
| glVertexAttribPointer(context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, map); | |||
| @endcode | |||
| */ | |||
| GLuint textureMap; | |||
| /** | |||
| A boolean uniform used to indicate if next drawing should @a texture or @a color. | |||
| Set to 0 for color mode, 1 for texture. | |||
| Default mode is color, if changed make sure to revert to color mode at the end of your pipeline. | |||
| @code | |||
| // setup for drawing based on texture | |||
| glUniform1i(context.usingTexture, 1); | |||
| // bind texture | |||
| glBindTexture(GL_TEXTURE_2D, myTextureId); | |||
| // etc.. | |||
| // glDrawElements or similar | |||
| // unbind texture | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| // etc.. | |||
| // revert to color mode | |||
| glUniform1i(context.usingTexture, 0); | |||
| @endcode | |||
| */ | |||
| GLuint usingTexture; | |||
| /** | |||
| Set of buffers created with glGenBuffers. | |||
| Used internally in DPF to draw generic shapes, can be reused in custom code. | |||
| Unbound by default, make sure to leave them unbound at the end of your pipeline. | |||
| */ | |||
| GLuint buffers[2]; | |||
| /** | |||
| Total width of the window used for this context. | |||
| */ | |||
| uint width; | |||
| /** | |||
| Total height of the window used for this context. | |||
| */ | |||
| uint height; | |||
| }; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static inline | |||
| ImageFormat asDISTRHOImageFormat(const GLenum format) | |||
| { | |||
| switch (format) | |||
| { | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||
| case GL_RED: | |||
| #else | |||
| #else | |||
| case GL_LUMINANCE: | |||
| #endif | |||
| #endif | |||
| return kImageFormatGrayscale; | |||
| #ifndef DGL_USE_GLES | |||
| case GL_BGR: | |||
| return kImageFormatBGR; | |||
| case GL_BGRA: | |||
| return kImageFormatBGRA; | |||
| #endif | |||
| case GL_RGB: | |||
| return kImageFormatRGB; | |||
| case GL_RGBA: | |||
| @@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format) | |||
| case kImageFormatNull: | |||
| break; | |||
| case kImageFormatGrayscale: | |||
| #ifdef DGL_USE_OPENGL3 | |||
| #if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2) | |||
| return GL_RED; | |||
| #else | |||
| #else | |||
| return GL_LUMINANCE; | |||
| #endif | |||
| #endif | |||
| case kImageFormatBGR: | |||
| #ifndef DGL_USE_GLES | |||
| return GL_BGR; | |||
| #else | |||
| return 0; | |||
| #endif | |||
| case kImageFormatBGRA: | |||
| #ifndef DGL_USE_GLES | |||
| return GL_BGRA; | |||
| #else | |||
| return 0; | |||
| #endif | |||
| case kImageFormatRGB: | |||
| return GL_RGB; | |||
| case kImageFormatRGBA: | |||
| return GL_RGBA; | |||
| } | |||
| return 0x0; | |||
| return 0; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| OpenGL Image class. | |||
| @@ -144,6 +249,18 @@ public: | |||
| */ | |||
| void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | |||
| #ifdef DGL_USE_GLES | |||
| /** | |||
| Get the image format. | |||
| */ | |||
| ImageFormat getFormat() const noexcept; | |||
| /** | |||
| Get the raw image data. | |||
| */ | |||
| const char* getRawData() const noexcept; | |||
| #endif | |||
| /** | |||
| TODO document this. | |||
| */ | |||
| @@ -157,6 +274,7 @@ public: | |||
| inline void drawAt(const GraphicsContext& context, int x, int y) | |||
| { drawAt(context, Point<int>(x, y)); } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** | |||
| Constructor using raw image data, specifying an OpenGL image format. | |||
| @note @a rawData must remain valid for the lifetime of this Image. | |||
| @@ -200,14 +318,19 @@ public: | |||
| */ | |||
| DISTRHO_DEPRECATED | |||
| GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; } | |||
| #endif // DGL_ALLOW_DEPRECATED_METHODS | |||
| private: | |||
| bool setupCalled; | |||
| bool textureInit; | |||
| GLuint textureId; | |||
| #ifdef DGL_USE_GLES | |||
| mutable char* convertedData; | |||
| mutable const char* rawDataLast; | |||
| #endif | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow; | |||
| typedef ImageBaseButton<OpenGLImage> OpenGLImageButton; | |||
| @@ -215,7 +338,7 @@ typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob; | |||
| typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider; | |||
| typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -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; | |||
| @@ -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,) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -53,13 +53,14 @@ const char* Application::getClassName() const noexcept | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| Application::PrivateData::PrivateData(const bool standalone) | |||
| Application::PrivateData::PrivateData(const bool standalone, const Type type) | |||
| : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||
| standalone ? PUGL_WORLD_THREADS : 0x0)), | |||
| (standalone ? PUGL_WORLD_THREADS : 0))), | |||
| isModern(false), | |||
| isStandalone(standalone), | |||
| isStarting(true), | |||
| isQuitting(false), | |||
| isQuittingInNextCycle(false), | |||
| isStarting(true), | |||
| needsRepaint(false), | |||
| visibleWindows(0), | |||
| mainThreadHandle(getCurrentThreadHandle()), | |||
| @@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | |||
| #ifdef DGL_USING_SDL | |||
| SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO); | |||
| #else | |||
| puglSetWorldHandle(world, this); | |||
| #ifdef __EMSCRIPTEN__ | |||
| puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | |||
| #else | |||
| puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | |||
| #endif | |||
| #endif | |||
| } | |||
| Application::PrivateData::~PrivateData() | |||
| @@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData() | |||
| windows.clear(); | |||
| idleCallbacks.clear(); | |||
| #ifdef DGL_USING_SDL | |||
| SDL_Quit(); | |||
| #else | |||
| if (world != nullptr) | |||
| puglFreeWorld(world); | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -173,13 +165,11 @@ void Application::PrivateData::quit() | |||
| isQuitting = true; | |||
| #ifndef DPF_TEST_APPLICATION_CPP | |||
| for (WindowListReverseIterator rit = windows.rbegin(), rite = windows.rend(); rit != rite; ++rit) | |||
| { | |||
| DGL_NAMESPACE::Window* const window(*rit); | |||
| window->close(); | |||
| } | |||
| #endif | |||
| } | |||
| double Application::PrivateData::getTime() const | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -51,18 +51,21 @@ struct Application::PrivateData { | |||
| /** Pugl world instance. */ | |||
| PuglWorld* const world; | |||
| /** Whether the applicating uses modern backend, otherwise classic. */ | |||
| const bool isModern; | |||
| /** Whether the application is running as standalone, otherwise it is part of a plugin. */ | |||
| const bool isStandalone; | |||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||
| bool isStarting; | |||
| /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | |||
| bool isQuitting; | |||
| /** Helper for safely close everything from main thread. */ | |||
| bool isQuittingInNextCycle; | |||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||
| bool isStarting; | |||
| /** When true force all windows to be repainted on next idle. */ | |||
| bool needsRepaint; | |||
| @@ -80,7 +83,7 @@ struct Application::PrivateData { | |||
| std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | |||
| /** Constructor and destructor */ | |||
| explicit PrivateData(bool standalone); | |||
| explicit PrivateData(bool standalone, Type type); | |||
| ~PrivateData(); | |||
| /** Flag one window as shown, which increments @a visibleWindows. | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| @@ -32,21 +32,43 @@ | |||
| // templated classes | |||
| #include "ImageBaseWidgets.cpp" | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Check for correct build config | |||
| #ifndef DGL_CAIRO | |||
| # error Build config error, Cairo was NOT requested while building Cairo code | |||
| #endif | |||
| #ifdef DGL_OPENGL | |||
| # error Build config error, OpenGL requested while building Cairo code | |||
| #endif | |||
| #ifdef DGL_VULKAN | |||
| # error Build config error, Vulkan requested while building Cairo code | |||
| #endif | |||
| #ifdef DGL_USE_GLES2 | |||
| # error Build config error, GLESv2 requested while building Cairo code | |||
| #endif | |||
| #ifdef DGL_USE_GLES3 | |||
| # error Build config error, GLESv3 requested while building Cairo code | |||
| #endif | |||
| #ifdef DGL_USE_OPENGL3 | |||
| # error Build config error, OpenGL3 requested while building Cairo code | |||
| #endif | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static void notImplemented(const char* const name) | |||
| { | |||
| d_stderr2("cairo function not implemented: %s", name); | |||
| d_stderr2("Cairo function not implemented: %s", name); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Color | |||
| void Color::setFor(const GraphicsContext& context, const bool includeAlpha) | |||
| { | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| if (includeAlpha) | |||
| cairo_set_source_rgba(handle, red, green, blue, alpha); | |||
| @@ -54,7 +76,7 @@ void Color::setFor(const GraphicsContext& context, const bool includeAlpha) | |||
| cairo_set_source_rgb(handle, red, green, blue); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Line | |||
| template<typename T> | |||
| @@ -63,7 +85,7 @@ void Line<T>::draw(const GraphicsContext& context, const T width) | |||
| DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| cairo_set_line_width(handle, width); | |||
| cairo_move_to(handle, posStart.getX(), posStart.getY()); | |||
| @@ -71,18 +93,13 @@ void Line<T>::draw(const GraphicsContext& context, const T width) | |||
| cairo_stroke(handle); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Line<T>::draw() | |||
| { | |||
| notImplemented("Line::draw"); | |||
| } | |||
| template class Line<double>; | |||
| template class Line<float>; | |||
| template class Line<int>; | |||
| template class Line<uint>; | |||
| template class Line<short>; | |||
| template class Line<ushort>; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Circle | |||
| @@ -129,7 +146,7 @@ static void drawCircle(cairo_t* const handle, | |||
| template<typename T> | |||
| void Circle<T>::draw(const GraphicsContext& context) | |||
| { | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false); | |||
| } | |||
| @@ -139,12 +156,13 @@ void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| cairo_set_line_width(handle, lineWidth); | |||
| drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Circle<T>::draw() | |||
| { | |||
| @@ -156,13 +174,7 @@ void Circle<T>::drawOutline() | |||
| { | |||
| notImplemented("Circle::drawOutline"); | |||
| } | |||
| template class Circle<double>; | |||
| template class Circle<float>; | |||
| template class Circle<int>; | |||
| template class Circle<uint>; | |||
| template class Circle<short>; | |||
| template class Circle<ushort>; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Triangle | |||
| @@ -190,7 +202,7 @@ static void drawTriangle(cairo_t* const handle, | |||
| template<typename T> | |||
| void Triangle<T>::draw(const GraphicsContext& context) | |||
| { | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| drawTriangle<T>(handle, pos1, pos2, pos3, false); | |||
| } | |||
| @@ -200,12 +212,13 @@ void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| cairo_set_line_width(handle, lineWidth); | |||
| drawTriangle<T>(handle, pos1, pos2, pos3, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Triangle<T>::draw() | |||
| { | |||
| @@ -217,13 +230,7 @@ void Triangle<T>::drawOutline() | |||
| { | |||
| notImplemented("Triangle::drawOutline"); | |||
| } | |||
| template class Triangle<double>; | |||
| template class Triangle<float>; | |||
| template class Triangle<int>; | |||
| template class Triangle<uint>; | |||
| template class Triangle<short>; | |||
| template class Triangle<ushort>; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Rectangle | |||
| @@ -244,7 +251,7 @@ void Rectangle<T>::draw(const GraphicsContext& context) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| drawRectangle(handle, *this, false); | |||
| } | |||
| @@ -255,12 +262,13 @@ void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth | |||
| DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| cairo_set_line_width(handle, lineWidth); | |||
| drawRectangle(handle, *this, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Rectangle<T>::draw() | |||
| { | |||
| @@ -272,13 +280,7 @@ void Rectangle<T>::drawOutline() | |||
| { | |||
| notImplemented("Rectangle::drawOutline"); | |||
| } | |||
| template class Rectangle<double>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<uint>; | |||
| template class Rectangle<short>; | |||
| template class Rectangle<ushort>; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // CairoImage | |||
| @@ -516,7 +518,7 @@ void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noex | |||
| if (datarefcount != nullptr && --(*datarefcount) == 0) | |||
| std::free(surfacedata); | |||
| else | |||
| datarefcount = (int*)malloc(sizeof(*datarefcount)); | |||
| datarefcount = static_cast<int*>(malloc(sizeof(*datarefcount))); | |||
| surface = newsurface; | |||
| surfacedata = nullptr; // cairo_image_surface_get_data(newsurface); | |||
| @@ -531,7 +533,7 @@ void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); | |||
| cairo_paint(handle); | |||
| @@ -623,7 +625,7 @@ void ImageBaseKnob<CairoImage>::PrivateData::init() | |||
| template <> | |||
| void ImageBaseKnob<CairoImage>::PrivateData::cleanup() | |||
| { | |||
| cairo_surface_destroy((cairo_surface_t*)cairoSurface); | |||
| cairo_surface_destroy(static_cast<cairo_surface_t*>(cairoSurface)); | |||
| cairoSurface = nullptr; | |||
| } | |||
| @@ -672,10 +674,10 @@ template <> | |||
| void ImageBaseKnob<CairoImage>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
| cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle; | |||
| const double normValue = getNormalizedValue(); | |||
| cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | |||
| cairo_surface_t* surface = static_cast<cairo_surface_t*>(pData->cairoSurface); | |||
| if (! pData->isReady) | |||
| { | |||
| @@ -826,15 +828,57 @@ void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, u | |||
| notImplemented("Window::PrivateData::renderToPicture"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| void Window::PrivateData::createContextIfNeeded() | |||
| { | |||
| GraphicsContext& context((GraphicsContext&)graphicsContext); | |||
| ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); | |||
| return context; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| void Window::PrivateData::destroyContext() | |||
| { | |||
| } | |||
| void Window::PrivateData::startContext() | |||
| { | |||
| reinterpret_cast<CairoGraphicsContext&>(graphicsContext).handle = static_cast<cairo_t*>(puglGetContext(view)); | |||
| } | |||
| void Window::PrivateData::endContext() | |||
| { | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifndef DGL_GEOMETRY_CPP_INCLUDED | |||
| template class Line<double>; | |||
| template class Line<float>; | |||
| template class Line<int>; | |||
| template class Line<uint>; | |||
| template class Line<short>; | |||
| template class Line<ushort>; | |||
| template class Circle<double>; | |||
| template class Circle<float>; | |||
| template class Circle<int>; | |||
| template class Circle<uint>; | |||
| template class Circle<short>; | |||
| template class Circle<ushort>; | |||
| template class Triangle<double>; | |||
| template class Triangle<float>; | |||
| template class Triangle<int>; | |||
| template class Triangle<uint>; | |||
| template class Triangle<short>; | |||
| template class Triangle<ushort>; | |||
| template class Rectangle<double>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<uint>; | |||
| template class Rectangle<short>; | |||
| template class Rectangle<ushort>; | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -242,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept | |||
| return Color(r, g, b, alpha); | |||
| } | |||
| Color Color::fromRGB(const uint color, const float alpha) noexcept | |||
| { | |||
| return Color(static_cast<int>(color >> 24) & 0xff, | |||
| static_cast<int>(color >> 16) & 0xff, | |||
| static_cast<int>(color >> 8) & 0xff, | |||
| alpha); | |||
| } | |||
| void Color::interpolate(const Color& other, float u) noexcept | |||
| { | |||
| fixRange(u); | |||
| @@ -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 | |||
| @@ -23,6 +23,8 @@ | |||
| #include "../Geometry.hpp" | |||
| #define DGL_GEOMETRY_CPP_INCLUDED | |||
| #include <cmath> | |||
| START_NAMESPACE_DGL | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) | |||
| //#define STB_IMAGE_STATIC | |||
| #if defined(DGL_USE_GLES2) | |||
| # define NANOVG_GLES2_IMPLEMENTATION | |||
| #elif defined(DGL_USE_GLES3) | |||
| # define NANOVG_GLES3_IMPLEMENTATION | |||
| #elif defined(DGL_USE_OPENGL3) | |||
| # define NANOVG_GL3_IMPLEMENTATION | |||
| #else | |||
| @@ -1142,6 +1144,9 @@ inline void NanoBaseWidget<SubWidget>::onDisplay() | |||
| onNanoDisplay(); | |||
| displayChildren(); | |||
| NanoVG::endFrame(); | |||
| #ifdef DGL_USE_OPENGL3 | |||
| glUseProgram(reinterpret_cast<const OpenGL3GraphicsContext&>(getGraphicsContext()).program); | |||
| #endif | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -20,7 +20,6 @@ | |||
| #endif | |||
| #include "../OpenGL.hpp" | |||
| #include "../Color.hpp" | |||
| #include "../ImageWidgets.hpp" | |||
| #include "SubWidgetPrivateData.hpp" | |||
| @@ -28,416 +27,20 @@ | |||
| #include "WidgetPrivateData.hpp" | |||
| #include "WindowPrivateData.hpp" | |||
| // templated classes | |||
| #include "ImageBaseWidgets.cpp" | |||
| START_NAMESPACE_DGL | |||
| // ----------------------------------------------------------------------- | |||
| #ifdef DGL_USE_OPENGL3 | |||
| static void notImplemented(const char* const name) | |||
| { | |||
| d_stderr2("OpenGL3 function not implemented: %s", name); | |||
| } | |||
| #else | |||
| # define DGL_USE_COMPAT_OPENGL | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // Color | |||
| void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| if (includeAlpha) | |||
| glColor4f(red, green, blue, alpha); | |||
| else | |||
| glColor3f(red, green, blue); | |||
| #else | |||
| notImplemented("Color::setFor"); | |||
| // unused | |||
| (void)includeAlpha; | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Line | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| template<typename T> | |||
| static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||
| glBegin(GL_LINES); | |||
| { | |||
| glVertex2d(posStart.getX(), posStart.getY()); | |||
| glVertex2d(posEnd.getX(), posEnd.getY()); | |||
| } | |||
| glEnd(); | |||
| } | |||
| #endif | |||
| template<typename T> | |||
| void Line<T>::draw(const GraphicsContext&, const T width) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||
| glLineWidth(static_cast<GLfloat>(width)); | |||
| drawLine<T>(posStart, posEnd); | |||
| #else | |||
| notImplemented("Line::draw"); | |||
| #endif | |||
| } | |||
| // deprecated calls | |||
| template<typename T> | |||
| void Line<T>::draw() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawLine<T>(posStart, posEnd); | |||
| #else | |||
| notImplemented("Line::draw"); | |||
| #endif | |||
| } | |||
| template class Line<double>; | |||
| template class Line<float>; | |||
| template class Line<int>; | |||
| template class Line<uint>; | |||
| template class Line<short>; | |||
| template class Line<ushort>; | |||
| // ----------------------------------------------------------------------- | |||
| // Circle | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| template<typename T> | |||
| static void drawCircle(const Point<T>& pos, | |||
| const uint numSegments, | |||
| const float size, | |||
| const float sin, | |||
| const float cos, | |||
| const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); | |||
| const T origx = pos.getX(); | |||
| const T origy = pos.getY(); | |||
| double t, x = size, y = 0.0; | |||
| glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); | |||
| for (uint i=0; i<numSegments; ++i) | |||
| { | |||
| glVertex2d(x + origx, y + origy); | |||
| t = x; | |||
| x = cos * x - sin * y; | |||
| y = sin * t + cos * y; | |||
| } | |||
| glEnd(); | |||
| } | |||
| #endif | |||
| template<typename T> | |||
| void Circle<T>::draw(const GraphicsContext&) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
| #else | |||
| notImplemented("Circle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
| #else | |||
| notImplemented("Circle::drawOutline"); | |||
| #endif | |||
| } | |||
| // deprecated calls | |||
| template<typename T> | |||
| void Circle<T>::draw() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
| #else | |||
| notImplemented("Circle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Circle<T>::drawOutline() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
| #else | |||
| notImplemented("Circle::drawOutline"); | |||
| #endif | |||
| } | |||
| template class Circle<double>; | |||
| template class Circle<float>; | |||
| template class Circle<int>; | |||
| template class Circle<uint>; | |||
| template class Circle<short>; | |||
| template class Circle<ushort>; | |||
| // ----------------------------------------------------------------------- | |||
| // Triangle | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| template<typename T> | |||
| static void drawTriangle(const Point<T>& pos1, | |||
| const Point<T>& pos2, | |||
| const Point<T>& pos3, | |||
| const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |||
| glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); | |||
| { | |||
| glVertex2d(pos1.getX(), pos1.getY()); | |||
| glVertex2d(pos2.getX(), pos2.getY()); | |||
| glVertex2d(pos3.getX(), pos3.getY()); | |||
| } | |||
| glEnd(); | |||
| } | |||
| #endif | |||
| template<typename T> | |||
| void Triangle<T>::draw(const GraphicsContext&) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||
| #else | |||
| notImplemented("Triangle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||
| #else | |||
| notImplemented("Triangle::drawOutline"); | |||
| #endif | |||
| } | |||
| // deprecated calls | |||
| template<typename T> | |||
| void Triangle<T>::draw() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||
| #else | |||
| notImplemented("Triangle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Triangle<T>::drawOutline() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||
| #else | |||
| notImplemented("Triangle::drawOutline"); | |||
| #endif | |||
| } | |||
| template class Triangle<double>; | |||
| template class Triangle<float>; | |||
| template class Triangle<int>; | |||
| template class Triangle<uint>; | |||
| template class Triangle<short>; | |||
| template class Triangle<ushort>; | |||
| // ----------------------------------------------------------------------- | |||
| // Rectangle | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| template<typename T> | |||
| static void drawRectangle(const Rectangle<T>& rect, const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); | |||
| glBegin(outline ? GL_LINE_LOOP : GL_QUADS); | |||
| { | |||
| const T x = rect.getX(); | |||
| const T y = rect.getY(); | |||
| const T w = rect.getWidth(); | |||
| const T h = rect.getHeight(); | |||
| glTexCoord2f(0.0f, 0.0f); | |||
| glVertex2d(x, y); | |||
| glTexCoord2f(1.0f, 0.0f); | |||
| glVertex2d(x+w, y); | |||
| glTexCoord2f(1.0f, 1.0f); | |||
| glVertex2d(x+w, y+h); | |||
| glTexCoord2f(0.0f, 1.0f); | |||
| glVertex2d(x, y+h); | |||
| } | |||
| glEnd(); | |||
| } | |||
| #endif | |||
| template<typename T> | |||
| void Rectangle<T>::draw(const GraphicsContext&) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawRectangle<T>(*this, false); | |||
| #else | |||
| notImplemented("Rectangle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawRectangle<T>(*this, true); | |||
| #else | |||
| notImplemented("Rectangle::drawOutline"); | |||
| #endif | |||
| } | |||
| // deprecated calls | |||
| template<typename T> | |||
| void Rectangle<T>::draw() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawRectangle<T>(*this, false); | |||
| #else | |||
| notImplemented("Rectangle::draw"); | |||
| #endif | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::drawOutline() | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| drawRectangle<T>(*this, true); | |||
| #else | |||
| notImplemented("Rectangle::drawOutline"); | |||
| #endif | |||
| } | |||
| template class Rectangle<double>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<uint>; | |||
| template class Rectangle<short>; | |||
| template class Rectangle<ushort>; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // OpenGLImage | |||
| static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, textureId); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
| static_cast<GLsizei>(image.getWidth()), | |||
| static_cast<GLsizei>(image.getHeight()), | |||
| 0, | |||
| asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData()); | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled) | |||
| { | |||
| if (textureId == 0 || image.isInvalid()) | |||
| return; | |||
| if (! setupCalled) | |||
| { | |||
| setupOpenGLImage(image, textureId); | |||
| setupCalled = true; | |||
| } | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
| #endif | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, textureId); | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| glBegin(GL_QUADS); | |||
| { | |||
| const int x = pos.getX(); | |||
| const int y = pos.getY(); | |||
| const int w = static_cast<int>(image.getWidth()); | |||
| const int h = static_cast<int>(image.getHeight()); | |||
| glTexCoord2f(0.0f, 0.0f); | |||
| glVertex2d(x, y); | |||
| glTexCoord2f(1.0f, 0.0f); | |||
| glVertex2d(x+w, y); | |||
| glTexCoord2f(1.0f, 1.0f); | |||
| glVertex2d(x+w, y+h); | |||
| glTexCoord2f(0.0f, 1.0f); | |||
| glVertex2d(x, y+h); | |||
| } | |||
| glEnd(); | |||
| #endif | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| OpenGLImage::OpenGLImage() | |||
| : ImageBase(), | |||
| setupCalled(false), | |||
| textureInit(false), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| } | |||
| @@ -446,6 +49,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, co | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const Ima | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image) | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage() | |||
| { | |||
| if (textureId != 0) | |||
| glDeleteTextures(1, &textureId); | |||
| #ifdef DGL_USE_GLES | |||
| std::free(convertedData); | |||
| #endif | |||
| } | |||
| void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||
| @@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, c | |||
| ImageBase::loadFromMemory(rdata, s, fmt); | |||
| } | |||
| void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||
| { | |||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
| } | |||
| OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||
| { | |||
| rawData = image.rawData; | |||
| @@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept | |||
| return *this; | |||
| } | |||
| // deprecated calls | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt) | |||
| : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)), | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| @@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLe | |||
| setupCalled(false), | |||
| textureInit(true), | |||
| textureId(0) | |||
| #ifdef DGL_USE_GLES | |||
| , convertedData(nullptr) | |||
| , rawDataLast(nullptr) | |||
| #endif | |||
| { | |||
| glGenTextures(1, &textureId); | |||
| DISTRHO_SAFE_ASSERT(textureId != 0); | |||
| } | |||
| void OpenGLImage::draw() | |||
| { | |||
| drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||
| } | |||
| void OpenGLImage::drawAt(const int x, const int y) | |||
| { | |||
| drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||
| } | |||
| void OpenGLImage::drawAt(const Point<int>& pos) | |||
| { | |||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // ImageBaseAboutWindow | |||
| #if 0 | |||
| template <> | |||
| void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| img.draw(context); | |||
| } | |||
| #endif | |||
| template class ImageBaseAboutWindow<OpenGLImage>; | |||
| // ----------------------------------------------------------------------- | |||
| // ImageBaseButton | |||
| template class ImageBaseButton<OpenGLImage>; | |||
| // ----------------------------------------------------------------------- | |||
| // ImageBaseKnob | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||
| { | |||
| glTextureId = 0; | |||
| glGenTextures(1, &glTextureId); | |||
| } | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||
| { | |||
| if (glTextureId == 0) | |||
| return; | |||
| glDeleteTextures(1, &glTextureId); | |||
| glTextureId = 0; | |||
| } | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| const float normValue = getNormalizedValue(); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||
| if (! pData->isReady) | |||
| { | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| uint imageDataOffset = 0; | |||
| if (pData->rotationAngle == 0) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); | |||
| const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); | |||
| const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); | |||
| // TODO kImageFormatGreyscale | |||
| const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || | |||
| pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||
| /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); | |||
| } | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
| static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||
| asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||
| pData->isReady = true; | |||
| } | |||
| const int w = static_cast<int>(getWidth()); | |||
| const int h = static_cast<int>(getHeight()); | |||
| if (pData->rotationAngle != 0) | |||
| { | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| glPushMatrix(); | |||
| #endif | |||
| const int w2 = w/2; | |||
| const int h2 = h/2; | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||
| glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||
| #endif | |||
| Rectangle<int>(-w2, -h2, w, h).draw(context); | |||
| #ifdef DGL_USE_COMPAT_OPENGL | |||
| glPopMatrix(); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| Rectangle<int>(0, 0, w, h).draw(context); | |||
| } | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| template class ImageBaseKnob<OpenGLImage>; | |||
| // ----------------------------------------------------------------------- | |||
| // ImageBaseSlider | |||
| template class ImageBaseSlider<OpenGLImage>; | |||
| // ----------------------------------------------------------------------- | |||
| // ImageBaseSwitch | |||
| template class ImageBaseSwitch<OpenGLImage>; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |||
| { | |||
| @@ -736,7 +216,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const | |||
| selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void TopLevelWidget::PrivateData::display() | |||
| { | |||
| @@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display() | |||
| selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void Window::PrivateData::renderToPicture(const char* const filename, | |||
| const GraphicsContext&, | |||
| @@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename, | |||
| fclose(f); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| return (const GraphicsContext&)graphicsContext; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -0,0 +1,585 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifdef _MSC_VER | |||
| // instantiated template classes whose methods are defined elsewhere | |||
| # pragma warning(disable:4661) | |||
| #endif | |||
| #include "../OpenGL.hpp" | |||
| #include "../Color.hpp" | |||
| #include "../ImageWidgets.hpp" | |||
| // #include "SubWidgetPrivateData.hpp" | |||
| // #include "TopLevelWidgetPrivateData.hpp" | |||
| // #include "WidgetPrivateData.hpp" | |||
| #include "WindowPrivateData.hpp" | |||
| // templated classes | |||
| #include "ImageBaseWidgets.cpp" | |||
| START_NAMESPACE_DGL | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Check for correct build config | |||
| #ifndef DGL_OPENGL | |||
| # error Build config error, OpenGL was NOT requested while building OpenGL2 code | |||
| #endif | |||
| #ifdef DGL_CAIRO | |||
| # error Build config error, Cairo requested while building OpenGL2 code | |||
| #endif | |||
| #ifdef DGL_VULKAN | |||
| # error Build config error, Vulkan requested while building OpenGL2 code | |||
| #endif | |||
| #ifdef DGL_USE_GLES2 | |||
| # error Build config error, GLESv2 requested while building OpenGL2 code | |||
| #endif | |||
| #ifdef DGL_USE_GLES3 | |||
| # error Build config error, GLESv3 requested while building OpenGL2 code | |||
| #endif | |||
| #ifdef DGL_USE_OPENGL3 | |||
| # error Build config error, OpenGL3 requested while building OpenGL2 code | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Color | |||
| void Color::setFor(const GraphicsContext&, const bool includeAlpha) | |||
| { | |||
| if (includeAlpha) | |||
| glColor4f(red, green, blue, alpha); | |||
| else | |||
| glColor3f(red, green, blue); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Line | |||
| template<typename T> | |||
| static void drawLine(const Point<T>& posStart, const Point<T>& posEnd) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |||
| glBegin(GL_LINES); | |||
| { | |||
| glVertex2d(posStart.getX(), posStart.getY()); | |||
| glVertex2d(posEnd.getX(), posEnd.getY()); | |||
| } | |||
| glEnd(); | |||
| } | |||
| template<typename T> | |||
| void Line<T>::draw(const GraphicsContext&, const T width) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |||
| glLineWidth(static_cast<GLfloat>(width)); | |||
| drawLine<T>(posStart, posEnd); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Line<T>::draw() | |||
| { | |||
| drawLine<T>(posStart, posEnd); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Circle | |||
| template<typename T> | |||
| static void drawCircle(const Point<T>& pos, | |||
| const uint numSegments, | |||
| const float size, | |||
| const float sin, | |||
| const float cos, | |||
| const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); | |||
| const double origx = static_cast<double>(pos.getX()); | |||
| const double origy = static_cast<double>(pos.getY()); | |||
| double t; | |||
| double x = size; | |||
| double y = 0.0; | |||
| glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); | |||
| for (uint i = 0; i < numSegments; ++i) | |||
| { | |||
| glVertex2d(x + origx, y + origy); | |||
| t = x; | |||
| x = cos * x - sin * y; | |||
| y = sin * t + cos * y; | |||
| } | |||
| glEnd(); | |||
| } | |||
| template<typename T> | |||
| static void drawCircle(const GraphicsContext&, | |||
| const Point<T>& pos, | |||
| const uint numSegments, | |||
| const float size, | |||
| const float sin, | |||
| const float cos, | |||
| const bool outline) | |||
| { | |||
| drawCircle<T>(pos, numSegments, size, sin, cos, outline); | |||
| } | |||
| template<typename T> | |||
| void Circle<T>::draw(const GraphicsContext& context) | |||
| { | |||
| drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, false); | |||
| } | |||
| template<typename T> | |||
| void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Circle<T>::draw() | |||
| { | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false); | |||
| } | |||
| template<typename T> | |||
| void Circle<T>::drawOutline() | |||
| { | |||
| drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Triangle | |||
| template<typename T> | |||
| static void drawTriangle(const Point<T>& pos1, | |||
| const Point<T>& pos2, | |||
| const Point<T>& pos3, | |||
| const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |||
| glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); | |||
| { | |||
| glVertex2d(pos1.getX(), pos1.getY()); | |||
| glVertex2d(pos2.getX(), pos2.getY()); | |||
| glVertex2d(pos3.getX(), pos3.getY()); | |||
| } | |||
| glEnd(); | |||
| } | |||
| template<typename T> | |||
| static void drawTriangle(const GraphicsContext&, | |||
| const Point<T>& pos1, | |||
| const Point<T>& pos2, | |||
| const Point<T>& pos3, | |||
| const bool outline) | |||
| { | |||
| drawTriangle<T>(pos1, pos2, pos3, outline); | |||
| } | |||
| template<typename T> | |||
| void Triangle<T>::draw(const GraphicsContext&) | |||
| { | |||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||
| } | |||
| template<typename T> | |||
| void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Triangle<T>::draw() | |||
| { | |||
| drawTriangle<T>(pos1, pos2, pos3, false); | |||
| } | |||
| template<typename T> | |||
| void Triangle<T>::drawOutline() | |||
| { | |||
| drawTriangle<T>(pos1, pos2, pos3, true); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Rectangle | |||
| template<typename T> | |||
| static void drawRectangle(const Rectangle<T>& rect, const bool outline) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); | |||
| glBegin(outline ? GL_LINE_LOOP : GL_QUADS); | |||
| { | |||
| const T x = rect.getX(); | |||
| const T y = rect.getY(); | |||
| const T w = rect.getWidth(); | |||
| const T h = rect.getHeight(); | |||
| glTexCoord2f(0.0f, 0.0f); | |||
| glVertex2d(x, y); | |||
| glTexCoord2f(1.0f, 0.0f); | |||
| glVertex2d(x+w, y); | |||
| glTexCoord2f(1.0f, 1.0f); | |||
| glVertex2d(x+w, y+h); | |||
| glTexCoord2f(0.0f, 1.0f); | |||
| glVertex2d(x, y+h); | |||
| } | |||
| glEnd(); | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::draw(const GraphicsContext&) | |||
| { | |||
| drawRectangle<T>(*this, false); | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |||
| glLineWidth(static_cast<GLfloat>(lineWidth)); | |||
| drawRectangle<T>(*this, true); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| template<typename T> | |||
| void Rectangle<T>::draw() | |||
| { | |||
| drawRectangle<T>(*this, false); | |||
| } | |||
| template<typename T> | |||
| void Rectangle<T>::drawOutline() | |||
| { | |||
| drawRectangle<T>(*this, true); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // OpenGLImage | |||
| static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, textureId); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| glTexImage2D(GL_TEXTURE_2D, | |||
| 0, | |||
| GL_RGBA, | |||
| static_cast<GLsizei>(image.getWidth()), | |||
| static_cast<GLsizei>(image.getHeight()), | |||
| 0, | |||
| asOpenGLImageFormat(image.getFormat()), | |||
| GL_UNSIGNED_BYTE, | |||
| image.getRawData()); | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled) | |||
| { | |||
| if (textureId == 0 || image.isInvalid()) | |||
| return; | |||
| if (! setupCalled) | |||
| { | |||
| setupOpenGLImage(image, textureId); | |||
| setupCalled = true; | |||
| } | |||
| glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, textureId); | |||
| glBegin(GL_QUADS); | |||
| { | |||
| const int x = pos.getX(); | |||
| const int y = pos.getY(); | |||
| const int w = static_cast<int>(image.getWidth()); | |||
| const int h = static_cast<int>(image.getHeight()); | |||
| glTexCoord2f(0.0f, 0.0f); | |||
| glVertex2d(x, y); | |||
| glTexCoord2f(1.0f, 0.0f); | |||
| glVertex2d(x+w, y); | |||
| glTexCoord2f(1.0f, 1.0f); | |||
| glVertex2d(x+w, y+h); | |||
| glTexCoord2f(0.0f, 1.0f); | |||
| glVertex2d(x, y+h); | |||
| } | |||
| glEnd(); | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | |||
| { | |||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
| } | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| void OpenGLImage::draw() | |||
| { | |||
| drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled); | |||
| } | |||
| void OpenGLImage::drawAt(const int x, const int y) | |||
| { | |||
| drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled); | |||
| } | |||
| void OpenGLImage::drawAt(const Point<int>& pos) | |||
| { | |||
| drawOpenGLImage(*this, pos, textureId, setupCalled); | |||
| } | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // ImageBaseAboutWindow | |||
| #if 0 | |||
| template <> | |||
| void ImageBaseAboutWindow<OpenGLImage>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| img.draw(context); | |||
| } | |||
| #endif | |||
| template class ImageBaseAboutWindow<OpenGLImage>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // ImageBaseButton | |||
| template class ImageBaseButton<OpenGLImage>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // ImageBaseKnob | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||
| { | |||
| glTextureId = 0; | |||
| glGenTextures(1, &glTextureId); | |||
| } | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||
| { | |||
| if (glTextureId == 0) | |||
| return; | |||
| glDeleteTextures(1, &glTextureId); | |||
| glTextureId = 0; | |||
| } | |||
| template <> | |||
| void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
| { | |||
| const GraphicsContext& context(getGraphicsContext()); | |||
| const float normValue = getNormalizedValue(); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||
| if (! pData->isReady) | |||
| { | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |||
| static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f }; | |||
| glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans); | |||
| glPixelStorei(GL_PACK_ALIGNMENT, 1); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |||
| uint imageDataOffset = 0; | |||
| if (pData->rotationAngle == 0) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,); | |||
| const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); | |||
| const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); | |||
| // TODO kImageFormatGreyscale | |||
| const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || | |||
| pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||
| /* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); | |||
| } | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |||
| static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0, | |||
| asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); | |||
| pData->isReady = true; | |||
| } | |||
| const int w = static_cast<int>(getWidth()); | |||
| const int h = static_cast<int>(getHeight()); | |||
| if (pData->rotationAngle != 0) | |||
| { | |||
| glPushMatrix(); | |||
| const int w2 = w/2; | |||
| const int h2 = h/2; | |||
| glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f); | |||
| glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f); | |||
| Rectangle<int>(-w2, -h2, w, h).draw(context); | |||
| glPopMatrix(); | |||
| } | |||
| else | |||
| { | |||
| Rectangle<int>(0, 0, w, h).draw(context); | |||
| } | |||
| glBindTexture(GL_TEXTURE_2D, 0); | |||
| glDisable(GL_TEXTURE_2D); | |||
| } | |||
| template class ImageBaseKnob<OpenGLImage>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // ImageBaseSlider | |||
| template class ImageBaseSlider<OpenGLImage>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // ImageBaseSwitch | |||
| template class ImageBaseSwitch<OpenGLImage>; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void Window::PrivateData::createContextIfNeeded() | |||
| { | |||
| } | |||
| void Window::PrivateData::destroyContext() | |||
| { | |||
| } | |||
| void Window::PrivateData::startContext() | |||
| { | |||
| const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| glViewport(0, 0, static_cast<GLsizei>(size.width), static_cast<GLsizei>(size.height)); | |||
| glMatrixMode(GL_PROJECTION); | |||
| glLoadIdentity(); | |||
| glOrtho(0.0, static_cast<GLdouble>(size.width), static_cast<GLdouble>(size.height), 0.0, 0.0, 1.0); | |||
| glViewport(0, 0, static_cast<GLsizei>(size.width), static_cast<GLsizei>(size.height)); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| glLoadIdentity(); | |||
| } | |||
| void Window::PrivateData::endContext() | |||
| { | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #ifndef DGL_GEOMETRY_CPP_INCLUDED | |||
| template class Line<double>; | |||
| template class Line<float>; | |||
| template class Line<int>; | |||
| template class Line<uint>; | |||
| template class Line<short>; | |||
| template class Line<ushort>; | |||
| template class Circle<double>; | |||
| template class Circle<float>; | |||
| template class Circle<int>; | |||
| template class Circle<uint>; | |||
| template class Circle<short>; | |||
| template class Circle<ushort>; | |||
| template class Triangle<double>; | |||
| template class Triangle<float>; | |||
| template class Triangle<int>; | |||
| template class Triangle<uint>; | |||
| template class Triangle<short>; | |||
| template class Triangle<ushort>; | |||
| template class Rectangle<double>; | |||
| template class Rectangle<float>; | |||
| template class Rectangle<int>; | |||
| template class Rectangle<uint>; | |||
| template class Rectangle<short>; | |||
| template class Rectangle<ushort>; | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DGL | |||
| @@ -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 | |||
| @@ -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) | |||
| }; | |||
| @@ -28,7 +28,11 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win) | |||
| : window(win), | |||
| ppData(nullptr), | |||
| active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)), | |||
| reenter(false) {} | |||
| reenter(false) | |||
| { | |||
| if (active) | |||
| window.pData->createContextIfNeeded(); | |||
| } | |||
| Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin) | |||
| : window(win), | |||
| @@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi | |||
| { | |||
| puglBackendLeave(ppData->view); | |||
| active = puglBackendEnter(window.pData->view); | |||
| if (active) | |||
| window.pData->createContextIfNeeded(); | |||
| } | |||
| } | |||
| @@ -180,22 +186,22 @@ int Window::getOffsetX() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| return puglGetFrame(pData->view).x; | |||
| return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).x; | |||
| } | |||
| int Window::getOffsetY() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| return puglGetFrame(pData->view).y; | |||
| return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y; | |||
| } | |||
| Point<int> Window::getOffset() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>()); | |||
| const PuglRect rect = puglGetFrame(pData->view); | |||
| return Point<int>(rect.x, rect.y); | |||
| const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION); | |||
| return Point<int>(pos.x, pos.y); | |||
| } | |||
| void Window::setOffsetX(const int x) | |||
| @@ -214,7 +220,7 @@ void Window::setOffset(const int x, const int y) | |||
| DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,); | |||
| if (pData->view != nullptr) | |||
| puglSetPosition(pData->view, x, y); | |||
| puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y); | |||
| } | |||
| void Window::setOffset(const Point<int>& offset) | |||
| @@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| const double width = puglGetFrame(pData->view).width; | |||
| DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0); | |||
| return static_cast<uint>(width + 0.5); | |||
| const PuglSpan width = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).width; | |||
| DISTRHO_SAFE_ASSERT(width > 0); | |||
| return width; | |||
| } | |||
| uint Window::getHeight() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); | |||
| const double height = puglGetFrame(pData->view).height; | |||
| DISTRHO_SAFE_ASSERT_RETURN(height > 0.0, 0); | |||
| return static_cast<uint>(height + 0.5); | |||
| const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height; | |||
| DISTRHO_SAFE_ASSERT(height > 0); | |||
| return height; | |||
| } | |||
| Size<uint> Window::getSize() const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>()); | |||
| const PuglRect rect = puglGetFrame(pData->view); | |||
| DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size<uint>()); | |||
| DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size<uint>()); | |||
| return Size<uint>(static_cast<uint>(rect.width + 0.5), | |||
| static_cast<uint>(rect.height + 0.5)); | |||
| const PuglArea size = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE); | |||
| DISTRHO_SAFE_ASSERT(size.width > 0); | |||
| DISTRHO_SAFE_ASSERT(size.height > 0); | |||
| return Size<uint>(size.width, size.height); | |||
| } | |||
| void Window::setWidth(const uint width) | |||
| @@ -392,12 +397,10 @@ Application& Window::getApp() const noexcept | |||
| return pData->app; | |||
| } | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| const GraphicsContext& Window::getGraphicsContext() const noexcept | |||
| { | |||
| return pData->getGraphicsContext(); | |||
| } | |||
| #endif | |||
| uintptr_t Window::getNativeWindowHandle() const noexcept | |||
| { | |||
| @@ -443,7 +446,7 @@ void Window::repaint() noexcept | |||
| if (pData->usesScheduledRepaints) | |||
| pData->appData->needsRepaint = true; | |||
| puglPostRedisplay(pData->view); | |||
| puglObscureView(pData->view); | |||
| } | |||
| void Window::repaint(const Rectangle<uint>& rect) noexcept | |||
| @@ -454,22 +457,22 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||
| if (pData->usesScheduledRepaints) | |||
| pData->appData->needsRepaint = true; | |||
| PuglRect prect = { | |||
| static_cast<PuglCoord>(rect.getX()), | |||
| static_cast<PuglCoord>(rect.getY()), | |||
| static_cast<PuglSpan>(rect.getWidth()), | |||
| static_cast<PuglSpan>(rect.getHeight()), | |||
| }; | |||
| int x = static_cast<int>(rect.getX()); | |||
| int y = static_cast<int>(rect.getY()); | |||
| uint width = rect.getWidth(); | |||
| uint height = rect.getHeight(); | |||
| if (pData->autoScaling) | |||
| { | |||
| const double autoScaleFactor = pData->autoScaleFactor; | |||
| prect.x = static_cast<PuglCoord>(prect.x * autoScaleFactor); | |||
| prect.y = static_cast<PuglCoord>(prect.y * autoScaleFactor); | |||
| prect.width = static_cast<PuglSpan>(prect.width * autoScaleFactor + 0.5); | |||
| prect.height = static_cast<PuglSpan>(prect.height * autoScaleFactor + 0.5); | |||
| x = d_roundToIntPositive(x * autoScaleFactor); | |||
| y = d_roundToIntPositive(y * autoScaleFactor); | |||
| width = d_roundToUnsignedInt(width * autoScaleFactor); | |||
| height = d_roundToUnsignedInt(height * autoScaleFactor); | |||
| } | |||
| puglPostRedisplayRect(pData->view, prect); | |||
| puglObscureRegion(pData->view, x, y, width, height); | |||
| } | |||
| void Window::renderToPicture(const char* const filename) | |||
| @@ -523,8 +526,8 @@ void Window::setGeometryConstraints(uint minimumWidth, | |||
| { | |||
| const Size<uint> size(getSize()); | |||
| setSize(static_cast<uint>(size.getWidth() * scaleFactor + 0.5), | |||
| static_cast<uint>(size.getHeight() * scaleFactor + 0.5)); | |||
| setSize(d_roundToUnsignedInt(size.getWidth() * scaleFactor), | |||
| d_roundToUnsignedInt(size.getHeight() * scaleFactor)); | |||
| } | |||
| } | |||
| @@ -578,11 +581,11 @@ void Window::onFocus(bool, CrossingMode) | |||
| { | |||
| } | |||
| void Window::onReshape(const uint width, const uint height) | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| void Window::onReshape(uint, uint) | |||
| { | |||
| if (pData->view != nullptr) | |||
| puglFallbackOnResize(pData->view, width, height); | |||
| } | |||
| #endif | |||
| void Window::onScaleFactorChanged(double) | |||
| { | |||
| @@ -91,10 +91,10 @@ static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintp | |||
| if (PuglView* const view = puglNewView(world)) | |||
| { | |||
| puglSetParentWindow(view, parentWindowHandle); | |||
| puglSetParent(view, parentWindowHandle); | |||
| if (parentWindowHandle != 0) | |||
| puglSetPosition(view, 0, 0); | |||
| puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0); | |||
| return view; | |||
| } | |||
| @@ -268,6 +268,7 @@ Window::PrivateData::~PrivateData() | |||
| isVisible = false; | |||
| } | |||
| destroyContext(); | |||
| puglFreeView(view); | |||
| } | |||
| @@ -300,8 +301,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||
| // PUGL_SAMPLES ?? | |||
| puglSetEventFunc(view, puglEventCallback); | |||
| // setting default size triggers system-level calls, do it last | |||
| puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height)); | |||
| // setting size triggers system-level calls, do it last | |||
| puglSetSizeAndDefault(view, width, height); | |||
| } | |||
| bool Window::PrivateData::initPost() | |||
| @@ -366,10 +367,6 @@ void Window::PrivateData::show() | |||
| isClosed = false; | |||
| appData->oneWindowShown(); | |||
| // FIXME | |||
| // PuglRect rect = puglGetFrame(view); | |||
| // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| puglWin32ShowCentered(view); | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| @@ -453,6 +450,13 @@ void Window::PrivateData::setResizable(const bool resizable) | |||
| puglSetResizable(view, resizable); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
| { | |||
| return reinterpret_cast<const GraphicsContext&>(graphicsContext); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| void Window::PrivateData::idleCallback() | |||
| @@ -538,9 +542,9 @@ bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPA | |||
| if (webViewHandle != nullptr) | |||
| webViewDestroy(webViewHandle); | |||
| const PuglRect rect = puglGetFrame(view); | |||
| uint initialWidth = static_cast<uint>(rect.width) - options.offset.x; | |||
| uint initialHeight = static_cast<uint>(rect.height) - options.offset.y; | |||
| const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| uint initialWidth = size.width - options.offset.x; | |||
| uint initialHeight = size.height - options.offset.y; | |||
| webViewOffset = Point<int>(options.offset.x, options.offset.y); | |||
| @@ -640,6 +644,8 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||
| DGL_DBGp("PUGL: onReshape : %d %d\n", width, height); | |||
| createContextIfNeeded(); | |||
| if (autoScaling) | |||
| { | |||
| const double scaleHorizontal = width / static_cast<double>(minWidth); | |||
| @@ -662,9 +668,27 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||
| autoScaling ? autoScaleFactor : scaleFactor); | |||
| #endif | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| #if defined(_MSC_VER) | |||
| #pragma warning(push) | |||
| #pragma warning(disable:4996) | |||
| #elif defined(__clang__) | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
| #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| #endif | |||
| self->onReshape(uwidth, uheight); | |||
| #if defined(_MSC_VER) | |||
| #pragma warning(pop) | |||
| #elif defined(__clang__) | |||
| #pragma clang diagnostic pop | |||
| #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| #endif | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET(it) | |||
| { | |||
| TopLevelWidget* const widget = *it; | |||
| @@ -679,10 +703,9 @@ void Window::PrivateData::onPuglConfigure(const uint width, const uint height) | |||
| */ | |||
| ((Widget*)widget)->setSize(uwidth, uheight); | |||
| } | |||
| #endif | |||
| // always repaint after a resize | |||
| puglPostRedisplay(view); | |||
| puglObscureView(view); | |||
| } | |||
| void Window::PrivateData::onPuglExpose() | |||
| @@ -691,7 +714,8 @@ void Window::PrivateData::onPuglExpose() | |||
| puglOnDisplayPrepare(view); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| startContext(); | |||
| FOR_EACH_TOP_LEVEL_WIDGET(it) | |||
| { | |||
| TopLevelWidget* const widget(*it); | |||
| @@ -702,12 +726,13 @@ void Window::PrivateData::onPuglExpose() | |||
| if (char* const filename = filenameToRenderInto) | |||
| { | |||
| const PuglRect rect = puglGetFrame(view); | |||
| const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| filenameToRenderInto = nullptr; | |||
| renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||
| renderToPicture(filename, getGraphicsContext(), size.width, size.height); | |||
| std::free(filename); | |||
| } | |||
| #endif | |||
| endContext(); | |||
| } | |||
| void Window::PrivateData::onPuglClose() | |||
| @@ -760,7 +785,6 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) | |||
| if (modal.child != nullptr) | |||
| return modal.child->focus(); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |||
| { | |||
| TopLevelWidget* const widget(*rit); | |||
| @@ -768,7 +792,6 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) | |||
| if (widget->isVisible() && widget->onKeyboard(ev)) | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) | |||
| @@ -778,7 +801,6 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) | |||
| if (modal.child != nullptr) | |||
| return modal.child->focus(); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |||
| { | |||
| TopLevelWidget* const widget(*rit); | |||
| @@ -786,7 +808,6 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) | |||
| if (widget->isVisible() && widget->onCharacterInput(ev)) | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) | |||
| @@ -796,7 +817,6 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) | |||
| if (modal.child != nullptr) | |||
| return modal.child->focus(); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |||
| { | |||
| TopLevelWidget* const widget(*rit); | |||
| @@ -804,7 +824,6 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) | |||
| if (widget->isVisible() && widget->onMouse(ev)) | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) | |||
| @@ -814,7 +833,6 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) | |||
| if (modal.child != nullptr) | |||
| return modal.child->focus(); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |||
| { | |||
| TopLevelWidget* const widget(*rit); | |||
| @@ -822,7 +840,6 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) | |||
| if (widget->isVisible() && widget->onMotion(ev)) | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||
| @@ -832,7 +849,6 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||
| if (modal.child != nullptr) | |||
| return modal.child->focus(); | |||
| #ifndef DPF_TEST_WINDOW_CPP | |||
| FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) | |||
| { | |||
| TopLevelWidget* const widget(*rit); | |||
| @@ -840,7 +856,6 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||
| if (widget->isVisible() && widget->onScroll(ev)) | |||
| break; | |||
| } | |||
| #endif | |||
| } | |||
| const void* Window::PrivateData::getClipboard(size_t& dataSize) | |||
| @@ -978,7 +993,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
| SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); | |||
| #endif | |||
| #ifdef DGL_USING_X11 | |||
| puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |||
| puglX11SetWindowType(view, pData->appData->isStandalone); | |||
| #endif | |||
| } | |||
| break; | |||
| @@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback { | |||
| PuglView* view; | |||
| /** Reserved space for graphics context. */ | |||
| mutable uint8_t graphicsContext[sizeof(void*)]; | |||
| mutable uint8_t graphicsContext[sizeof(int) * 9]; | |||
| void createContextIfNeeded(); | |||
| void destroyContext(); | |||
| void startContext(); | |||
| void endContext(); | |||
| /** The top-level widgets associated with this Window. */ | |||
| std::list<TopLevelWidget*> topLevelWidgets; | |||
| @@ -173,7 +173,7 @@ struct GLNVGtexture { | |||
| int width, height; | |||
| int type; | |||
| int flags; | |||
| #if defined NANOVG_GLES2 | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| unsigned char* data; | |||
| #endif | |||
| }; | |||
| @@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
| switch (type) | |||
| { | |||
| case NVG_TEXTURE_BGR: | |||
| #if NANOVG_GLES2 | |||
| // GLES2 cannot handle GL_BGR, do local conversion to GL_RGB | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| // GLES cannot handle GL_BGR, do local conversion to GL_RGB | |||
| tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h); | |||
| for (uint32_t i=0; i<w*h; ++i) | |||
| for (int i = 0; i < w * h; ++i) | |||
| { | |||
| tex->data[i*3+0] = data[i*3+2]; | |||
| tex->data[i*3+1] = data[i*3+1]; | |||
| @@ -829,34 +829,34 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im | |||
| #endif | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| #if NANOVG_GLES2 | |||
| // GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GLES3) | |||
| // GLES cannot handle GL_BGRA, do local conversion to GL_RGBA | |||
| tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h); | |||
| for (uint32_t i=0; i<w*h; ++i) | |||
| for (int i = 0; i < w * h; ++i) | |||
| { | |||
| tex->data[i*3+0] = data[i*3+3]; | |||
| tex->data[i*3+1] = data[i*3+2]; | |||
| tex->data[i*3+2] = data[i*3+1]; | |||
| tex->data[i*3+3] = data[i*3+0]; | |||
| tex->data[i*4+0] = data[i*4+2]; | |||
| tex->data[i*4+1] = data[i*4+1]; | |||
| tex->data[i*4+2] = data[i*4+0]; | |||
| tex->data[i*4+3] = data[i*4+3]; | |||
| } | |||
| data = tex->data; | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| #endif | |||
| break; | |||
| case NVG_TEXTURE_RGB: | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_RGBA: | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| default: | |||
| #if defined(NANOVG_GLES2) || defined (NANOVG_GL2) | |||
| #if defined(NANOVG_GL2) || defined(NANOVG_GLES2) | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
| #elif defined(NANOVG_GLES3) | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| #endif | |||
| break; | |||
| @@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w | |||
| switch (tex->type) | |||
| { | |||
| case NVG_TEXTURE_BGR: | |||
| #if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| #endif | |||
| case NVG_TEXTURE_RGB: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| case NVG_TEXTURE_BGRA: | |||
| #if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3)) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| #endif | |||
| case NVG_TEXTURE_RGBA: | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); | |||
| break; | |||
| default: | |||
| #if defined(NANOVG_GLES2) || defined(NANOVG_GL2) | |||
| #if defined(NANOVG_GL2) || defined(NANOVG_GLES2) | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); | |||
| #else | |||
| glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); | |||
| @@ -246,7 +246,7 @@ puglPostRedisplayRect(PuglView* const view, const PuglRect rect) | |||
| } | |||
| PuglNativeView | |||
| puglGetNativeView(PuglView* const view) | |||
| puglGetNativeView(const PuglView* const view) | |||
| { | |||
| return 0; | |||
| } | |||
| @@ -1,10 +1,11 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // Copyright 2021-2025 Filipe Coelho <falktx@falktx.com> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "wasm.h" | |||
| #include "../pugl-upstream/src/internal.h" | |||
| #include "../pugl-upstream/src/platform.h" | |||
| #include <stdio.h> | |||
| @@ -82,6 +83,73 @@ puglInitViewInternals(PuglWorld* const world) | |||
| return impl; | |||
| } | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint)) | |||
| { | |||
| // No fine-grained updates, hints are always recalculated together | |||
| return puglUpdateSizeHints(view); | |||
| } | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const view) | |||
| { | |||
| const char* const className = view->world->strings[PUGL_CLASS_NAME]; | |||
| if (!view->hints[PUGL_RESIZABLE]) { | |||
| PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| if (!puglIsValidSize(size.width, size.height)) { | |||
| size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE); | |||
| } | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| var width = parseInt($1 / window.devicePixelRatio); | |||
| var height = parseInt($2 / window.devicePixelRatio); | |||
| canvasWrapper.style.setProperty("min-width", width + 'px'); | |||
| canvasWrapper.style.setProperty("max-width", width + 'px'); | |||
| canvasWrapper.style.setProperty("min-height", height + 'px'); | |||
| canvasWrapper.style.setProperty("max-height", height + 'px'); | |||
| }, className, size.width, size.height); | |||
| } else { | |||
| const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE]; | |||
| if (puglIsValidArea(minSize)) { | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px'); | |||
| canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px'); | |||
| }, className, minSize.width, minSize.height); | |||
| } else { | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.removeProperty("min-width"); | |||
| canvasWrapper.style.removeProperty("min-height"); | |||
| }, className); | |||
| } | |||
| const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE]; | |||
| if (puglIsValidArea(maxSize)) { | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.setProperty("max-width", parseInt($1 / window.devicePixelRatio) + 'px'); | |||
| canvasWrapper.style.setProperty("max-height", parseInt($2 / window.devicePixelRatio) + 'px'); | |||
| }, className, maxSize.width, maxSize.height); | |||
| } else { | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.removeProperty("max-width"); | |||
| canvasWrapper.style.removeProperty("max-height"); | |||
| }, className); | |||
| } | |||
| /* TODO | |||
| const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; | |||
| */ | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| static PuglStatus | |||
| puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event) | |||
| { | |||
| @@ -729,7 +797,8 @@ puglRealize(PuglView* const view) | |||
| const char* const className = view->world->strings[PUGL_CLASS_NAME]; | |||
| d_stdout("className is %s", className); | |||
| PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; | |||
| const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| if (!defaultSize.width || !defaultSize.height) { | |||
| return PUGL_BAD_CONFIGURATION; | |||
| } | |||
| @@ -747,8 +816,8 @@ puglRealize(PuglView* const view) | |||
| puglDispatchSimpleEvent(view, PUGL_REALIZE); | |||
| PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |||
| event.configure.x = view->defaultX; | |||
| event.configure.y = view->defaultY; | |||
| event.configure.x = defaultPos.x; | |||
| event.configure.y = defaultPos.y; | |||
| event.configure.width = defaultSize.width; | |||
| event.configure.height = defaultSize.height; | |||
| puglDispatchEvent(view, &event); | |||
| @@ -760,6 +829,8 @@ puglRealize(PuglView* const view) | |||
| canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio); | |||
| }, className); | |||
| puglUpdateSizeHints(view); | |||
| emscripten_set_canvas_element_size(className, defaultSize.width, defaultSize.height); | |||
| #ifndef PUGL_WASM_NO_KEYBOARD_INPUT | |||
| // emscripten_set_keypress_callback(className, view, false, puglKeyCallback); | |||
| @@ -794,7 +865,7 @@ puglShow(PuglView* const view, PuglShowCommand) | |||
| { | |||
| view->impl->visible = true; | |||
| view->impl->needsRepaint = true; | |||
| return puglPostRedisplay(view); | |||
| return puglObscureView(view); | |||
| } | |||
| PuglStatus | |||
| @@ -884,21 +955,29 @@ puglUpdate(PuglWorld* const world, const double timeout) | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* const view) | |||
| puglObscureView(PuglView* const view) | |||
| { | |||
| view->impl->needsRepaint = true; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* const view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->impl->needsRepaint = true; | |||
| return PUGL_FAILURE; | |||
| } | |||
| PuglNativeView | |||
| puglGetNativeView(PuglView* const view) | |||
| puglGetNativeView(const PuglView* const view) | |||
| { | |||
| return 0; | |||
| } | |||
| @@ -918,13 +997,13 @@ puglViewStringChanged(PuglView*, const PuglStringHint key, const char* const val | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| puglSetWindowSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| { | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| if (view->impl->created) { | |||
| const char* const className = view->world->strings[PUGL_CLASS_NAME]; | |||
| emscripten_set_canvas_element_size(className, width, height); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -1152,24 +1231,3 @@ puglSetTransientParent(PuglView* const view, const PuglNativeView parent) | |||
| view->transientParent = parent; | |||
| return PUGL_FAILURE; | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| printf("TODO: %s %d\n", __func__, __LINE__); | |||
| if (x > INT16_MAX || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->created) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| view->lastConfigure.x = (PuglCoord)x; | |||
| view->lastConfigure.y = (PuglCoord)y; | |||
| return puglPostRedisplay(view); | |||
| } | |||
| @@ -1,10 +1,17 @@ | |||
| # Copyright 2020-2022 David Robillard <d@drobilla.net> | |||
| # Copyright 2020-2025 David Robillard <d@drobilla.net> | |||
| # SPDX-License-Identifier: 0BSD OR ISC | |||
| --- | |||
| AlignConsecutiveAssignments: true | |||
| AlignConsecutiveDeclarations: true | |||
| AlignEscapedNewlinesLeft: true | |||
| AlignEscapedNewlines: Left | |||
| AttributeMacros: | |||
| - PUGL_API | |||
| - PUGL_CONST_API | |||
| - PUGL_CONST_FUNC | |||
| - PUGL_MALLOC_API | |||
| - PUGL_MALLOC_FUNC | |||
| - PUGL_WARN_UNUSED_RESULT | |||
| BasedOnStyle: Mozilla | |||
| BraceWrapping: | |||
| AfterNamespace: false | |||
| @@ -22,11 +29,5 @@ IndentPPDirectives: AfterHash | |||
| KeepEmptyLinesAtTheStartOfBlocks: false | |||
| SpacesInContainerLiterals: false | |||
| StatementMacros: | |||
| - PUGL_API | |||
| - PUGL_CONST_API | |||
| - PUGL_CONST_FUNC | |||
| - PUGL_DEPRECATED_BY | |||
| - PUGL_UNUSED | |||
| - PUGL_WARN_UNUSED_RESULT | |||
| - _Pragma | |||
| ... | |||
| @@ -0,0 +1,4 @@ | |||
| # Copyright 2025 David Robillard <d@drobilla.net> | |||
| # SPDX-License-Identifier: 0BSD OR ISC | |||
| examples/glad/ | |||
| @@ -1,4 +1,4 @@ | |||
| # Copyright 2020-2024 David Robillard <d@drobilla.net> | |||
| # Copyright 2020-2025 David Robillard <d@drobilla.net> | |||
| # SPDX-License-Identifier: 0BSD OR ISC | |||
| Checks: > | |||
| @@ -13,10 +13,11 @@ Checks: > | |||
| -misc-include-cleaner, | |||
| -readability-avoid-nested-conditional-operator, | |||
| -readability-identifier-length, | |||
| -readability-implicit-bool-conversion, | |||
| CheckOptions: | |||
| - key: hicpp-uppercase-literal-suffix.NewSuffixes | |||
| - key: hicpp-uppercase-literal-suffix.NewSuffixes | |||
| value: L;U;f | |||
| - key: readability-uppercase-literal-suffix.NewSuffixes | |||
| - key: readability-uppercase-literal-suffix.NewSuffixes | |||
| value: L;U;f | |||
| FormatStyle: file | |||
| HeaderFilterRegex: 'pugl/.*' | |||
| @@ -1,9 +1,5 @@ | |||
| { | |||
| "version": "1.0.0", | |||
| "include_dirs": [ | |||
| "bindings/cpp/include", | |||
| "include" | |||
| ], | |||
| "exclude_patterns": [ | |||
| "glad\\.c" | |||
| ], | |||
| @@ -23,6 +23,6 @@ Copyright: 2008-2018 The Khronos Group Inc. | |||
| License: MIT | |||
| Files: doc/*.rst resources/pugl.ipe resources/pugl.png resources/pugl.svg | |||
| Copyright: 2021-2023 David Robillard <d@drobilla.net> | |||
| Copyright: 2021-2025 David Robillard <d@drobilla.net> | |||
| Comment: Documentation | |||
| License: ISC | |||
| @@ -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 | |||
| ------------- | |||
| @@ -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 | |||
| @@ -4,8 +4,8 @@ | |||
| #ifndef PUGL_CAIRO_H | |||
| #define PUGL_CAIRO_H | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| PUGL_BEGIN_DECLS | |||
| @@ -21,8 +21,7 @@ PUGL_BEGIN_DECLS | |||
| Pass the returned value to puglSetBackend() to draw to a view with Cairo. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglCairoBackend(void); | |||
| /** | |||
| @@ -4,8 +4,8 @@ | |||
| #ifndef PUGL_GL_H | |||
| #define PUGL_GL_H | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| // IWYU pragma: begin_exports | |||
| @@ -42,8 +42,7 @@ typedef void (*PuglGlFunc)(void); | |||
| /** | |||
| Return the address of an OpenGL extension function. | |||
| */ | |||
| PUGL_API | |||
| PuglGlFunc | |||
| PUGL_API PuglGlFunc | |||
| puglGetProcAddress(const char* name); | |||
| /** | |||
| @@ -53,8 +52,7 @@ puglGetProcAddress(const char* name); | |||
| doing things like loading textures. Note that this must not be used for | |||
| drawing, which may only be done while processing an expose event. | |||
| */ | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_API PuglStatus | |||
| puglEnterContext(PuglView* view); | |||
| /** | |||
| @@ -62,8 +60,7 @@ puglEnterContext(PuglView* view); | |||
| This must only be called after puglEnterContext(). | |||
| */ | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_API PuglStatus | |||
| puglLeaveContext(PuglView* view); | |||
| /** | |||
| @@ -71,8 +68,7 @@ puglLeaveContext(PuglView* view); | |||
| Pass the returned value to puglSetBackend() to draw to a view with OpenGL. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglGlBackend(void); | |||
| PUGL_END_DECLS | |||
| @@ -4,8 +4,8 @@ | |||
| #ifndef PUGL_STUB_H | |||
| #define PUGL_STUB_H | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| PUGL_BEGIN_DECLS | |||
| @@ -22,8 +22,7 @@ PUGL_BEGIN_DECLS | |||
| This backend just creates a simple native window without setting up any | |||
| portable graphics API. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglStubBackend(void); | |||
| /** | |||
| @@ -10,8 +10,8 @@ | |||
| #ifndef PUGL_VULKAN_H | |||
| #define PUGL_VULKAN_H | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <vulkan/vulkan_core.h> | |||
| @@ -70,8 +70,7 @@ typedef struct PuglVulkanLoaderImpl PuglVulkanLoader; | |||
| @return A new Vulkan loader, or null on failure. | |||
| */ | |||
| PUGL_API | |||
| PuglVulkanLoader* | |||
| PUGL_API PuglVulkanLoader* | |||
| puglNewVulkanLoader(PuglWorld* world, const char* libraryName); | |||
| /** | |||
| @@ -80,8 +79,7 @@ puglNewVulkanLoader(PuglWorld* world, const char* libraryName); | |||
| Note that this closes the Vulkan library, so no Vulkan objects or API may be | |||
| used after this is called. | |||
| */ | |||
| PUGL_API | |||
| void | |||
| PUGL_API void | |||
| puglFreeVulkanLoader(PuglVulkanLoader* loader); | |||
| /** | |||
| @@ -90,8 +88,7 @@ puglFreeVulkanLoader(PuglVulkanLoader* loader); | |||
| @return Null if the Vulkan library does not contain this function (which is | |||
| unlikely and indicates a broken system). | |||
| */ | |||
| PUGL_API | |||
| PFN_vkGetInstanceProcAddr | |||
| PUGL_API PFN_vkGetInstanceProcAddr | |||
| puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader); | |||
| /** | |||
| @@ -100,8 +97,7 @@ puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader); | |||
| @return Null if the Vulkan library does not contain this function (which is | |||
| unlikely and indicates a broken system). | |||
| */ | |||
| PUGL_API | |||
| PFN_vkGetDeviceProcAddr | |||
| PUGL_API PFN_vkGetDeviceProcAddr | |||
| puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader); | |||
| /** | |||
| @@ -113,8 +109,7 @@ puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader); | |||
| @param[out] count The number of extensions in the returned array. | |||
| @return An array of extension name strings. | |||
| */ | |||
| PUGL_API | |||
| const char* const* | |||
| PUGL_API const char* const* | |||
| puglGetInstanceExtensions(uint32_t* count); | |||
| /** | |||
| @@ -127,8 +122,7 @@ puglGetInstanceExtensions(uint32_t* count); | |||
| @param[out] surface Pointed to a newly created Vulkan surface. | |||
| @return `VK_SUCCESS` on success, or a Vulkan error code. | |||
| */ | |||
| PUGL_API | |||
| VkResult | |||
| PUGL_API VkResult | |||
| puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||
| PuglView* view, | |||
| VkInstance instance, | |||
| @@ -140,8 +134,7 @@ puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, | |||
| Pass the returned value to puglSetBackend() to draw to a view with Vulkan. | |||
| */ | |||
| PUGL_CONST_API | |||
| const PuglBackend* | |||
| PUGL_CONST_API const PuglBackend* | |||
| puglVulkanBackend(void); | |||
| /** | |||
| @@ -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, | |||
| @@ -4,13 +4,11 @@ | |||
| // Common implementations of public API functions in the core library | |||
| #include "internal.h" | |||
| #include "platform.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <limits.h> | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #include <stdlib.h> | |||
| @@ -27,6 +25,7 @@ puglStrerror(const PuglStatus status) | |||
| case PUGL_BAD_BACKEND: return "Invalid or missing backend"; | |||
| case PUGL_BAD_CONFIGURATION: return "Invalid view configuration"; | |||
| case PUGL_BAD_PARAMETER: return "Invalid parameter"; | |||
| case PUGL_BAD_CALL: return "Invalid call"; | |||
| case PUGL_BACKEND_FAILED: return "Backend initialisation failed"; | |||
| case PUGL_REGISTRATION_FAILED: return "Class registration failed"; | |||
| case PUGL_REALIZE_FAILED: return "View creation failed"; | |||
| @@ -106,27 +105,37 @@ puglGetWorldString(const PuglWorld* const world, const PuglStringHint key) | |||
| } | |||
| static void | |||
| puglSetDefaultHints(PuglHints hints) | |||
| puglSetDefaultHints(PuglView* const view) | |||
| { | |||
| hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; | |||
| hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; | |||
| hints[PUGL_CONTEXT_VERSION_MINOR] = 0; | |||
| hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; | |||
| hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; | |||
| hints[PUGL_RED_BITS] = 8; | |||
| hints[PUGL_GREEN_BITS] = 8; | |||
| hints[PUGL_BLUE_BITS] = 8; | |||
| hints[PUGL_ALPHA_BITS] = 8; | |||
| hints[PUGL_DEPTH_BITS] = 0; | |||
| hints[PUGL_STENCIL_BITS] = 0; | |||
| hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; | |||
| hints[PUGL_SAMPLES] = 0; | |||
| hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; | |||
| hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; | |||
| hints[PUGL_RESIZABLE] = PUGL_FALSE; | |||
| hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; | |||
| hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; | |||
| hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_CONTEXT_API] = PUGL_OPENGL_API; | |||
| view->hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; | |||
| view->hints[PUGL_CONTEXT_VERSION_MINOR] = 0; | |||
| view->hints[PUGL_CONTEXT_PROFILE] = PUGL_OPENGL_CORE_PROFILE; | |||
| view->hints[PUGL_CONTEXT_DEBUG] = PUGL_FALSE; | |||
| view->hints[PUGL_RED_BITS] = 8; | |||
| view->hints[PUGL_GREEN_BITS] = 8; | |||
| view->hints[PUGL_BLUE_BITS] = 8; | |||
| view->hints[PUGL_ALPHA_BITS] = 8; | |||
| view->hints[PUGL_DEPTH_BITS] = 0; | |||
| view->hints[PUGL_STENCIL_BITS] = 0; | |||
| view->hints[PUGL_SAMPLE_BUFFERS] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_SAMPLES] = 0; | |||
| view->hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; | |||
| view->hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_RESIZABLE] = PUGL_FALSE; | |||
| view->hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; | |||
| view->hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; | |||
| view->hints[PUGL_VIEW_TYPE] = PUGL_DONT_CARE; | |||
| for (unsigned i = 0U; i < PUGL_NUM_POSITION_HINTS; ++i) { | |||
| view->positionHints[i].x = INT16_MIN; | |||
| view->positionHints[i].y = INT16_MIN; | |||
| } | |||
| for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { | |||
| view->sizeHints[i].width = 0U; | |||
| view->sizeHints[i].height = 0U; | |||
| } | |||
| } | |||
| PuglView* | |||
| @@ -138,13 +147,8 @@ puglNewView(PuglWorld* const world) | |||
| return NULL; | |||
| } | |||
| view->world = world; | |||
| view->sizeHints[PUGL_MIN_SIZE].width = 1; | |||
| view->sizeHints[PUGL_MIN_SIZE].height = 1; | |||
| view->defaultX = INT_MIN; | |||
| view->defaultY = INT_MIN; | |||
| puglSetDefaultHints(view->hints); | |||
| view->world = world; | |||
| puglSetDefaultHints(view); | |||
| // Enlarge world view list | |||
| const size_t newNumViews = world->numViews + 1U; | |||
| @@ -287,43 +291,84 @@ puglGetViewString(const PuglView* const view, const PuglStringHint key) | |||
| return view->strings[key]; | |||
| } | |||
| PuglRect | |||
| puglGetFrame(const PuglView* view) | |||
| PuglPoint | |||
| puglGetPositionHint(const PuglView* const view, const PuglPositionHint hint) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Return the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| if (hint == PUGL_CURRENT_POSITION) { | |||
| PuglPoint pos = {0, 0}; | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| pos.x = view->lastConfigure.x; | |||
| pos.y = view->lastConfigure.y; | |||
| } else { | |||
| const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; | |||
| if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { | |||
| pos.x = defaultPos.x; | |||
| pos.y = defaultPos.y; | |||
| } | |||
| } | |||
| return pos; | |||
| } | |||
| // Get the default position if set, or fallback to (0, 0) | |||
| int x = view->defaultX; | |||
| int y = view->defaultY; | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| x = 0; | |||
| y = 0; | |||
| return view->positionHints[hint]; | |||
| } | |||
| PuglStatus | |||
| puglSetPositionHint(PuglView* const view, | |||
| const PuglPositionHint hint, | |||
| const int x, | |||
| const int y) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| // Return the default frame, sanitized if necessary | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| if (x <= INT16_MIN || x > INT16_MAX || y <= INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->positionHints[hint].x = (PuglCoord)x; | |||
| view->positionHints[hint].y = (PuglCoord)y; | |||
| return (hint != PUGL_CURRENT_POSITION) ? PUGL_SUCCESS | |||
| : puglGetNativeView(view) ? puglSetWindowPosition(view, x, y) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PuglArea | |||
| puglGetSizeHint(const PuglView* const view, const PuglSizeHint hint) | |||
| { | |||
| if (hint == PUGL_CURRENT_SIZE && view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| const PuglArea area = {view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return area; | |||
| } | |||
| return view->sizeHints[hint]; | |||
| } | |||
| PuglStatus | |||
| puglSetParentWindow(PuglView* view, PuglNativeView parent) | |||
| puglSetParent(PuglView* view, PuglNativeView parent) | |||
| { | |||
| view->parent = parent; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const PuglStatus st = puglStoreSizeHint(view, hint, width, height); | |||
| return st ? st | |||
| : (hint != PUGL_CURRENT_SIZE) ? puglApplySizeHint(view, hint) | |||
| : puglGetNativeView(view) ? puglSetWindowSize(view, width, height) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PuglNativeView | |||
| puglGetParentWindow(const PuglView* const view) | |||
| puglGetParent(const PuglView* const view) | |||
| { | |||
| return view->parent; | |||
| } | |||
| @@ -1,23 +1,84 @@ | |||
| // Copyright 2012-2023 David Robillard <d@drobilla.net> | |||
| // Copyright 2012-2025 David Robillard <d@drobilla.net> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "internal.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <assert.h> | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| static PuglPoint | |||
| make_point(const PuglCoord x, const PuglCoord y) | |||
| { | |||
| const PuglPoint point = {x, y}; | |||
| return point; | |||
| } | |||
| bool | |||
| puglIsValidPosition(const int x, const int y) | |||
| { | |||
| // INT16_MIN is a sentinel, INT16_MAX is impossible with non-zero size | |||
| return x > INT16_MIN && x < INT16_MAX && y > INT16_MIN && y < INT16_MAX; | |||
| } | |||
| bool | |||
| puglIsValidSize(const PuglViewSize size) | |||
| puglIsValidSize(const unsigned width, const unsigned height) | |||
| { | |||
| return width && height && width <= INT16_MAX && height <= INT16_MAX; | |||
| } | |||
| bool | |||
| puglIsValidArea(const PuglArea size) | |||
| { | |||
| return size.width && size.height; | |||
| } | |||
| PuglArea | |||
| puglGetInitialSize(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured size | |||
| const PuglConfigureEvent config = view->lastConfigure; | |||
| const PuglArea size = {config.width, config.height}; | |||
| return size; | |||
| } | |||
| // Use the default size hint set by the application | |||
| return view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| } | |||
| PuglPoint | |||
| puglGetInitialPosition(const PuglView* const view, const PuglArea size) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| return make_point(view->lastConfigure.x, view->lastConfigure.y); | |||
| } | |||
| const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION]; | |||
| if (puglIsValidPosition(defaultPos.x, defaultPos.y)) { | |||
| // Use the default position hint set by the application | |||
| return make_point(defaultPos.x, defaultPos.y); | |||
| } | |||
| if (view->parent) { | |||
| // Default to the top/left origin of the parent | |||
| return make_point(0, 0); | |||
| } | |||
| // Center frame on a transient ancestor, or failing that, the screen | |||
| const PuglPoint center = puglGetAncestorCenter(view); | |||
| const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)), | |||
| (PuglCoord)(center.y - (size.height / 2))}; | |||
| return pos; | |||
| } | |||
| void | |||
| puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) | |||
| { | |||
| @@ -68,6 +129,25 @@ puglSetString(char** dest, const char* string) | |||
| } | |||
| } | |||
| PuglStatus | |||
| puglStoreSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (!puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = (PuglSpan)width; | |||
| view->sizeHints[hint].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| uint32_t | |||
| puglDecodeUTF8(const uint8_t* buf) | |||
| { | |||
| @@ -159,11 +239,12 @@ puglPreRealize(PuglView* const view) | |||
| } | |||
| // Ensure that the default size is set to a valid size | |||
| if (!puglIsValidSize(view->sizeHints[PUGL_DEFAULT_SIZE])) { | |||
| if (!puglIsValidArea(view->sizeHints[PUGL_DEFAULT_SIZE])) { | |||
| return PUGL_BAD_CONFIGURATION; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return (view->world->state == PUGL_WORLD_EXPOSING) ? PUGL_BAD_CALL | |||
| : PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| @@ -173,30 +254,10 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) | |||
| type == PUGL_UPDATE || type == PUGL_CLOSE || type == PUGL_LOOP_ENTER || | |||
| type == PUGL_LOOP_LEAVE); | |||
| const PuglEvent event = {{type, 0}}; | |||
| const PuglEvent event = {{type, 0U}}; | |||
| return puglDispatchEvent(view, &event); | |||
| } | |||
| static inline bool | |||
| puglMustConfigure(PuglView* view, const PuglConfigureEvent* configure) | |||
| { | |||
| return !!memcmp(configure, &view->lastConfigure, sizeof(PuglConfigureEvent)); | |||
| } | |||
| PuglStatus | |||
| puglConfigure(PuglView* view, const PuglEvent* event) | |||
| { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| assert(event->type == PUGL_CONFIGURE); | |||
| if (puglMustConfigure(view, &event->configure)) { | |||
| st = view->eventFunc(view, event); | |||
| view->lastConfigure = event->configure; | |||
| } | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| @@ -226,12 +287,8 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
| break; | |||
| case PUGL_CONFIGURE: | |||
| if (puglMustConfigure(view, &event->configure)) { | |||
| if (!(st0 = view->backend->enter(view, NULL))) { | |||
| st0 = puglConfigure(view, event); | |||
| st1 = view->backend->leave(view, NULL); | |||
| } | |||
| } | |||
| st0 = view->eventFunc(view, event); | |||
| view->lastConfigure = event->configure; | |||
| if (view->stage == PUGL_VIEW_STAGE_REALIZED) { | |||
| view->stage = PUGL_VIEW_STAGE_CONFIGURED; | |||
| } | |||
| @@ -240,8 +297,12 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
| case PUGL_EXPOSE: | |||
| assert(view->stage == PUGL_VIEW_STAGE_CONFIGURED); | |||
| if (!(st0 = view->backend->enter(view, &event->expose))) { | |||
| st0 = view->eventFunc(view, event); | |||
| st1 = view->backend->leave(view, &event->expose); | |||
| const PuglWorldState old_state = view->world->state; | |||
| view->world->state = PUGL_WORLD_EXPOSING; | |||
| st0 = view->eventFunc(view, event); | |||
| view->world->state = old_state; | |||
| st1 = view->backend->leave(view, &event->expose); | |||
| } | |||
| break; | |||
| @@ -6,11 +6,10 @@ | |||
| #ifndef PUGL_INTERNAL_H | |||
| #define PUGL_INTERNAL_H | |||
| #include "attributes.h" | |||
| #include "types.h" | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <stdbool.h> | |||
| #include <stddef.h> | |||
| @@ -18,9 +17,29 @@ | |||
| PUGL_BEGIN_DECLS | |||
| /// Return true if `x`,`y` is a valid position | |||
| bool | |||
| puglIsValidPosition(int x, int y); | |||
| /// Return true if `width`,`height` is a valid position | |||
| bool | |||
| puglIsValidSize(unsigned width, unsigned height); | |||
| /// Return true if `size` is a valid view size | |||
| bool | |||
| puglIsValidSize(PuglViewSize size); | |||
| puglIsValidArea(PuglArea size); | |||
| /// Return the center point of some "soft" ancestor (parent window or screen) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* view); | |||
| /// Return the initial size of a view | |||
| PuglArea | |||
| puglGetInitialSize(const PuglView* view); | |||
| /// Return the initial position of a view if known, or an invalid position | |||
| PuglPoint | |||
| puglGetInitialPosition(const PuglView* view, PuglArea size); | |||
| /// Set hint to a default value if it is unset (PUGL_DONT_CARE) | |||
| void | |||
| @@ -34,8 +53,14 @@ puglSetBlob(PuglBlob* dest, const void* data, size_t len); | |||
| void | |||
| puglSetString(char** dest, const char* string); | |||
| /// Store `width` and `height` as the current value of a size `hint` | |||
| PuglStatus | |||
| puglStoreSizeHint(PuglView* view, | |||
| PuglSizeHint hint, | |||
| unsigned width, | |||
| unsigned height); | |||
| /// Handle a changed string property | |||
| PUGL_API | |||
| PuglStatus | |||
| puglViewStringChanged(PuglView* view, PuglStringHint key, const char* value); | |||
| @@ -55,11 +80,6 @@ puglPreRealize(PuglView* view); | |||
| PuglStatus | |||
| puglDispatchSimpleEvent(PuglView* view, PuglEventType type); | |||
| /// Process configure event while already in the graphics context | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus | |||
| puglConfigure(PuglView* view, const PuglEvent* event); | |||
| /// Dispatch `event` to `view`, entering graphics context if necessary | |||
| PuglStatus | |||
| puglDispatchEvent(PuglView* view, const PuglEvent* event); | |||
| @@ -5,7 +5,7 @@ | |||
| #ifndef PUGL_SRC_MAC_H | |||
| #define PUGL_SRC_MAC_H | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -7,9 +7,10 @@ | |||
| #include "mac.h" | |||
| #include "internal.h" | |||
| #include "macros.h" | |||
| #include "platform.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -121,7 +122,7 @@ viewScreen(const PuglView* view) | |||
| } | |||
| static NSRect | |||
| nsRectToPoints(PuglView* view, const NSRect rect) | |||
| nsRectToPoints(const PuglView* view, const NSRect rect) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) | |||
| } | |||
| static NSRect | |||
| nsRectFromPoints(PuglView* view, const NSRect rect) | |||
| nsRectFromPoints(const PuglView* view, const NSRect rect) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -143,21 +144,15 @@ nsRectFromPoints(PuglView* view, const NSRect rect) | |||
| } | |||
| static NSPoint | |||
| nsPointFromPoints(PuglView* view, const NSPoint point) | |||
| nsPointFromPoints(const PuglView* view, const NSPoint point) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor); | |||
| } | |||
| static NSRect | |||
| rectToNsRect(const PuglRect rect) | |||
| { | |||
| return NSMakeRect(rect.x, rect.y, rect.width, rect.height); | |||
| } | |||
| static NSSize | |||
| sizePoints(PuglView* view, const double width, const double height) | |||
| sizePoints(PuglView* view, const PuglSpan width, const PuglSpan height) | |||
| { | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -188,7 +183,7 @@ getCurrentViewStyleFlags(PuglView* const view) | |||
| } | |||
| static PuglStatus | |||
| dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| dispatchCurrentChildViewConfiguration(PuglView* const view) | |||
| { | |||
| const NSRect framePt = [view->impl->wrapperView frame]; | |||
| const NSRect framePx = nsRectFromPoints(view, framePt); | |||
| @@ -197,14 +192,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (drawViewResize) { | |||
| const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size]; | |||
| [view->impl->drawView setFrameSize:sizePt]; | |||
| } | |||
| const PuglConfigureEvent ev = { | |||
| PUGL_CONFIGURE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)framePx.origin.x, | |||
| (PuglCoord)framePx.origin.y, | |||
| (PuglSpan)framePx.size.width, | |||
| @@ -254,7 +244,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| const PuglConfigureEvent ev = { | |||
| PUGL_CONFIGURE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)contentPx.origin.x, | |||
| (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), | |||
| (PuglSpan)contentPx.size.width, | |||
| @@ -322,7 +312,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| if (puglview->impl->window) { | |||
| [puglview->impl->window dispatchCurrentConfiguration]; | |||
| } else { | |||
| dispatchCurrentChildViewConfiguration(puglview, true); | |||
| dispatchCurrentChildViewConfiguration(puglview); | |||
| } | |||
| reshaped = false; | |||
| } | |||
| @@ -336,7 +326,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| const PuglExposeEvent ev = { | |||
| PUGL_EXPOSE, | |||
| 0, | |||
| 0U, | |||
| (PuglCoord)(rect.origin.x * scaleFactor), | |||
| (PuglCoord)viewY, | |||
| (PuglSpan)(rect.size.width * scaleFactor), | |||
| @@ -350,9 +340,9 @@ dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
| - (NSSize)intrinsicContentSize | |||
| { | |||
| const PuglViewSize defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| const PuglArea defaultSize = puglview->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| return puglIsValidSize(defaultSize) | |||
| return puglIsValidArea(defaultSize) | |||
| ? sizePoints(puglview, defaultSize.width, defaultSize.height) | |||
| : NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); | |||
| } | |||
| @@ -468,7 +458,7 @@ keySymToSpecial(const NSEvent* const ev) | |||
| return PUGL_KEY_PAD_9; | |||
| } | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| - (void)updateTrackingAreas | |||
| @@ -501,7 +491,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglCrossingEvent ev = { | |||
| type, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -542,7 +532,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglMotionEvent ev = { | |||
| PUGL_MOTION, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -577,7 +567,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglButtonEvent ev = { | |||
| PUGL_BUTTON_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -598,7 +588,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| const PuglButtonEvent ev = { | |||
| PUGL_BUTTON_RELEASE, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -638,11 +628,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const NSPoint wloc = [self eventLocation:event]; | |||
| const NSPoint rloc = [NSEvent mouseLocation]; | |||
| double dx = -[event scrollingDeltaX] / 2.0; | |||
| double dy = [event scrollingDeltaY] / 2.0; | |||
| double dx = -[event scrollingDeltaX]; | |||
| double dy = [event scrollingDeltaY]; | |||
| if ([event hasPreciseScrollingDeltas]) { | |||
| dx /= 10.0; | |||
| dy /= 10.0; | |||
| dx /= 20.0; | |||
| dy /= 20.0; | |||
| } | |||
| const PuglScrollDirection dir = | |||
| @@ -657,7 +647,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglScrollEvent ev = { | |||
| PUGL_SCROLL, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -689,7 +679,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglKeyEvent ev = { | |||
| PUGL_KEY_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -720,7 +710,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| const PuglKeyEvent ev = { | |||
| PUGL_KEY_RELEASE, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -832,7 +822,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) | |||
| PuglTextEvent ev = { | |||
| PUGL_TEXT, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -861,7 +851,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| - (void)flagsChanged:(NSEvent*)event | |||
| { | |||
| const uint32_t mods = getModifiers(event); | |||
| PuglKey special = (PuglKey)0; | |||
| PuglKey special = PUGL_KEY_NONE; | |||
| const uint16_t keyCode = [event keyCode]; | |||
| if (flagDiffers(mods, puglview->impl->mods, PUGL_MOD_SHIFT)) { | |||
| @@ -882,7 +872,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| const bool release = [event type] == NSEventTypeKeyUp; | |||
| const PuglKeyEvent ev = {release ? PUGL_KEY_RELEASE : PUGL_KEY_PRESS, | |||
| 0, | |||
| 0U, | |||
| [event timestamp], | |||
| wloc.x, | |||
| wloc.y, | |||
| @@ -924,7 +914,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| - (void)timerTick:(NSTimer*)userTimer | |||
| { | |||
| const NSNumber* userInfo = userTimer.userInfo; | |||
| const PuglTimerEvent ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue}; | |||
| const PuglTimerEvent ev = {PUGL_TIMER, 0U, userInfo.unsignedLongValue}; | |||
| PuglEvent timerEvent; | |||
| timerEvent.timer = ev; | |||
| @@ -989,7 +979,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| { | |||
| (void)notification; | |||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0}}; | |||
| PuglEvent ev = {{PUGL_FOCUS_IN, 0U}}; | |||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | |||
| puglDispatchEvent(window->puglview, &ev); | |||
| } | |||
| @@ -998,7 +988,7 @@ flagDiffers(const uint32_t lhs, const uint32_t rhs, const uint32_t mask) | |||
| { | |||
| (void)notification; | |||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0}}; | |||
| PuglEvent ev = {{PUGL_FOCUS_OUT, 0U}}; | |||
| ev.focus.mode = PUGL_CROSSING_NORMAL; | |||
| puglDispatchEvent(window->puglview, &ev); | |||
| } | |||
| @@ -1117,17 +1107,18 @@ puglConstraint(const id item, | |||
| constant:(CGFloat)constant]; | |||
| } | |||
| static PuglStatus | |||
| updateSizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| { | |||
| const PuglSpan width = view->sizeHints[hint].width; | |||
| const PuglSpan height = view->sizeHints[hint].height; | |||
| if (!puglIsValidSize(view->sizeHints[hint])) { | |||
| if (!puglIsValidArea(view->sizeHints[hint])) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| switch (hint) { | |||
| case PUGL_DEFAULT_SIZE: | |||
| case PUGL_CURRENT_SIZE: | |||
| break; | |||
| case PUGL_MIN_SIZE: | |||
| @@ -1150,38 +1141,18 @@ updateSizeHint(PuglView* const view, const PuglSizeHint hint) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| static void | |||
| updateSizeHints(PuglView* const view) | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const view) | |||
| { | |||
| for (unsigned i = 0U; i < PUGL_NUM_SIZE_HINTS; ++i) { | |||
| updateSizeHint(view, (PuglSizeHint)i); | |||
| puglApplySizeHint(view, (PuglSizeHint)i); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| } | |||
| // Get a bounding rect from the transient parent or the screen | |||
| const NSScreen* const screen = viewScreen(view); | |||
| const NSRect boundsPt = | |||
| rectFromScreen(screen, | |||
| @@ -1189,17 +1160,11 @@ getInitialFrame(PuglView* const view) | |||
| ? [[(const NSView*)view->transientParent window] frame] | |||
| : [screen frame]); | |||
| // Center the frame around the center of the bounding rectangle | |||
| const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| const NSRect boundsPx = nsRectFromPoints(view, boundsPt); | |||
| const double centerX = boundsPx.origin.x + boundsPx.size.width / 2; | |||
| const double centerY = boundsPx.origin.y + boundsPx.size.height / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)), | |||
| (PuglCoord)(centerY - (defaultSize.height / 2U)), | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width, | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height}; | |||
| return frame; | |||
| const NSRect boundsPx = nsRectFromPoints(view, boundsPt); | |||
| const PuglPoint center = { | |||
| (PuglCoord)(boundsPx.origin.x + (boundsPx.size.width / 2.0)), | |||
| (PuglCoord)(boundsPx.origin.y + (boundsPx.size.height / 2.0))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -1247,11 +1212,12 @@ puglRealize(PuglView* view) | |||
| CVDisplayLinkRelease(link); | |||
| } | |||
| // Get the initial frame to use from the defaults or last configuration | |||
| const PuglRect initialFrame = getInitialFrame(view); | |||
| // Get the initial size and position from the defaults or last configuration | |||
| const PuglArea size = puglGetInitialSize(view); | |||
| const PuglPoint pos = puglGetInitialPosition(view, size); | |||
| // Convert frame to points | |||
| const NSRect framePx = rectToNsRect(initialFrame); | |||
| const NSRect framePx = NSMakeRect(pos.x, pos.y, size.width, size.height); | |||
| const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, | |||
| framePx.origin.y / scaleFactor, | |||
| framePx.size.width / scaleFactor, | |||
| @@ -1277,7 +1243,7 @@ puglRealize(PuglView* view) | |||
| NSLayoutRelationGreaterThanOrEqual, | |||
| view->sizeHints[PUGL_MIN_SIZE].height)]; | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| [impl->wrapperView | |||
| addConstraint:puglConstraint(impl->wrapperView, | |||
| NSLayoutAttributeWidth, | |||
| @@ -1337,10 +1303,19 @@ puglRealize(PuglView* view) | |||
| ((NSWindow*)window).delegate = | |||
| [[PuglWindowDelegate alloc] initWithPuglWindow:window]; | |||
| // Set basic window hints and attributes | |||
| puglSetFrame(view, initialFrame); | |||
| // Set window frame | |||
| const NSRect screenPt = rectToScreen(screen, framePt); | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| // Resize views and move them to (0, 0) | |||
| const NSRect sizePx = {{0, 0}, {framePx.size.width, framePx.size.height}}; | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:sizePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| puglSetTransientParent(view, view->transientParent); | |||
| updateSizeHints(view); | |||
| puglUpdateSizeHints(view); | |||
| [window setContentView:impl->wrapperView]; | |||
| [view->world->impl->app activateIgnoringOtherApps:YES]; | |||
| @@ -1393,24 +1368,23 @@ puglUnrealize(PuglView* const view) | |||
| PuglStatus | |||
| puglShow(PuglView* view, const PuglShowCommand command) | |||
| { | |||
| if (!view->impl->wrapperView) { | |||
| const PuglStatus st = puglRealize(view); | |||
| if (st) { | |||
| return st; | |||
| } | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->wrapperView ? PUGL_SUCCESS : puglRealize(view); | |||
| if (st || !impl->wrapperView) { | |||
| return st; | |||
| } | |||
| NSWindow* const window = [view->impl->wrapperView window]; | |||
| NSWindow* const window = [impl->wrapperView window]; | |||
| if (![window isVisible]) { | |||
| [window setIsVisible:YES]; | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| [impl->drawView setNeedsDisplay:YES]; | |||
| } | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| [window orderFront:view->impl->wrapperView]; | |||
| [window orderFront:impl->wrapperView]; | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| [window orderFrontRegardless]; | |||
| @@ -1583,6 +1557,10 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||
| PuglStatus | |||
| puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| if (!view->impl->window || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| if (event->type == PUGL_CLIENT) { | |||
| PuglEvent copiedEvent = *event; | |||
| @@ -1610,18 +1588,17 @@ puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* view) | |||
| { | |||
| return puglPollEvents(view->world, -1.0); | |||
| } | |||
| #endif | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* world, const double timeout) | |||
| { | |||
| @autoreleasepool { | |||
| const PuglWorldState startState = world->state; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (world->type == PUGL_PROGRAM) { | |||
| NSDate* date = | |||
| ((timeout < 0) ? [NSDate distantFuture] | |||
| @@ -1650,19 +1627,13 @@ puglUpdate(PuglWorld* world, const double timeout) | |||
| [view->impl->drawView displayIfNeeded]; | |||
| } | |||
| world->state = startState; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| return puglDispatchEvents(view->world); | |||
| } | |||
| #endif | |||
| double | |||
| puglGetTime(const PuglWorld* world) | |||
| { | |||
| @@ -1674,28 +1645,51 @@ puglGetTime(const PuglWorld* world) | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* view) | |||
| puglObscureView(PuglView* view) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const NSRect rectPx = { | |||
| {(double)rect.x, | |||
| (double)view->lastConfigure.height - (rect.y + rect.height)}, | |||
| {(double)rect.width, (double)rect.height}, | |||
| }; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| const PuglSpan viewHeight = view->lastConfigure.height; | |||
| const int cx = MAX(0, x); | |||
| const int cy = MAX(0, viewHeight - y - (int)height); | |||
| const unsigned cw = MIN(view->lastConfigure.width, width); | |||
| const unsigned ch = MIN(view->lastConfigure.height, height); | |||
| if (cw == view->lastConfigure.width && ch == view->lastConfigure.height) { | |||
| [view->impl->drawView setNeedsDisplay:YES]; | |||
| } else { | |||
| const NSRect rectPx = NSMakeRect(cx, cy, cw, ch); | |||
| [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; | |||
| [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglNativeView | |||
| puglGetNativeView(PuglView* view) | |||
| puglGetNativeView(const PuglView* view) | |||
| { | |||
| return (PuglNativeView)view->impl->wrapperView; | |||
| } | |||
| @@ -1735,107 +1729,35 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* view, const PuglRect frame) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| const NSRect framePx = rectToNsRect(frame); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (impl->window) { | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| // Move and resize window to fit new content rect | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| // Resize views | |||
| const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:sizePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| [impl->window dispatchCurrentConfiguration]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| // Resize view | |||
| const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; | |||
| [impl->wrapperView setFrame:framePt]; | |||
| [impl->drawView setFrame:sizePt]; | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| PuglInternals* const impl = view->impl; | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const PuglRect frame = {(PuglCoord)x, | |||
| (PuglCoord)y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| const NSRect framePx = | |||
| NSMakeRect(x, y, view->lastConfigure.width, view->lastConfigure.height); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| if (impl->window) { | |||
| // Adjust top-level window frame | |||
| return puglSetFrame(view, frame); | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| [impl->window setFrameOrigin:screenPt.origin]; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| // Set wrapper view origin | |||
| const NSRect framePx = rectToNsRect(frame); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| [impl->wrapperView setFrameOrigin:framePt.origin]; | |||
| // Set draw view origin | |||
| const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height); | |||
| const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; | |||
| [impl->drawView setFrameOrigin:drawPt.origin]; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| return dispatchCurrentChildViewConfiguration(view); | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| PuglInternals* const impl = view->impl; | |||
| if (!impl->wrapperView) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| if (impl->window) { | |||
| // Adjust top-level window frame | |||
| PuglRect frame = puglGetFrame(view); | |||
| frame.width = (PuglSpan)width; | |||
| frame.height = (PuglSpan)height; | |||
| return puglSetFrame(view, frame); | |||
| } | |||
| // Set wrapper view size | |||
| const double scaleFactor = [viewScreen(view) backingScaleFactor]; | |||
| @@ -1847,24 +1769,20 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; | |||
| [impl->drawView setFrameSize:drawPt.size]; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view, false); | |||
| } | |||
| if (impl->window) { | |||
| const NSRect framePx = | |||
| NSMakeRect(view->lastConfigure.x, view->lastConfigure.y, width, height); | |||
| const NSRect framePt = nsRectToPoints(view, framePx); | |||
| const NSRect screenPt = rectToScreen(viewScreen(view), framePt); | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| // Resize window to fit new content rect | |||
| const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; | |||
| [impl->window setFrame:winFrame display:NO]; | |||
| [impl->window dispatchCurrentConfiguration]; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return view->impl->window ? updateSizeHint(view, hint) : PUGL_SUCCESS; | |||
| // Dispatch new configuration | |||
| return dispatchCurrentChildViewConfiguration(view); | |||
| } | |||
| PuglStatus | |||
| @@ -1892,7 +1810,7 @@ puglPaste(PuglView* const view) | |||
| { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, | |||
| 0, | |||
| 0U, | |||
| puglGetTime(view->world), | |||
| }; | |||
| @@ -1939,7 +1857,7 @@ puglAcceptOffer(PuglView* const view, | |||
| PuglWrapperView* const wrapper = view->impl->wrapperView; | |||
| NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; | |||
| if (!pasteboard) { | |||
| return PUGL_BAD_PARAMETER; | |||
| return PUGL_UNKNOWN_ERROR; | |||
| } | |||
| const NSArray<NSPasteboardType>* const types = [pasteboard types]; | |||
| @@ -1950,13 +1868,12 @@ puglAcceptOffer(PuglView* const view, | |||
| wrapper->dragOperation = NSDragOperationCopy; | |||
| wrapper->dragTypeIndex = typeIndex; | |||
| const PuglDataEvent data = { | |||
| PUGL_DATA, 0U, puglGetTime(view->world), (uint32_t)typeIndex}; | |||
| const double now = puglGetTime(view->world); | |||
| const PuglDataEvent data = {PUGL_DATA, 0U, now, typeIndex}; | |||
| PuglEvent dataEvent; | |||
| dataEvent.data = data; | |||
| puglDispatchEvent(view, &dataEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &dataEvent); | |||
| } | |||
| const void* | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/cairo.h" | |||
| #include <pugl/cairo.h> | |||
| #include <cairo-quartz.h> | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/gl.h" | |||
| #include <pugl/gl.h> | |||
| #ifndef __MAC_10_10 | |||
| # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core | |||
| @@ -5,7 +5,7 @@ | |||
| #include "mac.h" | |||
| #include "stub.h" | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| #import <Cocoa/Cocoa.h> | |||
| @@ -8,9 +8,9 @@ | |||
| #include "stub.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include "pugl/stub.h" | |||
| #include "pugl/vulkan.h" | |||
| #include <pugl/pugl.h> | |||
| #include <pugl/stub.h> | |||
| #include <pugl/vulkan.h> | |||
| #include <vulkan/vulkan_core.h> | |||
| #include <vulkan/vulkan_macos.h> | |||
| @@ -1,20 +1,20 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2012-2025 David Robillard <d@drobilla.net> | |||
| // SPDX-License-Identifier: ISC | |||
| // The API that a platform implementation must define | |||
| // The internal API that a platform implementation must define | |||
| #ifndef PUGL_PLATFORM_H | |||
| #define PUGL_PLATFORM_H | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| PUGL_BEGIN_DECLS | |||
| /// Allocate and initialise world internals (implemented once per platform) | |||
| PUGL_MALLOC_FUNC | |||
| PuglWorldInternals* | |||
| PUGL_MALLOC_FUNC PuglWorldInternals* | |||
| puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); | |||
| /// Destroy and free world internals (implemented once per platform) | |||
| @@ -22,14 +22,29 @@ void | |||
| puglFreeWorldInternals(PuglWorld* world); | |||
| /// Allocate and initialise view internals (implemented once per platform) | |||
| PUGL_MALLOC_FUNC | |||
| PuglInternals* | |||
| PUGL_MALLOC_FUNC PuglInternals* | |||
| puglInitViewInternals(PuglWorld* world); | |||
| /// Destroy and free view internals (implemented once per platform) | |||
| void | |||
| puglFreeViewInternals(PuglView* view); | |||
| /// Adapt to the change of a size hint if necessary | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* view, PuglSizeHint hint); | |||
| /// Adapt to all configured size hints | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* view); | |||
| /// Set the current position of a view window | |||
| PuglStatus | |||
| puglSetWindowPosition(PuglView* view, int x, int y); | |||
| /// Set the current size of a view window | |||
| PuglStatus | |||
| puglSetWindowSize(PuglView* view, unsigned width, unsigned height); | |||
| PUGL_END_DECLS | |||
| #endif // PUGL_PLATFORM_H | |||
| @@ -4,7 +4,8 @@ | |||
| #ifndef PUGL_SRC_STUB_H | |||
| #define PUGL_SRC_STUB_H | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <stddef.h> | |||
| @@ -6,7 +6,7 @@ | |||
| #include "attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <stdbool.h> | |||
| #include <stddef.h> | |||
| @@ -21,24 +21,20 @@ typedef struct PuglInternalsImpl PuglInternals; | |||
| /// View hints | |||
| typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; | |||
| /// View position (both X and Y coordinates) or general point | |||
| typedef struct { | |||
| PuglCoord x; | |||
| PuglCoord y; | |||
| } PuglPoint; | |||
| /// View size (both X and Y coordinates) | |||
| typedef struct { | |||
| PuglSpan width; | |||
| PuglSpan height; | |||
| } PuglViewSize; | |||
| /// Blob of arbitrary data | |||
| typedef struct { | |||
| void* data; ///< Dynamically allocated data | |||
| size_t len; ///< Length of data in bytes | |||
| } PuglBlob; | |||
| /// State of the world in the process of an update | |||
| typedef enum { | |||
| PUGL_WORLD_IDLE, ///< Idle, not in puglUpdate() | |||
| PUGL_WORLD_UPDATING, ///< Event processing stage of puglUpdate() | |||
| PUGL_WORLD_EXPOSING, ///< Exposing stage of puglUpdate() | |||
| PUGL_WORLD_RECURSING, ///< Currently in recursive loop (Windows) | |||
| } PuglWorldState; | |||
| /// Stage of a view along its lifespan | |||
| typedef enum { | |||
| PUGL_VIEW_STAGE_ALLOCATED, | |||
| @@ -57,10 +53,9 @@ struct PuglViewImpl { | |||
| uintptr_t transientParent; | |||
| PuglConfigureEvent lastConfigure; | |||
| PuglHints hints; | |||
| PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS]; | |||
| PuglPoint positionHints[PUGL_NUM_POSITION_HINTS]; | |||
| PuglArea sizeHints[PUGL_NUM_SIZE_HINTS]; | |||
| char* strings[PUGL_NUM_STRING_HINTS]; | |||
| int defaultX; | |||
| int defaultY; | |||
| PuglViewStage stage; | |||
| bool resizing; | |||
| }; | |||
| @@ -74,6 +69,7 @@ struct PuglWorldImpl { | |||
| PuglView** views; | |||
| char* strings[PUGL_NUM_STRING_HINTS]; | |||
| PuglWorldType type; | |||
| PuglWorldState state; | |||
| }; | |||
| /// Opaque surface used by graphics backend | |||
| @@ -82,23 +78,21 @@ typedef void PuglSurface; | |||
| /// Graphics backend interface | |||
| struct PuglBackendImpl { | |||
| /// Get visual information from display and setup view as necessary | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*configure)(PuglView*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*configure)(PuglView*); | |||
| /// Create surface and drawing context | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*create)(PuglView*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*create)(PuglView*); | |||
| /// Destroy surface and drawing context | |||
| void (*destroy)(PuglView*); | |||
| /// Enter drawing context, for drawing if expose is non-null | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*enter)(PuglView*, const PuglExposeEvent*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*enter)(PuglView*, | |||
| const PuglExposeEvent*); | |||
| /// Leave drawing context, after drawing if expose is non-null | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PuglStatus (*leave)(PuglView*, const PuglExposeEvent*); | |||
| PUGL_WARN_UNUSED_RESULT PuglStatus (*leave)(PuglView*, | |||
| const PuglExposeEvent*); | |||
| /// Return the puglGetContext() handle for the application, if any | |||
| void* (*getContext)(PuglView*); | |||
| @@ -4,9 +4,10 @@ | |||
| #include "win.h" | |||
| #include "internal.h" | |||
| #include "macros.h" | |||
| #include "platform.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <dwmapi.h> | |||
| #include <windows.h> | |||
| @@ -42,13 +43,11 @@ | |||
| #define PUGL_USER_TIMER_MIN 9470 | |||
| #ifdef __cplusplus | |||
| # define PUGL_INIT_STRUCT \ | |||
| {} | |||
| #else | |||
| # define PUGL_INIT_STRUCT \ | |||
| { \ | |||
| 0 \ | |||
| } | |||
| #else | |||
| # define PUGL_INIT_STRUCT {0} | |||
| #endif | |||
| typedef BOOL(WINAPI* PFN_SetProcessDPIAware)(void); | |||
| @@ -58,8 +57,12 @@ typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| static wchar_t* | |||
| puglUtf8ToWideChar(const char* const utf8) | |||
| #ifdef UNICODE | |||
| typedef wchar_t ArgStringChar; | |||
| static ArgStringChar* | |||
| puglArgStringNew(const char* const utf8) | |||
| { | |||
| const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); | |||
| if (len > 0) { | |||
| @@ -71,6 +74,30 @@ puglUtf8ToWideChar(const char* const utf8) | |||
| return NULL; | |||
| } | |||
| static void | |||
| puglArgStringFree(ArgStringChar* const utf8) | |||
| { | |||
| free(utf8); | |||
| } | |||
| #else // !defined(UNICODE) | |||
| typedef const char ArgStringChar; | |||
| static ArgStringChar* | |||
| puglArgStringNew(const char* const utf8) | |||
| { | |||
| return utf8; | |||
| } | |||
| static void | |||
| puglArgStringFree(ArgStringChar* const utf8) | |||
| { | |||
| (void)utf8; | |||
| } | |||
| #endif | |||
| static char* | |||
| puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) | |||
| { | |||
| @@ -94,11 +121,7 @@ puglWinStatus(const BOOL success) | |||
| static bool | |||
| puglRegisterWindowClass(const char* name) | |||
| { | |||
| #ifdef UNICODE | |||
| wchar_t* const wname = puglUtf8ToWideChar(name); | |||
| #else | |||
| const char* const wname = name; | |||
| #endif | |||
| ArgStringChar* const nameArg = puglArgStringNew(name); | |||
| HMODULE module = NULL; | |||
| if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | |||
| @@ -109,7 +132,7 @@ puglRegisterWindowClass(const char* name) | |||
| } | |||
| WNDCLASSEX wc = PUGL_INIT_STRUCT; | |||
| if (GetClassInfoEx(module, wname, &wc)) { | |||
| if (GetClassInfoEx(module, nameArg, &wc)) { | |||
| return true; // Already registered | |||
| } | |||
| @@ -120,12 +143,10 @@ puglRegisterWindowClass(const char* name) | |||
| wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |||
| wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |||
| wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||
| wc.lpszClassName = wname; | |||
| wc.lpszClassName = nameArg; | |||
| const bool success = !!RegisterClassEx(&wc); | |||
| #ifdef UNICODE | |||
| free(wname); | |||
| #endif | |||
| puglArgStringFree(nameArg); | |||
| return success; | |||
| } | |||
| @@ -184,7 +205,7 @@ static double | |||
| puglWinGetViewScaleFactor(const PuglView* const view) | |||
| { | |||
| const HMODULE shcore = | |||
| LoadLibraryExA("Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| LoadLibraryEx(TEXT("Shcore.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| if (!shcore) { | |||
| return 1.0; | |||
| } | |||
| @@ -219,7 +240,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) | |||
| if (type == PUGL_PROGRAM) { | |||
| HMODULE user32 = | |||
| LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| LoadLibraryEx(TEXT("user32.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | |||
| if (user32) { | |||
| PFN_SetProcessDPIAware SetProcessDPIAware = | |||
| (PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware"); | |||
| @@ -250,20 +271,6 @@ puglInitViewInternals(PuglWorld* PUGL_UNUSED(world)) | |||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| } | |||
| static PuglStatus | |||
| puglPollWinEvents(PuglWorld* world, const double timeout) | |||
| { | |||
| (void)world; | |||
| if (timeout < 0) { | |||
| WaitMessage(); | |||
| } else { | |||
| MsgWaitForMultipleObjects( | |||
| 0, NULL, FALSE, (DWORD)(timeout * 1e3), QS_ALLEVENTS); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglRealize(PuglView* view) | |||
| { | |||
| @@ -287,9 +294,9 @@ puglRealize(PuglView* view) | |||
| puglEnsureHint(view, PUGL_ALPHA_BITS, 8); | |||
| // Get refresh rate for resize draw timer | |||
| DEVMODEA devMode; | |||
| DEVMODE devMode; | |||
| memset(&devMode, 0, sizeof(devMode)); | |||
| EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode); | |||
| EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode); | |||
| view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; | |||
| // Register window class if necessary | |||
| @@ -308,16 +315,16 @@ puglRealize(PuglView* view) | |||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | |||
| puglSetTransientParent(view, view->transientParent); | |||
| view->impl->scaleFactor = puglWinGetViewScaleFactor(view); | |||
| view->impl->cursor = LoadCursor(NULL, IDC_ARROW); | |||
| impl->scaleFactor = puglWinGetViewScaleFactor(view); | |||
| impl->cursor = LoadCursor(NULL, IDC_ARROW); | |||
| if (view->hints[PUGL_DARK_FRAME]) { | |||
| const BOOL useDarkMode = TRUE; | |||
| if ((DwmSetWindowAttribute(view->impl->hwnd, | |||
| if ((DwmSetWindowAttribute(impl->hwnd, | |||
| DWMWA_USE_IMMERSIVE_DARK_MODE, | |||
| &useDarkMode, | |||
| sizeof(useDarkMode)) != S_OK)) { | |||
| DwmSetWindowAttribute(view->impl->hwnd, | |||
| DwmSetWindowAttribute(impl->hwnd, | |||
| PRE_20H1_DWMWA_USE_IMMERSIVE_DARK_MODE, | |||
| &useDarkMode, | |||
| sizeof(useDarkMode)); | |||
| @@ -343,51 +350,48 @@ puglUnrealize(PuglView* const view) | |||
| view->backend->destroy(view); | |||
| } | |||
| ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||
| view->impl->hdc = NULL; | |||
| DestroyWindow(view->impl->hwnd); | |||
| view->impl->hwnd = NULL; | |||
| memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent)); | |||
| return PUGL_SUCCESS; | |||
| ReleaseDC(impl->hwnd, impl->hdc); | |||
| impl->hdc = NULL; | |||
| const PuglStatus st = puglWinStatus(DestroyWindow(impl->hwnd)); | |||
| impl->hwnd = NULL; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglShow(PuglView* view, const PuglShowCommand command) | |||
| { | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->hwnd ? PUGL_SUCCESS : puglRealize(view); | |||
| if (!impl->hwnd) { | |||
| const PuglStatus st = puglRealize(view); | |||
| if (st) { | |||
| return st; | |||
| if (!st) { | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| st = SetActiveWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| st = SetForegroundWindow(impl->hwnd) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
| break; | |||
| } | |||
| } | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNOACTIVATE); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| SetActiveWindow(impl->hwnd); | |||
| break; | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| ShowWindow(impl->hwnd, SW_SHOWNORMAL); | |||
| SetForegroundWindow(impl->hwnd); | |||
| break; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglHide(PuglView* view) | |||
| { | |||
| PuglInternals* impl = view->impl; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| ShowWindow(impl->hwnd, SW_HIDE); | |||
| ShowWindow(view->impl->hwnd, SW_HIDE); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -408,14 +412,11 @@ puglFreeViewInternals(PuglView* view) | |||
| void | |||
| puglFreeWorldInternals(PuglWorld* world) | |||
| { | |||
| #ifdef UNICODE | |||
| wchar_t* const wname = puglUtf8ToWideChar(world->strings[PUGL_CLASS_NAME]); | |||
| UnregisterClass(wname, NULL); | |||
| free(wname); | |||
| #else | |||
| UnregisterClass(world->strings[PUGL_CLASS_NAME], NULL); | |||
| #endif | |||
| const char* const className = world->strings[PUGL_CLASS_NAME]; | |||
| ArgStringChar* const classNameArg = puglArgStringNew(className); | |||
| UnregisterClass(classNameArg, NULL); | |||
| puglArgStringFree(classNameArg); | |||
| free(world->impl); | |||
| } | |||
| @@ -426,14 +427,14 @@ keyInRange(const WPARAM winSym, | |||
| const PuglKey puglMin) | |||
| { | |||
| return (winSym >= winMin && winSym <= winMax) | |||
| ? (PuglKey)(puglMin + (winSym - winMin)) | |||
| : (PuglKey)0; | |||
| ? (PuglKey)((WPARAM)puglMin + (winSym - winMin)) | |||
| : PUGL_KEY_NONE; | |||
| } | |||
| static PuglKey | |||
| keySymToSpecial(const WPARAM sym, const bool ext) | |||
| { | |||
| PuglKey key = (PuglKey)0; | |||
| PuglKey key = PUGL_KEY_NONE; | |||
| if ((key = keyInRange(sym, VK_F1, VK_F12, PUGL_KEY_F1)) || | |||
| (key = keyInRange(sym, | |||
| VK_PRIOR, | |||
| @@ -482,23 +483,26 @@ keySymToSpecial(const WPARAM sym, const bool ext) | |||
| // clang-format on | |||
| } | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| static bool | |||
| is_toggled(int vkey) | |||
| { | |||
| return (unsigned)GetKeyState(vkey) & 1U; | |||
| } | |||
| static uint32_t | |||
| getModifiers(void) | |||
| { | |||
| // clang-format off | |||
| return ( | |||
| ((GetKeyState(VK_SHIFT) < 0) ? (uint32_t)PUGL_MOD_SHIFT : 0U) | | |||
| ((GetKeyState(VK_CONTROL) < 0) ? (uint32_t)PUGL_MOD_CTRL : 0U) | | |||
| ((GetKeyState(VK_MENU) < 0) ? (uint32_t)PUGL_MOD_ALT : 0U) | | |||
| ((GetKeyState(VK_LWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | | |||
| ((GetKeyState(VK_RWIN) < 0) ? (uint32_t)PUGL_MOD_SUPER : 0U) | | |||
| ((GetKeyState(VK_NUMLOCK) & 1U) ? (uint32_t)PUGL_MOD_NUM_LOCK : 0U) | | |||
| ((GetKeyState(VK_SCROLL) & 1U) ? (uint32_t)PUGL_MOD_SCROLL_LOCK : 0U) | | |||
| ((GetKeyState(VK_CAPITAL) & 1U) ? (uint32_t)PUGL_MOD_CAPS_LOCK : 0U)); | |||
| // clang-format on | |||
| return ((uint32_t)(((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0) | | |||
| ((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0) | | |||
| ((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0) | | |||
| ((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0) | | |||
| ((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0) | | |||
| (is_toggled(VK_NUMLOCK) ? PUGL_MOD_NUM_LOCK : 0) | | |||
| (is_toggled(VK_SCROLL) ? PUGL_MOD_SCROLL_LOCK : 0) | | |||
| (is_toggled(VK_CAPITAL) ? PUGL_MOD_CAPS_LOCK : 0))); | |||
| } | |||
| static void | |||
| @@ -673,7 +677,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||
| const PuglCrossingEvent ev = { | |||
| type, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| (double)pos.x, | |||
| (double)pos.y, | |||
| @@ -683,7 +687,7 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos) | |||
| PUGL_CROSSING_NORMAL, | |||
| }; | |||
| PuglEvent crossingEvent = {{type, 0}}; | |||
| PuglEvent crossingEvent = {{type, 0U}}; | |||
| crossingEvent.crossing = ev; | |||
| puglDispatchEvent(view, &crossingEvent); | |||
| } | |||
| @@ -693,8 +697,8 @@ constrainAspect(const PuglView* const view, | |||
| RECT* const size, | |||
| const WPARAM wParam) | |||
| { | |||
| const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| const float minA = (float)minAspect.width / (float)minAspect.height; | |||
| const float maxA = (float)maxAspect.width / (float)maxAspect.height; | |||
| @@ -733,7 +737,7 @@ constrainAspect(const PuglView* const view, | |||
| static LRESULT | |||
| handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| RECT rect = {0, 0, 0, 0}; | |||
| POINT pt = {0, 0}; | |||
| MINMAXINFO* mmi = NULL; | |||
| @@ -779,8 +783,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| handleConfigure(view, &event); | |||
| break; | |||
| case WM_SIZING: | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MIN_ASPECT]) && | |||
| puglIsValidSize(view->sizeHints[PUGL_MAX_ASPECT])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MIN_ASPECT]) && | |||
| puglIsValidArea(view->sizeHints[PUGL_MAX_ASPECT])) { | |||
| constrainAspect(view, (RECT*)lParam, wParam); | |||
| return TRUE; | |||
| } | |||
| @@ -795,7 +799,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| break; | |||
| case WM_TIMER: | |||
| if (wParam >= PUGL_USER_TIMER_MIN) { | |||
| PuglEvent ev = {{PUGL_TIMER, 0}}; | |||
| PuglEvent ev = {{PUGL_TIMER, 0U}}; | |||
| ev.timer.id = wParam - PUGL_USER_TIMER_MIN; | |||
| puglDispatchEvent(view, &ev); | |||
| } | |||
| @@ -812,7 +816,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| mmi = (MINMAXINFO*)lParam; | |||
| mmi->ptMinTrackSize.x = view->sizeHints[PUGL_MIN_SIZE].width; | |||
| mmi->ptMinTrackSize.y = view->sizeHints[PUGL_MIN_SIZE].height; | |||
| if (puglIsValidSize(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| if (puglIsValidArea(view->sizeHints[PUGL_MAX_SIZE])) { | |||
| mmi->ptMaxTrackSize.x = view->sizeHints[PUGL_MAX_SIZE].width; | |||
| mmi->ptMaxTrackSize.y = view->sizeHints[PUGL_MAX_SIZE].height; | |||
| } | |||
| @@ -948,8 +952,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| PuglStatus | |||
| puglGrabFocus(PuglView* view) | |||
| { | |||
| SetFocus(view->impl->hwnd); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(!!SetFocus(view->impl->hwnd)); | |||
| } | |||
| bool | |||
| @@ -1023,20 +1026,32 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags) | |||
| const bool newMaximized = styleIsMaximized(flags); | |||
| if (oldMaximized != newMaximized) { | |||
| ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); | |||
| puglPostRedisplay(view); | |||
| puglObscureView(view); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const PUGL_UNUSED(view), | |||
| const PuglSizeHint PUGL_UNUSED(hint)) | |||
| { | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const PUGL_UNUSED(view)) | |||
| { | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglStartTimer(PuglView* view, uintptr_t id, double timeout) | |||
| { | |||
| const UINT msec = (UINT)floor(timeout * 1000.0); | |||
| return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL) | |||
| ? PUGL_SUCCESS | |||
| : PUGL_UNKNOWN_ERROR); | |||
| SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| @@ -1048,32 +1063,24 @@ puglStopTimer(PuglView* view, uintptr_t id) | |||
| PuglStatus | |||
| puglSendEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| if (!view->impl->hwnd || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| if (event->type == PUGL_CLOSE) { | |||
| PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(PostMessage(view->impl->hwnd, WM_CLOSE, 0, 0)); | |||
| } | |||
| if (event->type == PUGL_CLIENT) { | |||
| PostMessage(view->impl->hwnd, | |||
| PUGL_LOCAL_CLIENT_MSG, | |||
| (WPARAM)event->client.data1, | |||
| (LPARAM)event->client.data2); | |||
| return PUGL_SUCCESS; | |||
| return puglWinStatus(PostMessage(view->impl->hwnd, | |||
| PUGL_LOCAL_CLIENT_MSG, | |||
| (WPARAM)event->client.data1, | |||
| (LPARAM)event->client.data2)); | |||
| } | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* PUGL_UNUSED(view)) | |||
| { | |||
| WaitMessage(); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #endif | |||
| static PuglStatus | |||
| puglDispatchViewEvents(PuglView* view) | |||
| { | |||
| @@ -1118,21 +1125,31 @@ puglDispatchWinEvents(PuglWorld* world) | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* world, double timeout) | |||
| { | |||
| const double startTime = puglGetTime(world); | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| static const double minWaitSeconds = 0.002; | |||
| const double startTime = puglGetTime(world); | |||
| const PuglWorldState startState = world->state; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (timeout < 0.0) { | |||
| st = puglPollWinEvents(world, timeout); | |||
| st = st ? st : puglDispatchWinEvents(world); | |||
| } else if (timeout <= 0.001) { | |||
| WaitMessage(); | |||
| st = puglDispatchWinEvents(world); | |||
| } else if (timeout < minWaitSeconds) { | |||
| st = puglDispatchWinEvents(world); | |||
| } else { | |||
| const double endTime = startTime + timeout - 0.001; | |||
| for (double t = startTime; t < endTime; t = puglGetTime(world)) { | |||
| if ((st = puglPollWinEvents(world, endTime - t)) || | |||
| (st = puglDispatchWinEvents(world))) { | |||
| break; | |||
| } | |||
| const double endTime = startTime + timeout - minWaitSeconds; | |||
| double t = startTime; | |||
| while (!st && t < endTime) { | |||
| const DWORD timeoutMs = (DWORD)((endTime - t) * 1e3); | |||
| MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutMs, QS_ALLEVENTS); | |||
| st = puglDispatchWinEvents(world); | |||
| t = puglGetTime(world); | |||
| } | |||
| } | |||
| @@ -1144,17 +1161,10 @@ puglUpdate(PuglWorld* world, double timeout) | |||
| UpdateWindow(world->views[i]->impl->hwnd); | |||
| } | |||
| world->state = startState; | |||
| return st; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| return puglUpdate(view->world, 0.0); | |||
| } | |||
| #endif | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| @@ -1188,27 +1198,39 @@ puglGetTime(const PuglWorld* world) | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* view) | |||
| puglObscureView(PuglView* view) | |||
| { | |||
| InvalidateRect(view->impl->hwnd, NULL, false); | |||
| return PUGL_SUCCESS; | |||
| return view->world->state == PUGL_WORLD_EXPOSING | |||
| ? PUGL_BAD_CALL | |||
| : puglWinStatus(InvalidateRect(view->impl->hwnd, NULL, false)); | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* const view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const RECT r = {(long)floor(rect.x), | |||
| (long)floor(rect.y), | |||
| (long)ceil(rect.x + rect.width), | |||
| (long)ceil(rect.y + rect.height)}; | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| InvalidateRect(view->impl->hwnd, &r, false); | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| const int cx = MAX(0, x); | |||
| const int cy = MAX(0, y); | |||
| const unsigned cw = MIN(view->lastConfigure.width, width); | |||
| const unsigned ch = MIN(view->lastConfigure.height, height); | |||
| const RECT r = {cx, cy, cx + (long)cw, cy + (long)ch}; | |||
| return puglWinStatus(InvalidateRect(view->impl->hwnd, &r, false)); | |||
| } | |||
| PuglNativeView | |||
| puglGetNativeView(PuglView* view) | |||
| puglGetNativeView(const PuglView* view) | |||
| { | |||
| return (PuglNativeView)view->impl->hwnd; | |||
| } | |||
| @@ -1218,19 +1240,18 @@ puglViewStringChanged(PuglView* const view, | |||
| const PuglStringHint key, | |||
| const char* const value) | |||
| { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (!view->impl->hwnd) { | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| if (key == PUGL_WINDOW_TITLE) { | |||
| wchar_t* const wtitle = puglUtf8ToWideChar(value); | |||
| if (wtitle) { | |||
| SetWindowTextW(view->impl->hwnd, wtitle); | |||
| free(wtitle); | |||
| } | |||
| ArgStringChar* const titleArg = puglArgStringNew(value); | |||
| st = puglWinStatus(SetWindowText(view->impl->hwnd, titleArg)); | |||
| puglArgStringFree(titleArg); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| static RECT | |||
| @@ -1256,44 +1277,8 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* view, const PuglRect frame) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = | |||
| adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height); | |||
| return puglWinStatus( | |||
| SetWindowPos(view->impl->hwnd, | |||
| HWND_TOP, | |||
| rect.left, | |||
| rect.top, | |||
| rect.right - rect.left, | |||
| rect.bottom - rect.top, | |||
| SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = adjustedWindowRect( | |||
| view, x, y, view->lastConfigure.width, view->lastConfigure.height); | |||
| @@ -1305,19 +1290,10 @@ puglSetPosition(PuglView* const view, const int x, const int y) | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->hwnd) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| const RECT rect = adjustedWindowRect(view, | |||
| view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| @@ -1336,21 +1312,6 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| flags)); | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglSetTransientParent(PuglView* view, PuglNativeView parent) | |||
| { | |||
| @@ -1394,15 +1355,14 @@ puglAcceptOffer(PuglView* const view, | |||
| const PuglDataEvent data = { | |||
| PUGL_DATA, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| 0, | |||
| }; | |||
| PuglEvent dataEvent; | |||
| dataEvent.data = data; | |||
| puglDispatchEvent(view, &dataEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &dataEvent); | |||
| } | |||
| const void* | |||
| @@ -1412,8 +1372,21 @@ puglGetClipboard(PuglView* const view, | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT) || | |||
| !OpenClipboard(impl->hwnd)) { | |||
| if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT)) { | |||
| return NULL; | |||
| } | |||
| // Try to open the clipboard several times since others may have locked it | |||
| BOOL opened = FALSE; | |||
| static const unsigned max_tries = 16U; | |||
| for (unsigned i = 0U; !opened && i < max_tries; ++i) { | |||
| opened = OpenClipboard(impl->hwnd); | |||
| if (!opened) { | |||
| Sleep(0); | |||
| } | |||
| } | |||
| if (!opened) { | |||
| return NULL; | |||
| } | |||
| @@ -1424,15 +1397,14 @@ puglGetClipboard(PuglView* const view, | |||
| return NULL; | |||
| } | |||
| free(view->impl->clipboard.data); | |||
| view->impl->clipboard.data = | |||
| puglWideCharToUtf8(wstr, &view->impl->clipboard.len); | |||
| free(impl->clipboard.data); | |||
| impl->clipboard.data = puglWideCharToUtf8(wstr, &impl->clipboard.len); | |||
| GlobalUnlock(mem); | |||
| CloseClipboard(); | |||
| *len = view->impl->clipboard.len; | |||
| return view->impl->clipboard.data; | |||
| *len = impl->clipboard.len; | |||
| return impl->clipboard.data; | |||
| } | |||
| PuglStatus | |||
| @@ -1443,7 +1415,7 @@ puglSetClipboard(PuglView* const view, | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len); | |||
| PuglStatus st = puglSetBlob(&impl->clipboard, data, len); | |||
| if (st) { | |||
| return st; | |||
| } | |||
| @@ -1488,14 +1460,13 @@ puglPaste(PuglView* const view) | |||
| { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, | |||
| 0, | |||
| 0U, | |||
| GetMessageTime() / 1e3, | |||
| }; | |||
| PuglEvent offerEvent; | |||
| offerEvent.offer = offer; | |||
| puglDispatchEvent(view, &offerEvent); | |||
| return PUGL_SUCCESS; | |||
| return puglDispatchEvent(view, &offerEvent); | |||
| } | |||
| static const TCHAR* const cursor_ids[] = { | |||
| @@ -1563,42 +1534,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) | |||
| return pfd; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; | |||
| const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = { | |||
| (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; | |||
| return frame; | |||
| } | |||
| // Get a bounding rect from the "nearest" parent or parent-like window | |||
| const HWND hwnd = puglWinGetWindow(view); | |||
| RECT rect = {0, 0, 0, 0}; | |||
| GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect); | |||
| RECT rect = {0, 0, 0, 0}; | |||
| GetWindowRect(view->transientParent ? (HWND)view->transientParent | |||
| : GetDesktopWindow(), | |||
| &rect); | |||
| // Center the frame around the center of the bounding rectangle | |||
| const LONG centerX = rect.left + (rect.right - rect.left) / 2; | |||
| const LONG centerY = rect.top + (rect.bottom - rect.top) / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), | |||
| (PuglCoord)(centerY - (defaultHeight / 2)), | |||
| defaultWidth, | |||
| defaultHeight}; | |||
| return frame; | |||
| const PuglPoint center = { | |||
| (PuglCoord)(rect.left + ((rect.right - rect.left) / 2)), | |||
| (PuglCoord)(rect.top + ((rect.bottom - rect.top) / 2))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -1613,28 +1560,35 @@ puglWinCreateWindow(PuglView* const view, | |||
| PuglNativeView parent = view->parent ? view->parent : view->transientParent; | |||
| // Calculate initial window rectangle | |||
| const unsigned winFlags = puglWinGetWindowFlags(view); | |||
| const unsigned winExFlags = puglWinGetWindowExFlags(view); | |||
| const PuglRect frame = getInitialFrame(view); | |||
| RECT wr = {(long)frame.x, | |||
| (long)frame.y, | |||
| (long)frame.x + frame.width, | |||
| (long)frame.y + frame.height}; | |||
| const unsigned winFlags = puglWinGetWindowFlags(view); | |||
| const unsigned winExFlags = puglWinGetWindowExFlags(view); | |||
| const PuglArea size = puglGetInitialSize(view); | |||
| const PuglPoint pos = puglGetInitialPosition(view, size); | |||
| RECT wr = {(long)pos.x, | |||
| (long)pos.y, | |||
| (long)pos.x + size.width, | |||
| (long)pos.y + size.height}; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); | |||
| ArgStringChar* const classNameArg = puglArgStringNew(className); | |||
| ArgStringChar* const titleArg = puglArgStringNew(title); | |||
| // Create window and get drawing context | |||
| if (!(*hwnd = CreateWindowExA(winExFlags, | |||
| className, | |||
| title, | |||
| winFlags, | |||
| wr.left, | |||
| wr.right, | |||
| wr.right - wr.left, | |||
| wr.bottom - wr.top, | |||
| (HWND)parent, | |||
| NULL, | |||
| NULL, | |||
| NULL))) { | |||
| *hwnd = CreateWindowEx(winExFlags, | |||
| classNameArg, | |||
| titleArg, | |||
| winFlags, | |||
| wr.left, | |||
| wr.right, | |||
| wr.right - wr.left, | |||
| wr.bottom - wr.top, | |||
| (HWND)parent, | |||
| NULL, | |||
| NULL, | |||
| NULL); | |||
| puglArgStringFree(titleArg); | |||
| puglArgStringFree(classNameArg); | |||
| if (!*hwnd) { | |||
| return PUGL_REALIZE_FAILED; | |||
| } | |||
| @@ -1660,7 +1614,6 @@ puglWinConfigure(PuglView* view) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { | |||
| return st; | |||
| } | |||
| @@ -1673,17 +1626,17 @@ puglWinConfigure(PuglView* view) | |||
| DestroyWindow(impl->hwnd); | |||
| impl->hwnd = NULL; | |||
| impl->hdc = NULL; | |||
| return PUGL_SET_FORMAT_FAILED; | |||
| st = PUGL_SET_FORMAT_FAILED; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglStatus | |||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose) | |||
| { | |||
| if (expose) { | |||
| BeginPaint(view->impl->hwnd, &view->impl->paint); | |||
| return puglWinStatus(!!BeginPaint(view->impl->hwnd, &view->impl->paint)); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| @@ -6,7 +6,7 @@ | |||
| #include "internal.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <windows.h> | |||
| @@ -37,28 +37,19 @@ struct PuglInternalsImpl { | |||
| bool fullscreen; | |||
| }; | |||
| PUGL_API | |||
| PuglWinPFD | |||
| PUGL_API PuglWinPFD | |||
| puglWinGetPixelFormatDescriptor(const PuglHints hints); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinCreateWindow(PuglView* view, const char* title, HWND* hwnd, HDC* hdc); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinConfigure(PuglView* view); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinEnter(PuglView* view, const PuglExposeEvent* expose); | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglWinLeave(PuglView* view, const PuglExposeEvent* expose); | |||
| #endif // PUGL_SRC_WIN_H | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/cairo.h" | |||
| #include <pugl/cairo.h> | |||
| #include <cairo-win32.h> | |||
| #include <cairo.h> | |||
| @@ -27,7 +27,7 @@ puglWinCairoCreateDrawContext(PuglView* view) | |||
| surface->drawDc = CreateCompatibleDC(impl->hdc); | |||
| surface->drawBitmap = CreateCompatibleBitmap( | |||
| impl->hdc, (int)view->lastConfigure.width, (int)view->lastConfigure.height); | |||
| impl->hdc, view->lastConfigure.width, view->lastConfigure.height); | |||
| DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap)); | |||
| @@ -106,12 +106,11 @@ puglWinCairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||
| { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (expose && !(st = puglWinCairoCreateDrawContext(view)) && | |||
| !(st = puglWinCairoOpen(view))) { | |||
| st = puglWinEnter(view, expose); | |||
| if (expose && !(st = puglWinCairoCreateDrawContext(view))) { | |||
| st = puglWinCairoOpen(view); | |||
| } | |||
| return st; | |||
| return st ? st : puglWinEnter(view, expose); | |||
| } | |||
| static PuglStatus | |||
| @@ -125,8 +124,8 @@ puglWinCairoLeave(PuglView* view, const PuglExposeEvent* expose) | |||
| BitBlt(impl->hdc, | |||
| 0, | |||
| 0, | |||
| (int)view->lastConfigure.width, | |||
| (int)view->lastConfigure.height, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height, | |||
| surface->drawDc, | |||
| 0, | |||
| 0, | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/gl.h" | |||
| #include <pugl/gl.h> | |||
| #include <windows.h> | |||
| @@ -14,30 +14,42 @@ | |||
| #include <stdbool.h> | |||
| #include <stdlib.h> | |||
| #define WGL_DRAW_TO_WINDOW_ARB 0x2001 | |||
| #define WGL_ACCELERATION_ARB 0x2003 | |||
| #define WGL_SUPPORT_OPENGL_ARB 0x2010 | |||
| #define WGL_DOUBLE_BUFFER_ARB 0x2011 | |||
| #define WGL_PIXEL_TYPE_ARB 0x2013 | |||
| #define WGL_RED_BITS_ARB 0x2015 | |||
| #define WGL_GREEN_BITS_ARB 0x2017 | |||
| #define WGL_BLUE_BITS_ARB 0x2019 | |||
| #define WGL_ALPHA_BITS_ARB 0x201b | |||
| #define WGL_DEPTH_BITS_ARB 0x2022 | |||
| #define WGL_STENCIL_BITS_ARB 0x2023 | |||
| #define WGL_FULL_ACCELERATION_ARB 0x2027 | |||
| #define WGL_TYPE_RGBA_ARB 0x202b | |||
| #define WGL_SAMPLE_BUFFERS_ARB 0x2041 | |||
| #define WGL_SAMPLES_ARB 0x2042 | |||
| #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 | |||
| #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 | |||
| #define WGL_CONTEXT_FLAGS_ARB 0x2094 | |||
| #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 | |||
| #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 | |||
| #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | |||
| #define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 | |||
| typedef enum { | |||
| WGL_DRAW_TO_WINDOW_ARB = 0x2001, | |||
| WGL_ACCELERATION_ARB = 0x2003, | |||
| WGL_SUPPORT_OPENGL_ARB = 0x2010, | |||
| WGL_DOUBLE_BUFFER_ARB = 0x2011, | |||
| WGL_PIXEL_TYPE_ARB = 0x2013, | |||
| WGL_RED_BITS_ARB = 0x2015, | |||
| WGL_GREEN_BITS_ARB = 0x2017, | |||
| WGL_BLUE_BITS_ARB = 0x2019, | |||
| WGL_ALPHA_BITS_ARB = 0x201B, | |||
| WGL_DEPTH_BITS_ARB = 0x2022, | |||
| WGL_STENCIL_BITS_ARB = 0x2023, | |||
| WGL_SAMPLE_BUFFERS_ARB = 0x2041, | |||
| WGL_SAMPLES_ARB = 0x2042, | |||
| } PuglWinGlHintName; | |||
| typedef enum { | |||
| WGL_FULL_ACCELERATION_ARB = 0x2027, | |||
| WGL_TYPE_RGBA_ARB = 0x202B, | |||
| } PuglWinGlHintValue; | |||
| typedef enum { | |||
| WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091, | |||
| WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092, | |||
| WGL_CONTEXT_FLAGS_ARB = 0x2094, | |||
| WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126, | |||
| } PuglWinGlContextAttribName; | |||
| typedef enum { | |||
| WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001, | |||
| WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002, | |||
| } PuglWinGlContextProfileBit; | |||
| typedef enum { | |||
| WGL_CONTEXT_DEBUG_BIT_ARB = 0x00000001, | |||
| } PuglWinGlContextDebugBit; | |||
| typedef HGLRC(WINAPI* WglCreateContextAttribs)(HDC, HGLRC, const int*); | |||
| @@ -5,7 +5,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| static PuglStatus | |||
| puglWinStubConfigure(PuglView* view) | |||
| @@ -7,7 +7,7 @@ | |||
| #include "types.h" | |||
| #include "win.h" | |||
| #include "pugl/vulkan.h" | |||
| #include <pugl/vulkan.h> | |||
| #include <vulkan/vulkan.h> | |||
| #include <vulkan/vulkan_win32.h> | |||
| @@ -11,7 +11,7 @@ | |||
| #include "platform.h" | |||
| #include "types.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| #include <X11/X.h> | |||
| #include <X11/Xatom.h> | |||
| @@ -69,13 +69,11 @@ | |||
| #include <unistd.h> | |||
| #ifdef __cplusplus | |||
| # define PUGL_INIT_STRUCT \ | |||
| {} | |||
| #else | |||
| # define PUGL_INIT_STRUCT \ | |||
| { \ | |||
| 0 \ | |||
| } | |||
| #else | |||
| # define PUGL_INIT_STRUCT {0} | |||
| #endif | |||
| enum WmClientStateMessageAction { | |||
| @@ -396,8 +394,15 @@ findView(PuglWorld* const world, const Window window) | |||
| return NULL; | |||
| } | |||
| static PuglStatus | |||
| updateSizeHints(const PuglView* const view) | |||
| PuglStatus | |||
| puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint)) | |||
| { | |||
| // No fine-grained updates, hints are always recalculated together | |||
| return puglUpdateSizeHints(view); | |||
| } | |||
| PuglStatus | |||
| puglUpdateSizeHints(PuglView* const view) | |||
| { | |||
| if (!view->impl->win) { | |||
| return PUGL_SUCCESS; | |||
| @@ -407,40 +412,44 @@ updateSizeHints(const PuglView* const view) | |||
| XSizeHints sizeHints = PUGL_INIT_STRUCT; | |||
| if (!view->hints[PUGL_RESIZABLE]) { | |||
| const PuglRect frame = puglGetFrame(view); | |||
| PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE); | |||
| if (!puglIsValidSize(size.width, size.height)) { | |||
| size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE); | |||
| } | |||
| sizeHints.flags = PBaseSize | PMinSize | PMaxSize; | |||
| sizeHints.base_width = (int)frame.width; | |||
| sizeHints.base_height = (int)frame.height; | |||
| sizeHints.min_width = (int)frame.width; | |||
| sizeHints.min_height = (int)frame.height; | |||
| sizeHints.max_width = (int)frame.width; | |||
| sizeHints.max_height = (int)frame.height; | |||
| sizeHints.base_width = size.width; | |||
| sizeHints.base_height = size.height; | |||
| sizeHints.min_width = size.width; | |||
| sizeHints.min_height = size.height; | |||
| sizeHints.max_width = size.width; | |||
| sizeHints.max_height = size.height; | |||
| } else { | |||
| // Avoid setting PBaseSize for top level views to avoid window manager bugs | |||
| const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| if (puglIsValidSize(defaultSize) && view->parent) { | |||
| const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
| if (puglIsValidArea(defaultSize) && view->parent) { | |||
| sizeHints.flags |= PBaseSize; | |||
| sizeHints.base_width = defaultSize.width; | |||
| sizeHints.base_height = defaultSize.height; | |||
| } | |||
| const PuglViewSize minSize = view->sizeHints[PUGL_MIN_SIZE]; | |||
| if (puglIsValidSize(minSize)) { | |||
| const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE]; | |||
| if (puglIsValidArea(minSize)) { | |||
| sizeHints.flags |= PMinSize; | |||
| sizeHints.min_width = minSize.width; | |||
| sizeHints.min_height = minSize.height; | |||
| } | |||
| const PuglViewSize maxSize = view->sizeHints[PUGL_MAX_SIZE]; | |||
| if (puglIsValidSize(maxSize)) { | |||
| const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE]; | |||
| if (puglIsValidArea(maxSize)) { | |||
| sizeHints.flags |= PMaxSize; | |||
| sizeHints.max_width = maxSize.width; | |||
| sizeHints.max_height = maxSize.height; | |||
| } | |||
| const PuglViewSize minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglViewSize maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| if (puglIsValidSize(minAspect) && puglIsValidSize(maxAspect)) { | |||
| const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT]; | |||
| const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT]; | |||
| if (puglIsValidArea(minAspect) && puglIsValidArea(maxAspect)) { | |||
| sizeHints.flags |= PAspect; | |||
| sizeHints.min_aspect.x = minAspect.width; | |||
| sizeHints.min_aspect.y = minAspect.height; | |||
| @@ -448,8 +457,8 @@ updateSizeHints(const PuglView* const view) | |||
| sizeHints.max_aspect.y = maxAspect.height; | |||
| } | |||
| const PuglViewSize fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; | |||
| if (puglIsValidSize(fixedAspect)) { | |||
| const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT]; | |||
| if (puglIsValidArea(fixedAspect)) { | |||
| sizeHints.flags |= PAspect; | |||
| sizeHints.min_aspect.x = fixedAspect.width; | |||
| sizeHints.min_aspect.y = fixedAspect.height; | |||
| @@ -513,48 +522,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) | |||
| board->data.len = 0; | |||
| } | |||
| static PuglRect | |||
| getInitialFrame(PuglView* const view) | |||
| PuglPoint | |||
| puglGetAncestorCenter(const PuglView* const view) | |||
| { | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the last configured frame | |||
| const PuglRect frame = {view->lastConfigure.x, | |||
| view->lastConfigure.y, | |||
| view->lastConfigure.width, | |||
| view->lastConfigure.height}; | |||
| return frame; | |||
| } | |||
| Display* const display = view->world->impl->display; | |||
| const int screen = view->impl->screen; | |||
| XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT; | |||
| XGetWindowAttributes(display, | |||
| view->transientParent ? (Window)view->transientParent | |||
| : RootWindow(display, screen), | |||
| &ancestorAttrs); | |||
| const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; | |||
| const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; | |||
| const int x = view->defaultX; | |||
| const int y = view->defaultY; | |||
| if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { | |||
| // Use the default position set with puglSetPosition while unrealized | |||
| const PuglRect frame = { | |||
| (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; | |||
| return frame; | |||
| } | |||
| // Get the best "parentish" window to position the window in | |||
| Display* const display = view->world->impl->display; | |||
| const Window parent = | |||
| (view->parent ? (Window)view->parent | |||
| : view->transientParent ? (Window)view->transientParent | |||
| : RootWindow(display, view->impl->screen)); | |||
| // Get the position/size of the parent as bounds for the new window | |||
| XWindowAttributes parentAttrs = PUGL_INIT_STRUCT; | |||
| XGetWindowAttributes(display, parent, &parentAttrs); | |||
| // Center the frame within the parent bounds | |||
| const int centerX = parentAttrs.x + parentAttrs.width / 2; | |||
| const int centerY = parentAttrs.y + parentAttrs.height / 2; | |||
| const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), | |||
| (PuglCoord)(centerY - (defaultHeight / 2)), | |||
| defaultWidth, | |||
| defaultHeight}; | |||
| return frame; | |||
| const PuglPoint center = { | |||
| (PuglCoord)(ancestorAttrs.x + (ancestorAttrs.width / 2)), | |||
| (PuglCoord)(ancestorAttrs.y + (ancestorAttrs.height / 2))}; | |||
| return center; | |||
| } | |||
| PuglStatus | |||
| @@ -609,16 +591,17 @@ puglRealize(PuglView* const view) | |||
| attr.event_mask |= StructureNotifyMask; | |||
| attr.event_mask |= VisibilityChangeMask; | |||
| // Calculate the initial window rectangle | |||
| const PuglRect initialFrame = getInitialFrame(view); | |||
| // Calculate the initial window frame | |||
| const PuglArea initialSize = puglGetInitialSize(view); | |||
| const PuglPoint initialPos = puglGetInitialPosition(view, initialSize); | |||
| // Create the window | |||
| impl->win = XCreateWindow(display, | |||
| parent, | |||
| initialFrame.x, | |||
| initialFrame.y, | |||
| initialFrame.width, | |||
| initialFrame.height, | |||
| initialPos.x, | |||
| initialPos.y, | |||
| initialSize.width, | |||
| initialSize.height, | |||
| 0, | |||
| impl->vi->depth, | |||
| InputOutput, | |||
| @@ -650,7 +633,7 @@ puglRealize(PuglView* const view) | |||
| if (XRRQueryExtension(display, &ignored, &ignored)) { | |||
| // Set refresh rate hint to the real refresh rate | |||
| XRRScreenConfiguration* conf = XRRGetScreenInfo(display, parent); | |||
| short current_rate = XRRConfigCurrentRate(conf); | |||
| const short current_rate = XRRConfigCurrentRate(conf); | |||
| view->hints[PUGL_REFRESH_RATE] = current_rate; | |||
| XRRFreeScreenConfigInfo(conf); | |||
| @@ -663,7 +646,7 @@ puglRealize(PuglView* const view) | |||
| XSetClassHint(display, impl->win, &classHint); | |||
| puglSetViewString(view, PUGL_WINDOW_TITLE, view->strings[PUGL_WINDOW_TITLE]); | |||
| puglSetTransientParent(view, view->transientParent); | |||
| updateSizeHints(view); | |||
| puglUpdateSizeHints(view); | |||
| // Set PID and hostname so the window manager can access our process | |||
| char hostname[256] = PUGL_INIT_STRUCT; | |||
| @@ -746,34 +729,29 @@ puglUnrealize(PuglView* const view) | |||
| impl->vi = NULL; | |||
| memset(&view->lastConfigure, 0, sizeof(PuglConfigureEvent)); | |||
| memset(&view->impl->pendingConfigure, 0, sizeof(PuglEvent)); | |||
| memset(&view->impl->pendingExpose, 0, sizeof(PuglEvent)); | |||
| if (impl->mapped) { | |||
| view->impl->pendingConfigure.configure.style |= PUGL_VIEW_STYLE_MAPPED; | |||
| } | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PuglStatus | |||
| puglShow(PuglView* const view, const PuglShowCommand command) | |||
| { | |||
| PuglStatus st = view->impl->win ? PUGL_SUCCESS : puglRealize(view); | |||
| PuglInternals* impl = view->impl; | |||
| PuglStatus st = impl->win ? PUGL_SUCCESS : puglRealize(view); | |||
| if (!st) { | |||
| switch (command) { | |||
| case PUGL_SHOW_PASSIVE: | |||
| XMapWindow(view->world->impl->display, view->impl->win); | |||
| XMapWindow(view->world->impl->display, impl->win); | |||
| break; | |||
| case PUGL_SHOW_RAISE: | |||
| case PUGL_SHOW_FORCE_RAISE: | |||
| XMapRaised(view->world->impl->display, view->impl->win); | |||
| XMapRaised(view->world->impl->display, impl->win); | |||
| break; | |||
| } | |||
| if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) { | |||
| st = puglPostRedisplay(view); | |||
| st = puglObscureView(view); | |||
| } | |||
| } | |||
| @@ -783,6 +761,10 @@ puglShow(PuglView* const view, const PuglShowCommand command) | |||
| PuglStatus | |||
| puglHide(PuglView* const view) | |||
| { | |||
| if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| XUnmapWindow(view->world->impl->display, view->impl->win); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -817,13 +799,13 @@ keyInRange(const KeySym xSym, | |||
| const PuglKey puglMin) | |||
| { | |||
| return (xSym >= xMin && xSym <= xMax) ? (PuglKey)(puglMin + (xSym - xMin)) | |||
| : (PuglKey)0; | |||
| : PUGL_KEY_NONE; | |||
| } | |||
| static PuglKey | |||
| keySymToSpecial(const KeySym sym) | |||
| { | |||
| PuglKey key = (PuglKey)0; | |||
| PuglKey key = PUGL_KEY_NONE; | |||
| if ((key = keyInRange(sym, XK_F1, XK_F12, PUGL_KEY_F1)) || | |||
| (key = keyInRange(sym, XK_Page_Up, XK_End, PUGL_KEY_PAGE_UP)) || | |||
| (key = keyInRange(sym, XK_Home, XK_Down, PUGL_KEY_HOME)) || | |||
| @@ -856,7 +838,7 @@ keySymToSpecial(const KeySym sym) | |||
| } | |||
| // clang-format on | |||
| return (PuglKey)0; | |||
| return PUGL_KEY_NONE; | |||
| } | |||
| static int | |||
| @@ -897,7 +879,8 @@ translateKey(PuglView* const view, XEvent* const xevent, PuglEvent* const event) | |||
| event->key.key = (PuglKey)puglDecodeUTF8((const uint8_t*)ustr); | |||
| } | |||
| if (xevent->type == KeyPress && !filter && !special && view->impl->xic) { | |||
| if (xevent->type == KeyPress && !filter && (!special || ufound > 0) && | |||
| view->impl->xic) { | |||
| // Lookup shifted key for possible text event | |||
| xevent->xkey.state = state; | |||
| @@ -1028,7 +1011,7 @@ translateClientMessage(PuglView* const view, XClientMessageEvent message) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| if (message.message_type == atoms->WM_PROTOCOLS) { | |||
| const Atom protocol = (Atom)message.data.l[0]; | |||
| @@ -1083,6 +1066,7 @@ getCurrentViewStyleFlags(PuglView* const view) | |||
| state |= PUGL_VIEW_STYLE_DEMANDING; | |||
| } | |||
| } | |||
| XFree(hints); | |||
| } | |||
| if (view->impl->mapped) { | |||
| @@ -1101,17 +1085,19 @@ getCurrentConfiguration(PuglView* const view) | |||
| XWindowAttributes attrs; | |||
| XGetWindowAttributes(display, view->impl->win, &attrs); | |||
| // Get window position relative to the root window | |||
| // Get window position (relative to the root window if not a child) | |||
| Window ignoredChild = 0; | |||
| int rootX = 0; | |||
| int rootY = 0; | |||
| XTranslateCoordinates( | |||
| display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild); | |||
| int x = attrs.x; | |||
| int y = attrs.y; | |||
| if (!view->parent) { | |||
| XTranslateCoordinates( | |||
| display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild); | |||
| } | |||
| // Build a configure event based on the current window configuration | |||
| PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}}; | |||
| configureEvent.configure.x = (PuglCoord)rootX; | |||
| configureEvent.configure.y = (PuglCoord)rootY; | |||
| PuglEvent configureEvent = {{PUGL_CONFIGURE, 0U}}; | |||
| configureEvent.configure.x = (PuglCoord)x; | |||
| configureEvent.configure.y = (PuglCoord)y; | |||
| configureEvent.configure.width = (PuglSpan)attrs.width; | |||
| configureEvent.configure.height = (PuglSpan)attrs.height; | |||
| configureEvent.configure.style = getCurrentViewStyleFlags(view); | |||
| @@ -1119,28 +1105,12 @@ getCurrentConfiguration(PuglView* const view) | |||
| return configureEvent; | |||
| } | |||
| static PuglEvent | |||
| makeConfigureEvent(PuglView* const view) | |||
| { | |||
| PuglEvent event = view->impl->pendingConfigure; | |||
| if (event.type != PUGL_CONFIGURE) { | |||
| event = getCurrentConfiguration(view); | |||
| } else if (view->impl->mapped) { | |||
| event.configure.style |= PUGL_VIEW_STYLE_MAPPED; | |||
| } else { | |||
| event.configure.style &= ~(PuglViewStyleFlags)PUGL_VIEW_STYLE_MAPPED; | |||
| } | |||
| return event; | |||
| } | |||
| static PuglEvent | |||
| translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||
| { | |||
| const PuglInternals* const impl = view->impl; | |||
| const PuglX11Atoms* const atoms = &view->world->impl->atoms; | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| if (message.atom == atoms->NET_WM_STATE) { | |||
| // Get all the current states set in the window hints | |||
| @@ -1151,8 +1121,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||
| } | |||
| // Make a configure event based on the current configuration to update | |||
| event = makeConfigureEvent(view); | |||
| event.configure.style = getCurrentViewStyleFlags(view); // FIXME: necessary? | |||
| event = getCurrentConfiguration(view); | |||
| XFree(hints); | |||
| } else if (message.atom == atoms->NET_FRAME_EXTENTS) { | |||
| @@ -1190,7 +1159,7 @@ translatePropertyNotify(PuglView* const view, XPropertyEvent message) | |||
| static PuglEvent | |||
| translateEvent(PuglView* const view, XEvent xevent) | |||
| { | |||
| PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent event = {{PUGL_NOTHING, 0U}}; | |||
| event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0; | |||
| switch (xevent.type) { | |||
| @@ -1201,18 +1170,21 @@ translateEvent(PuglView* const view, XEvent xevent) | |||
| event = translatePropertyNotify(view, xevent.xproperty); | |||
| break; | |||
| case VisibilityNotify: | |||
| event = makeConfigureEvent(view); | |||
| event = getCurrentConfiguration(view); | |||
| break; | |||
| case MapNotify: | |||
| view->impl->mapped = true; | |||
| event = makeConfigureEvent(view); | |||
| event = getCurrentConfiguration(view); | |||
| break; | |||
| case UnmapNotify: | |||
| view->impl->mapped = false; | |||
| event = makeConfigureEvent(view); | |||
| event = getCurrentConfiguration(view); | |||
| break; | |||
| case DestroyNotify: | |||
| view->impl->win = None; | |||
| break; | |||
| case ConfigureNotify: | |||
| event = makeConfigureEvent(view); | |||
| event = getCurrentConfiguration(view); | |||
| event.configure.width = (PuglSpan)xevent.xconfigure.width; | |||
| event.configure.height = (PuglSpan)xevent.xconfigure.height; | |||
| if (view->parent) { | |||
| @@ -1503,7 +1475,7 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||
| PuglInternals* const impl = view->impl; | |||
| Display* const display = view->world->impl->display; | |||
| XEvent xev = PUGL_INIT_STRUCT; | |||
| if (!impl->win) { | |||
| if (!impl->win || view->world->state == PUGL_WORLD_EXPOSING) { | |||
| return PUGL_FAILURE; | |||
| } | |||
| @@ -1534,21 +1506,11 @@ puglSendEvent(PuglView* const view, const PuglEvent* const event) | |||
| return PUGL_UNSUPPORTED; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglWaitForEvent(PuglView* const view) | |||
| { | |||
| XEvent xevent; | |||
| XPeekEvent(view->world->impl->display, &xevent); | |||
| return PUGL_SUCCESS; | |||
| } | |||
| #endif | |||
| static void | |||
| mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) | |||
| { | |||
| if (!dst->type) { | |||
| if (src->width > 0.0 && src->height > 0.0) { | |||
| if (src->width && src->height) { | |||
| *dst = *src; | |||
| } | |||
| } else { | |||
| @@ -1612,7 +1574,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||
| Display* const display = view->world->impl->display; | |||
| const Atom selection = event->selection; | |||
| PuglX11Clipboard* const board = getX11SelectionClipboard(view, selection); | |||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0}}; | |||
| PuglEvent puglEvent = {{PUGL_NOTHING, 0U}}; | |||
| if (event->target == atoms->TARGETS) { | |||
| // Notification of available datatypes | |||
| @@ -1622,7 +1584,7 @@ handleSelectionNotify(const PuglWorld* const world, | |||
| view, event->requestor, event->property, &numFormats, &formats) && | |||
| !setClipboardFormats(view, board, numFormats, formats)) { | |||
| const PuglDataOfferEvent offer = { | |||
| PUGL_DATA_OFFER, 0, (double)event->time / 1e3}; | |||
| PUGL_DATA_OFFER, 0U, (double)event->time / 1e3}; | |||
| puglEvent.offer = offer; | |||
| board->acceptedFormatIndex = UINT32_MAX; | |||
| @@ -1699,48 +1661,35 @@ handleSelectionRequest(const PuglWorld* const world, | |||
| } | |||
| /// Flush pending configure and expose events for all views | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| flushExposures(PuglWorld* const world) | |||
| { | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| PuglStatus st2 = PUGL_SUCCESS; | |||
| // Send update events so the application can trigger redraws | |||
| for (size_t i = 0; i < world->numViews; ++i) { | |||
| PuglView* const view = world->views[i]; | |||
| // Send update event so the application can trigger redraws | |||
| if (puglGetVisible(view)) { | |||
| puglDispatchSimpleEvent(view, PUGL_UPDATE); | |||
| if (puglGetVisible(world->views[i])) { | |||
| puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE); | |||
| } | |||
| } | |||
| // Copy and reset pending events (in case their handlers write new ones) | |||
| const PuglEvent configure = view->impl->pendingConfigure; | |||
| const PuglEvent expose = view->impl->pendingExpose; | |||
| view->impl->pendingConfigure.type = PUGL_NOTHING; | |||
| view->impl->pendingExpose.type = PUGL_NOTHING; | |||
| if (expose.type || configure.type) { | |||
| const PuglExposeEvent* const exposeEvent = | |||
| expose.type ? &expose.expose : NULL; | |||
| if (!(st0 = view->backend->enter(view, exposeEvent))) { | |||
| if (configure.type) { | |||
| st0 = puglConfigure(view, &configure); | |||
| } | |||
| // Expose any dirty views | |||
| world->state = PUGL_WORLD_EXPOSING; | |||
| for (size_t i = 0; i < world->numViews; ++i) { | |||
| PuglView* const view = world->views[i]; | |||
| if (expose.type) { | |||
| st1 = view->eventFunc(view, &expose); | |||
| } | |||
| if (puglGetVisible(view)) { | |||
| const PuglEvent expose = view->impl->pendingExpose; | |||
| if (expose.type && !(st0 = view->backend->enter(view, &expose.expose))) { | |||
| st0 = view->eventFunc(view, &expose); | |||
| st1 = view->backend->leave(view, &expose.expose); | |||
| view->impl->pendingExpose.type = PUGL_NOTHING; | |||
| } | |||
| st2 = view->backend->leave(view, exposeEvent); | |||
| } | |||
| } | |||
| return st0 ? st0 : st1 ? st1 : st2; | |||
| return st0 ? st0 : st1; | |||
| } | |||
| static bool | |||
| @@ -1753,7 +1702,7 @@ handleTimerEvent(PuglWorld* const world, const XEvent xevent) | |||
| for (size_t i = 0; i < world->impl->numTimers; ++i) { | |||
| if (world->impl->timers[i].alarm == notify->alarm) { | |||
| PuglEvent event = {{PUGL_TIMER, 0}}; | |||
| PuglEvent event = {{PUGL_TIMER, 0U}}; | |||
| event.timer.id = world->impl->timers[i].id; | |||
| puglDispatchEvent(world->impl->timers[i].view, &event); | |||
| } | |||
| @@ -1821,10 +1770,6 @@ dispatchX11Events(PuglWorld* const world) | |||
| const PuglEvent event = translateEvent(view, xevent); | |||
| switch (event.type) { | |||
| case PUGL_CONFIGURE: | |||
| // Update configure event to be dispatched after loop | |||
| view->impl->pendingConfigure = event; | |||
| break; | |||
| case PUGL_EXPOSE: | |||
| // Expand expose event to be dispatched after loop | |||
| mergeExposeEvents(&view->impl->pendingExpose.expose, &event.expose); | |||
| @@ -1851,26 +1796,24 @@ dispatchX11Events(PuglWorld* const world) | |||
| return st; | |||
| } | |||
| #ifndef PUGL_DISABLE_DEPRECATED | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* const view) | |||
| { | |||
| return puglUpdate(view->world, 0.0); | |||
| } | |||
| #endif | |||
| PuglStatus | |||
| puglUpdate(PuglWorld* const world, const double timeout) | |||
| { | |||
| const double startTime = puglGetTime(world); | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| const double startTime = puglGetTime(world); | |||
| const PuglWorldState startState = world->state; | |||
| PuglStatus st0 = PUGL_SUCCESS; | |||
| PuglStatus st1 = PUGL_SUCCESS; | |||
| world->impl->dispatchingEvents = true; | |||
| if (startState == PUGL_WORLD_IDLE) { | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| } else if (startState != PUGL_WORLD_RECURSING) { | |||
| return PUGL_BAD_CALL; | |||
| } | |||
| if (timeout < 0.0) { | |||
| st0 = pollX11Socket(world, timeout); | |||
| st0 = st0 ? st0 : dispatchX11Events(world); | |||
| if (!(st0 = pollX11Socket(world, timeout))) { | |||
| st0 = dispatchX11Events(world); | |||
| } | |||
| } else if (timeout <= 0.001) { | |||
| st0 = dispatchX11Events(world); | |||
| } else { | |||
| @@ -1885,10 +1828,8 @@ puglUpdate(PuglWorld* const world, const double timeout) | |||
| } | |||
| } | |||
| st1 = flushExposures(world); | |||
| world->impl->dispatchingEvents = false; | |||
| st1 = flushExposures(world); | |||
| world->state = startState; | |||
| return st0 ? st0 : st1; | |||
| } | |||
| @@ -1896,42 +1837,56 @@ double | |||
| puglGetTime(const PuglWorld* const world) | |||
| { | |||
| struct timespec ts; | |||
| clock_gettime(CLOCK_MONOTONIC, &ts); | |||
| if (clock_gettime(CLOCK_MONOTONIC, &ts)) { | |||
| return 0.0; | |||
| } | |||
| return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - | |||
| world->startTime; | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplay(PuglView* const view) | |||
| puglObscureView(PuglView* const view) | |||
| { | |||
| PuglRect rect = puglGetFrame(view); | |||
| rect.x = 0; | |||
| rect.y = 0; | |||
| return puglPostRedisplayRect(view, rect); | |||
| return puglObscureRegion( | |||
| view, 0, 0, view->lastConfigure.width, view->lastConfigure.height); | |||
| } | |||
| PuglStatus | |||
| puglPostRedisplayRect(PuglView* const view, const PuglRect rect) | |||
| puglObscureRegion(PuglView* const view, | |||
| const int x, | |||
| const int y, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| const PuglExposeEvent event = { | |||
| PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height}; | |||
| if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| const PuglCoord cx = MAX((PuglCoord)0, (PuglCoord)x); | |||
| const PuglCoord cy = MAX((PuglCoord)0, (PuglCoord)y); | |||
| const PuglSpan cw = MIN(view->lastConfigure.width, (PuglSpan)width); | |||
| const PuglSpan ch = MIN(view->lastConfigure.height, (PuglSpan)height); | |||
| const PuglExposeEvent event = {PUGL_EXPOSE, 0U, cx, cy, cw, ch}; | |||
| if (view->world->impl->dispatchingEvents) { | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (view->world->state == PUGL_WORLD_UPDATING) { | |||
| // Currently dispatching events, add/expand expose for the loop end | |||
| mergeExposeEvents(&view->impl->pendingExpose.expose, &event); | |||
| } else if (view->world->state == PUGL_WORLD_EXPOSING) { | |||
| st = PUGL_BAD_CALL; | |||
| } else if (view->impl->win) { | |||
| // Not dispatching events, send an X expose so we wake up next time | |||
| PuglEvent exposeEvent = {{PUGL_EXPOSE, 0}}; | |||
| PuglEvent exposeEvent = {{PUGL_EXPOSE, 0U}}; | |||
| exposeEvent.expose = event; | |||
| return puglSendEvent(view, &exposeEvent); | |||
| st = puglSendEvent(view, &exposeEvent); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| return st; | |||
| } | |||
| PuglNativeView | |||
| puglGetNativeView(PuglView* const view) | |||
| puglGetNativeView(const PuglView* const view) | |||
| { | |||
| return (PuglNativeView)view->impl->win; | |||
| } | |||
| @@ -1977,79 +1932,21 @@ puglGetScaleFactor(const PuglView* const view) | |||
| } | |||
| PuglStatus | |||
| puglSetFrame(PuglView* const view, const PuglRect frame) | |||
| { | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = frame.x; | |||
| view->defaultY = frame.y; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XMoveResizeWindow(view->world->impl->display, | |||
| view->impl->win, | |||
| frame.x, | |||
| frame.y, | |||
| frame.width, | |||
| frame.height)); | |||
| } | |||
| PuglStatus | |||
| puglSetPosition(PuglView* const view, const int x, const int y) | |||
| puglSetWindowPosition(PuglView* const view, const int x, const int y) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->defaultX = x; | |||
| view->defaultY = y; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XMoveWindow(display, | |||
| return puglX11Status(XMoveWindow(view->world->impl->display, | |||
| view->impl->win, | |||
| (int)(x - view->impl->frameExtentLeft), | |||
| (int)(y - view->impl->frameExtentTop))); | |||
| } | |||
| PuglStatus | |||
| puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| if (width > INT16_MAX || height > INT16_MAX) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| if (!view->impl->win) { | |||
| // Set defaults to be used when realized | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; | |||
| return PUGL_SUCCESS; | |||
| } | |||
| return puglX11Status(XResizeWindow(display, view->impl->win, width, height)); | |||
| } | |||
| PuglStatus | |||
| puglSetSizeHint(PuglView* const view, | |||
| const PuglSizeHint hint, | |||
| const PuglSpan width, | |||
| const PuglSpan height) | |||
| puglSetWindowSize(PuglView* const view, | |||
| const unsigned width, | |||
| const unsigned height) | |||
| { | |||
| if ((unsigned)hint >= PUGL_NUM_SIZE_HINTS) { | |||
| return PUGL_BAD_PARAMETER; | |||
| } | |||
| view->sizeHints[hint].width = width; | |||
| view->sizeHints[hint].height = height; | |||
| return updateSizeHints(view); | |||
| return puglX11Status( | |||
| XResizeWindow(view->world->impl->display, view->impl->win, width, height)); | |||
| } | |||
| PuglStatus | |||
| @@ -7,8 +7,8 @@ | |||
| #include "attributes.h" | |||
| #include "types.h" | |||
| #include "pugl/attributes.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/attributes.h> | |||
| #include <pugl/pugl.h> | |||
| #include <X11/X.h> | |||
| #include <X11/Xlib.h> | |||
| @@ -75,7 +75,6 @@ struct PuglWorldInternalsImpl { | |||
| XID serverTimeCounter; | |||
| int syncEventBase; | |||
| bool syncSupported; | |||
| bool dispatchingEvents; | |||
| }; | |||
| struct PuglInternalsImpl { | |||
| @@ -83,7 +82,6 @@ struct PuglInternalsImpl { | |||
| Window win; | |||
| XIC xic; | |||
| PuglSurface* surface; | |||
| PuglEvent pendingConfigure; | |||
| PuglEvent pendingExpose; | |||
| PuglX11Clipboard clipboard; | |||
| long frameExtentLeft; | |||
| @@ -93,9 +91,7 @@ struct PuglInternalsImpl { | |||
| bool mapped; | |||
| }; | |||
| PUGL_WARN_UNUSED_RESULT | |||
| PUGL_API | |||
| PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT PUGL_API PuglStatus | |||
| puglX11Configure(PuglView* view); | |||
| #endif // PUGL_SRC_X11_H | |||
| @@ -5,8 +5,8 @@ | |||
| #include "types.h" | |||
| #include "x11.h" | |||
| #include "pugl/cairo.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/cairo.h> | |||
| #include <pugl/pugl.h> | |||
| #include <cairo-xlib.h> | |||
| #include <cairo.h> | |||
| @@ -19,10 +19,10 @@ typedef struct { | |||
| cairo_t* cr; | |||
| } PuglX11CairoSurface; | |||
| static PuglViewSize | |||
| static PuglArea | |||
| puglX11CairoGetViewSize(const PuglView* const view) | |||
| { | |||
| PuglViewSize size = {0U, 0U}; | |||
| PuglArea size = {0U, 0U}; | |||
| if (view->lastConfigure.type == PUGL_CONFIGURE) { | |||
| // Use the size of the last configured frame | |||
| @@ -97,11 +97,11 @@ puglX11CairoEnter(PuglView* view, const PuglExposeEvent* expose) | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| if (expose) { | |||
| const PuglViewSize viewSize = puglX11CairoGetViewSize(view); | |||
| const PuglSpan right = (PuglSpan)(expose->x + expose->width); | |||
| const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); | |||
| const PuglSpan surfaceWidth = MAX(right, viewSize.width); | |||
| const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); | |||
| const PuglArea viewSize = puglX11CairoGetViewSize(view); | |||
| const PuglSpan right = (PuglSpan)(expose->x + expose->width); | |||
| const PuglSpan bottom = (PuglSpan)(expose->y + expose->height); | |||
| const PuglSpan surfaceWidth = MAX(right, viewSize.width); | |||
| const PuglSpan surfaceHeight = MAX(bottom, viewSize.height); | |||
| if (!(st = puglX11CairoOpen(view, surfaceWidth, surfaceHeight))) { | |||
| surface->cr = cairo_create(surface->front); | |||
| if (cairo_status(surface->cr)) { | |||
| @@ -6,15 +6,14 @@ | |||
| #include "types.h" | |||
| #include "x11.h" | |||
| #include "pugl/gl.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/gl.h> | |||
| #include <pugl/pugl.h> | |||
| #include <GL/glx.h> | |||
| #include <X11/X.h> | |||
| #include <X11/Xlib.h> | |||
| #include <stdint.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| @@ -101,8 +100,7 @@ puglX11GlConfigure(PuglView* view) | |||
| return PUGL_SUCCESS; | |||
| } | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||
| { | |||
| PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; | |||
| @@ -115,8 +113,7 @@ puglX11GlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||
| : PUGL_FAILURE; | |||
| } | |||
| PUGL_WARN_UNUSED_RESULT | |||
| static PuglStatus | |||
| PUGL_WARN_UNUSED_RESULT static PuglStatus | |||
| puglX11GlLeave(PuglView* view, const PuglExposeEvent* expose) | |||
| { | |||
| Display* const display = view->world->impl->display; | |||
| @@ -1,13 +1,13 @@ | |||
| // Copyright 2012-2021 David Robillard <d@drobilla.net> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "pugl/stub.h" | |||
| #include <pugl/stub.h> | |||
| #include "stub.h" | |||
| #include "types.h" | |||
| #include "x11.h" | |||
| #include "pugl/pugl.h" | |||
| #include <pugl/pugl.h> | |||
| const PuglBackend* | |||
| puglStubBackend(void) | |||
| @@ -8,8 +8,8 @@ | |||
| #include "types.h" | |||
| #include "x11.h" | |||
| #include "pugl/pugl.h" | |||
| #include "pugl/vulkan.h" | |||
| #include <pugl/pugl.h> | |||
| #include <pugl/vulkan.h> | |||
| #include <vulkan/vulkan_core.h> | |||
| #include <vulkan/vulkan_xlib.h> | |||
| @@ -250,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||
| if (view->backend != nullptr) | |||
| { | |||
| #ifdef DGL_OPENGL | |||
| #if defined(DGL_USE_GLES2) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
| #elif defined(DGL_USE_GLES3) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||
| #elif defined(DGL_USE_OPENGL3) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||
| #else | |||
| #elif defined(DGL_OPENGL) | |||
| puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API); | |||
| puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE); | |||
| puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
| #endif | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| @@ -277,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||
| void puglRaiseWindow(PuglView* const view) | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (NSWindow* const window = view->impl->window ? view->impl->window | |||
| : [view->impl->wrapperView window]) | |||
| [window orderFrontRegardless]; | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // this does the same as puglShow(view, PUGL_SHOW_FORCE_RAISE) + puglShow(view, PUGL_SHOW_RAISE) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSWindow* const window = [view->impl->wrapperView window]; | |||
| [window orderFrontRegardless]; | |||
| [window orderFront:view->impl->wrapperView]; | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| SetForegroundWindow(view->impl->hwnd); | |||
| SetActiveWindow(view->impl->hwnd); | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| XRaiseWindow(view->world->impl->display, view->impl->win); | |||
| #endif | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -306,29 +309,31 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co | |||
| view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast<PuglSpan>(height); | |||
| } | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (view->impl->window) | |||
| { | |||
| if (const PuglStatus status = updateSizeHint(view, PUGL_MIN_SIZE)) | |||
| return status; | |||
| if (const PuglStatus status = updateSizeHint(view, PUGL_FIXED_ASPECT)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| } | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| const char* const className = view->world->strings[PUGL_CLASS_NAME]; | |||
| EM_ASM({ | |||
| var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; | |||
| canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px'); | |||
| canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px'); | |||
| }, className, width, height); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| // nothing | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| if (view->impl->win) | |||
| { | |||
| if (const PuglStatus status = updateSizeHints(view)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| XFlush(view->world->impl->display); | |||
| } | |||
| #endif | |||
| #endif | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -340,99 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable) | |||
| { | |||
| puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (PuglWindow* const window = view->impl->window) | |||
| { | |||
| const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) | |||
| | (resizable ? NSResizableWindowMask : 0x0); | |||
| | (resizable ? NSResizableWindowMask : 0); | |||
| [window setStyleMask:style]; | |||
| } | |||
| // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| // nothing | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| puglUpdateSizeHints(view); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (const HWND hwnd = view->impl->hwnd) | |||
| { | |||
| const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) | |||
| : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); | |||
| SetWindowLong(hwnd, GWL_STYLE, winFlags); | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| updateSizeHints(view); | |||
| #endif | |||
| #elif defined(HAVE_X11) | |||
| puglUpdateSizeHints(view); | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // set window size while also changing default | |||
| PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) | |||
| PuglStatus puglSetSizeAndDefault(PuglView* const view, const uint width, const uint height) | |||
| { | |||
| if (width > INT16_MAX || height > INT16_MAX) | |||
| return PUGL_BAD_PARAMETER; | |||
| #ifdef DGL_USING_X11 | |||
| // workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118 | |||
| // NOTE troublesome if used under KDE | |||
| if (view->impl->win && !view->parent && !view->transientParent && std::getenv("KDE_SESSION_VERSION") == nullptr) | |||
| { | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0; | |||
| } | |||
| else | |||
| #endif | |||
| // set default size first | |||
| { | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast<PuglSpan>(width); | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast<PuglSpan>(height); | |||
| } | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_CURRENT_SIZE].width = width; | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = view->sizeHints[PUGL_CURRENT_SIZE].height = height; | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| // matches upstream pugl | |||
| if (view->impl->wrapperView) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| return status; | |||
| // nothing to do for PUGL_DEFAULT_SIZE hint | |||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||
| return status; | |||
| } | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| d_stdout("className is %s", view->world->strings[PUGL_CLASS_NAME]); | |||
| #elif defined(DISTRHO_OS_WASM) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| emscripten_set_canvas_element_size(view->world->strings[PUGL_CLASS_NAME], width, height); | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| // matches upstream pugl, except we re-enter context after resize | |||
| if (view->impl->hwnd) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| return status; | |||
| // nothing to do for PUGL_DEFAULT_SIZE hint | |||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||
| return status; | |||
| // make sure to return context back to ourselves | |||
| puglBackendEnter(view); | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| // matches upstream pugl, adds flush at the end | |||
| if (view->impl->win) | |||
| { | |||
| if (const PuglStatus status = puglSetSize(view, width, height)) | |||
| if (const PuglStatus status = puglUpdateSizeHints(view)) | |||
| return status; | |||
| // updateSizeHints will use last known size, which is not yet updated | |||
| const PuglSpan lastWidth = view->lastConfigure.width; | |||
| const PuglSpan lastHeight = view->lastConfigure.height; | |||
| view->lastConfigure.width = static_cast<PuglSpan>(width); | |||
| view->lastConfigure.height = static_cast<PuglSpan>(height); | |||
| updateSizeHints(view); | |||
| view->lastConfigure.width = lastWidth; | |||
| view->lastConfigure.height = lastHeight; | |||
| if (const PuglStatus status = puglSetWindowSize(view, width, height)) | |||
| return status; | |||
| // flush size changes | |||
| XFlush(view->world->impl->display); | |||
| } | |||
| #endif | |||
| #endif | |||
| return PUGL_SUCCESS; | |||
| } | |||
| @@ -450,31 +435,6 @@ void puglOnDisplayPrepare(PuglView*) | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // DGL specific, build-specific fallback resize | |||
| void puglFallbackOnResize(PuglView* const view, const uint width, const uint height) | |||
| { | |||
| #ifdef DGL_OPENGL | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| #ifdef DGL_USE_OPENGL3 | |||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
| #else | |||
| glMatrixMode(GL_PROJECTION); | |||
| glLoadIdentity(); | |||
| glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); | |||
| glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| glLoadIdentity(); | |||
| #endif | |||
| #else | |||
| return; | |||
| // unused | |||
| (void)view; | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| @@ -618,55 +578,64 @@ void puglWin32ShowCentered(PuglView* const view) | |||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) | |||
| { | |||
| const bool wasDispatchingEvents = world->impl->dispatchingEvents; | |||
| world->impl->dispatchingEvents = true; | |||
| const PuglWorldState startState = world->state; | |||
| world->state = PUGL_WORLD_UPDATING; | |||
| PuglStatus st = PUGL_SUCCESS; | |||
| const double startTime = puglGetTime(world); | |||
| const double endTime = startTime + 0.03; | |||
| const double endTime = startTime + 0.03; | |||
| for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) | |||
| { | |||
| pollX11Socket(world, endTime - t); | |||
| st = dispatchX11Events(world); | |||
| if (!(st = pollX11Socket(world, endTime - t))) | |||
| st = dispatchX11Events(world); | |||
| } | |||
| world->impl->dispatchingEvents = wasDispatchingEvents; | |||
| world->state = startState; | |||
| return st; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // X11 specific, set dialog window type and pid hints | |||
| // X11 specific, set dialog window type | |||
| void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) | |||
| void puglX11SetWindowType(const PuglView* const view, const bool isStandalone) | |||
| { | |||
| const PuglInternals* const impl = view->impl; | |||
| Display* const display = view->world->impl->display; | |||
| const pid_t pid = getpid(); | |||
| const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False); | |||
| XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); | |||
| #if defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE) | |||
| if (isStandalone) | |||
| { | |||
| const Atom _nwi = XInternAtom(display, "_NET_WM_ICON", False); | |||
| XChangeProperty(display, impl->win, _nwi, XA_CARDINAL, 32, PropModeReplace, | |||
| (const uchar*)DGL_X11_WINDOW_ICON_NAME, DGL_X11_WINDOW_ICON_SIZE); | |||
| const Atom NET_WM_ICON = XInternAtom(display, "_NET_WM_ICON", False); | |||
| XChangeProperty(display, | |||
| impl->win, | |||
| NET_WM_ICON, | |||
| XA_CARDINAL, | |||
| 32, | |||
| PropModeReplace, | |||
| reinterpret_cast<const uchar*>(DGL_X11_WINDOW_ICON_NAME), | |||
| DGL_X11_WINDOW_ICON_SIZE); | |||
| } | |||
| #endif | |||
| const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | |||
| const Atom NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | |||
| Atom _wts[2]; | |||
| int numAtoms = 0; | |||
| Atom windowTypes[2]; | |||
| int numWindowTypes = 0; | |||
| if (! isStandalone) | |||
| _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | |||
| _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); | |||
| XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); | |||
| windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | |||
| windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); | |||
| XChangeProperty(display, | |||
| impl->win, | |||
| NET_WM_WINDOW_TYPE, | |||
| XA_ATOM, | |||
| 32, | |||
| PropModeReplace, | |||
| reinterpret_cast<const uchar*>(&windowTypes), | |||
| numWindowTypes); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -84,9 +84,6 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height); | |||
| // DGL specific, build-specific drawing prepare | |||
| void puglOnDisplayPrepare(PuglView* view); | |||
| // DGL specific, build-specific fallback resize | |||
| void puglFallbackOnResize(PuglView* view, uint width, uint height); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| // nothing here yet | |||
| @@ -121,8 +118,8 @@ void puglWin32ShowCentered(PuglView* view); | |||
| // X11 specific, update world without triggering exposure events | |||
| PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); | |||
| // X11 specific, set dialog window type and pid hints | |||
| void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); | |||
| // X11 specific, set dialog window type | |||
| void puglX11SetWindowType(const PuglView* view, bool isStandalone); | |||
| #endif | |||
| @@ -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" | |||
| /** @} */ | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -29,15 +29,14 @@ | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
| # include "src/DistrhoPluginLV2.cpp" | |||
| # include "src/DistrhoPluginLV2export.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_MAPI) | |||
| # include "src/DistrhoPluginMAPI.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
| # include "src/DistrhoPluginVST2.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
| # include "src/DistrhoPluginVST3.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_EXPORT) | |||
| # include "src/DistrhoPluginExport.cpp" | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_SHARED) | |||
| DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); | |||
| DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } | |||
| #elif defined(DISTRHO_PLUGIN_TARGET_STATIC) | |||
| START_NAMESPACE_DISTRHO | |||
| Plugin* createStaticPlugin() { return createPlugin(); } | |||
| @@ -38,33 +38,36 @@ START_NAMESPACE_DISTRHO | |||
| const char* getBinaryFilename(); | |||
| /** | |||
| Get an OS-specific directory intended to store persistent configuration data about the plugin.@n | |||
| Calling this function will ensure the dictory exists on the filesystem.@n | |||
| The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. | |||
| */ | |||
| const char* getConfigDir(); | |||
| Get a string representation of the current plugin format we are building against.@n | |||
| This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2", "VST3" or "CLAP".@n | |||
| This string is purely informational and must not be used to tweak plugin behaviour. | |||
| /** | |||
| Get an OS-specific directory intended to store "documents" for the plugin.@n | |||
| Calling this function will ensure the dictory exists on the filesystem.@n | |||
| The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. | |||
| @note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. | |||
| */ | |||
| const char* getDocumentsDir(); | |||
| const char* getPluginFormatName() noexcept; | |||
| /** | |||
| Get the user "home" directory.@n | |||
| This function is provided only for convenience, it should not be needed under normal circunstances. | |||
| List of supported OS-specific directories to be used for getSpecialDir. | |||
| */ | |||
| const char* getHomeDir(); | |||
| enum SpecialDir { | |||
| /** The user "home" directory */ | |||
| kSpecialDirHome, | |||
| /** Directory intended to store persistent configuration data (in general) */ | |||
| kSpecialDirConfig, | |||
| /** Directory intended to store persistent configuration data for the current plugin */ | |||
| kSpecialDirConfigForPlugin, | |||
| /** Directory intended to store "documents" (in general) */ | |||
| kSpecialDirDocuments, | |||
| /** Directory intended to store "documents" for the current plugin */ | |||
| kSpecialDirDocumentsForPlugin, | |||
| }; | |||
| /** | |||
| Get a string representation of the current plugin format we are building against.@n | |||
| This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3" or "CLAP".@n | |||
| This string is purely informational and must not be used to tweak plugin behaviour. | |||
| @note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. | |||
| Get an OS-specific directory.@n | |||
| Calling this function will ensure the dictory exists on the filesystem.@n | |||
| The returned path always includes a final OS separator. | |||
| */ | |||
| const char* getPluginFormatName() noexcept; | |||
| const char* getSpecialDir(SpecialDir dir); | |||
| /** | |||
| Get the path to where resources are stored within the plugin bundle.@n | |||
| @@ -82,7 +85,13 @@ const char* getPluginFormatName() noexcept; | |||
| The other non-mentioned formats do not support bundles.@n | |||
| @note For CLAP and VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory | |||
| rather than only shipping with the binary (e.g. <myplugin.vst>/myplugin.dll) | |||
| rather than only shipping with the binary, like so: | |||
| @code | |||
| + myplugin.vst/ | |||
| - myplugin.dll | |||
| + resources/ | |||
| - image1.png | |||
| @endcode | |||
| */ | |||
| const char* getResourcePath(const char* bundlePath) noexcept; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -106,10 +106,11 @@ public: | |||
| The following example code can be use to extract individual colors: | |||
| ``` | |||
| const int red = (bgColor >> 24) & 0xff; | |||
| const int green = (bgColor >> 16) & 0xff; | |||
| const int blue = (bgColor >> 8) & 0xff; | |||
| int red = (bgColor >> 24) & 0xff; | |||
| int green = (bgColor >> 16) & 0xff; | |||
| int blue = (bgColor >> 8) & 0xff; | |||
| ``` | |||
| @see Color::fromRGB | |||
| */ | |||
| uint getBackgroundColor() const noexcept; | |||
| @@ -119,10 +120,11 @@ public: | |||
| The following example code can be use to extract individual colors: | |||
| ``` | |||
| const int red = (fgColor >> 24) & 0xff; | |||
| const int green = (fgColor >> 16) & 0xff; | |||
| const int blue = (fgColor >> 8) & 0xff; | |||
| int red = (fgColor >> 24) & 0xff; | |||
| int green = (fgColor >> 16) & 0xff; | |||
| int blue = (fgColor >> 8) & 0xff; | |||
| ``` | |||
| @see Color::fromRGB | |||
| */ | |||
| uint getForegroundColor() const noexcept; | |||
| @@ -289,19 +291,13 @@ protected: | |||
| */ | |||
| virtual void uiFocus(bool focus, DGL_NAMESPACE::CrossingMode mode); | |||
| /** | |||
| Window reshape function, called when the window is resized. | |||
| This function is for plugin UIs to be able to override Window::onReshape(uint, uint). | |||
| The plugin UI size will be set right after this function. | |||
| The default implementation sets up the drawing context where necessary. | |||
| You should almost never need to override this function. | |||
| The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code. | |||
| */ | |||
| #if DGL_ALLOW_DEPRECATED_METHODS | |||
| /** DEPRECATED DO NOT USE */ | |||
| DISTRHO_DEPRECATED | |||
| virtual void uiReshape(uint width, uint height); | |||
| #endif | |||
| #if DISTRHO_UI_FILE_BROWSER | |||
| #if DISTRHO_UI_FILE_BROWSER | |||
| /** | |||
| Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). | |||
| This function is for plugin UIs to be able to override Window::onFileSelected(const char*). | |||
| @@ -312,7 +308,7 @@ protected: | |||
| If you need to use files as plugin state, please setup and use states with kStateIsFilenamePath instead. | |||
| */ | |||
| virtual void uiFileBrowserSelected(const char* filename); | |||
| #endif | |||
| #endif | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * UI Resize Handling, internal */ | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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. | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -269,7 +269,7 @@ public: | |||
| /* | |||
| * Get length of the string. | |||
| */ | |||
| std::size_t length() const noexcept | |||
| size_t length() const noexcept | |||
| { | |||
| return fBufferLen; | |||
| } | |||
| @@ -295,7 +295,7 @@ public: | |||
| */ | |||
| bool contains(const char c) const noexcept | |||
| { | |||
| for (std::size_t i=0; i<fBufferLen; ++i) | |||
| for (size_t i=0; i<fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| return true; | |||
| @@ -334,7 +334,7 @@ public: | |||
| /* | |||
| * Check if character at 'pos' is a digit. | |||
| */ | |||
| bool isDigit(const std::size_t pos) const noexcept | |||
| bool isDigit(const size_t pos) const noexcept | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); | |||
| @@ -358,7 +358,7 @@ public: | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); | |||
| const std::size_t prefixLen(std::strlen(prefix)); | |||
| const size_t prefixLen(std::strlen(prefix)); | |||
| if (fBufferLen < prefixLen) | |||
| return false; | |||
| @@ -383,7 +383,7 @@ public: | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); | |||
| const std::size_t suffixLen(std::strlen(suffix)); | |||
| const size_t suffixLen(std::strlen(suffix)); | |||
| if (fBufferLen < suffixLen) | |||
| return false; | |||
| @@ -395,7 +395,7 @@ public: | |||
| * Find the first occurrence of character 'c' in the string. | |||
| * Returns "length()" if the character is not found. | |||
| */ | |||
| std::size_t find(const char c, bool* const found = nullptr) const noexcept | |||
| size_t find(const char c, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (fBufferLen == 0 || c == '\0') | |||
| { | |||
| @@ -404,7 +404,7 @@ public: | |||
| return fBufferLen; | |||
| } | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| { | |||
| @@ -423,7 +423,7 @@ public: | |||
| * Find the first occurrence of string 'strBuf' in the string. | |||
| * Returns "length()" if the string is not found. | |||
| */ | |||
| std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |||
| { | |||
| @@ -448,7 +448,7 @@ public: | |||
| if (found != nullptr) | |||
| *found = true; | |||
| return static_cast<std::size_t>(ret); | |||
| return static_cast<size_t>(ret); | |||
| } | |||
| if (found != nullptr) | |||
| @@ -460,7 +460,7 @@ public: | |||
| * Find the last occurrence of character 'c' in the string. | |||
| * Returns "length()" if the character is not found. | |||
| */ | |||
| std::size_t rfind(const char c, bool* const found = nullptr) const noexcept | |||
| size_t rfind(const char c, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (fBufferLen == 0 || c == '\0') | |||
| { | |||
| @@ -469,7 +469,7 @@ public: | |||
| return fBufferLen; | |||
| } | |||
| for (std::size_t i=fBufferLen; i > 0; --i) | |||
| for (size_t i=fBufferLen; i > 0; --i) | |||
| { | |||
| if (fBuffer[i-1] == c) | |||
| { | |||
| @@ -488,7 +488,7 @@ public: | |||
| * Find the last occurrence of string 'strBuf' in the string. | |||
| * Returns "length()" if the string is not found. | |||
| */ | |||
| std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept | |||
| { | |||
| if (found != nullptr) | |||
| *found = false; | |||
| @@ -496,12 +496,12 @@ public: | |||
| if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') | |||
| return fBufferLen; | |||
| const std::size_t strBufLen(std::strlen(strBuf)); | |||
| const size_t strBufLen(std::strlen(strBuf)); | |||
| std::size_t ret = fBufferLen; | |||
| size_t ret = fBufferLen; | |||
| const char* tmpBuf = fBuffer; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) | |||
| { | |||
| @@ -532,7 +532,7 @@ public: | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == before) | |||
| fBuffer[i] = after; | |||
| @@ -551,7 +551,7 @@ public: | |||
| if (fBufferLen == 0) | |||
| return *this; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == c) | |||
| { | |||
| @@ -567,7 +567,7 @@ public: | |||
| /* | |||
| * Truncate the string to size 'n'. | |||
| */ | |||
| String& truncate(const std::size_t n) noexcept | |||
| String& truncate(const size_t n) noexcept | |||
| { | |||
| if (n >= fBufferLen) | |||
| return *this; | |||
| @@ -583,7 +583,7 @@ public: | |||
| */ | |||
| String& toBasic() noexcept | |||
| { | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= '0' && fBuffer[i] <= '9') | |||
| continue; | |||
| @@ -607,7 +607,7 @@ public: | |||
| { | |||
| static constexpr const char kCharDiff = 'a' - 'A'; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') | |||
| fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff); | |||
| @@ -623,7 +623,7 @@ public: | |||
| { | |||
| static constexpr const char kCharDiff = 'a' - 'A'; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') | |||
| fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff); | |||
| @@ -688,31 +688,28 @@ public: | |||
| // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
| // Copyright (C) 2004-2008 René Nyffenegger | |||
| static String asBase64(const void* const data, const std::size_t dataSize) | |||
| static String asBase64(const void* const data, const size_t dataSize) | |||
| { | |||
| static constexpr const char* const kBase64Chars = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
| "abcdefghijklmnopqrstuvwxyz" | |||
| "0123456789+/"; | |||
| #ifndef _MSC_VER | |||
| const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |||
| #else | |||
| constexpr std::size_t kTmpBufSize = 65536U; | |||
| #endif | |||
| const size_t strBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U); | |||
| char* strBuf = static_cast<char*>(std::malloc(strBufSize)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, String()); | |||
| const uchar* bytesToEncode((const uchar*)data); | |||
| strBuf[strBufSize - 1] = '\0'; | |||
| size_t strBufIndex = 0; | |||
| const uchar* bytesToEncode = static_cast<const uchar*>(data); | |||
| uint i=0, j=0; | |||
| uint charArray3[3], charArray4[4]; | |||
| char strBuf[kTmpBufSize + 1]; | |||
| strBuf[kTmpBufSize] = '\0'; | |||
| std::size_t strBufIndex = 0; | |||
| String ret; | |||
| for (std::size_t s=0; s<dataSize; ++s) | |||
| for (size_t s = 0; s < dataSize; ++s) | |||
| { | |||
| charArray3[i++] = *(bytesToEncode++); | |||
| @@ -723,10 +720,10 @@ public: | |||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||
| charArray4[3] = charArray3[2] & 0x3f; | |||
| for (i=0; i<4; ++i) | |||
| for (i = 0; i < 4; ++i) | |||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[i]]; | |||
| if (strBufIndex >= kTmpBufSize-7) | |||
| if (strBufIndex >= strBufSize - 7) | |||
| { | |||
| strBuf[strBufIndex] = '\0'; | |||
| strBufIndex = 0; | |||
| @@ -739,7 +736,7 @@ public: | |||
| if (i != 0) | |||
| { | |||
| for (j=i; j<3; ++j) | |||
| for (j = i; j < 3; ++j) | |||
| charArray3[j] = '\0'; | |||
| charArray4[0] = (charArray3[0] & 0xfc) >> 2; | |||
| @@ -747,7 +744,7 @@ public: | |||
| charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); | |||
| charArray4[3] = charArray3[2] & 0x3f; | |||
| for (j=0; j<4 && i<3 && j<i+1; ++j) | |||
| for (j = 0; j < 4 && i < 3 && j < i + 1; ++j) | |||
| strBuf[strBufIndex++] = kBase64Chars[charArray4[j]]; | |||
| for (; i++ < 3;) | |||
| @@ -760,6 +757,7 @@ public: | |||
| ret += strBuf; | |||
| } | |||
| std::free(strBuf); | |||
| return ret; | |||
| } | |||
| @@ -778,7 +776,7 @@ public: | |||
| char* newbufptr = newbuf; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| const char c = fBuffer[i]; | |||
| @@ -901,7 +899,7 @@ public: | |||
| char* newbufptr = newbuf; | |||
| for (std::size_t i=0; i < fBufferLen; ++i) | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| const char c = fBuffer[i]; | |||
| @@ -957,19 +955,16 @@ public: | |||
| return fBuffer; | |||
| } | |||
| char operator[](const std::size_t pos) const noexcept | |||
| char operator[](const size_t pos) const noexcept | |||
| { | |||
| if (pos < fBufferLen) | |||
| return fBuffer[pos]; | |||
| d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); | |||
| static char fallback; | |||
| fallback = '\0'; | |||
| return fallback; | |||
| return '\0'; | |||
| } | |||
| char& operator[](const std::size_t pos) noexcept | |||
| char& operator[](const size_t pos) noexcept | |||
| { | |||
| if (pos < fBufferLen) | |||
| return fBuffer[pos]; | |||
| @@ -1020,7 +1015,7 @@ public: | |||
| if (strBuf == nullptr || strBuf[0] == '\0') | |||
| return *this; | |||
| const std::size_t strBufLen = std::strlen(strBuf); | |||
| const size_t strBufLen = std::strlen(strBuf); | |||
| // for empty strings, we can just take the appended string as our entire data | |||
| if (isEmpty()) | |||
| @@ -1030,13 +1025,14 @@ public: | |||
| } | |||
| // we have some data ourselves, reallocate to add the new stuff | |||
| char* const newBuf = static_cast<char*>(std::realloc(fBuffer, fBufferLen + strBufLen + 1)); | |||
| char* const newBuf = static_cast<char*>(std::realloc(fBufferAlloc ? fBuffer : nullptr, fBufferLen + strBufLen + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); | |||
| std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); | |||
| fBuffer = newBuf; | |||
| fBufferLen += strBufLen; | |||
| fBufferAlloc = true; | |||
| return *this; | |||
| } | |||
| @@ -1053,8 +1049,8 @@ public: | |||
| if (isEmpty()) | |||
| return String(strBuf); | |||
| const std::size_t strBufLen = std::strlen(strBuf); | |||
| const std::size_t newBufSize = fBufferLen + strBufLen; | |||
| const size_t strBufLen = std::strlen(strBuf); | |||
| const size_t newBufSize = fBufferLen + strBufLen; | |||
| char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1079,7 +1075,7 @@ public: | |||
| private: | |||
| char* fBuffer; // the actual string buffer | |||
| std::size_t fBufferLen; // string length | |||
| size_t fBufferLen; // string length | |||
| bool fBufferAlloc; // wherever the buffer is allocated, not using _null() | |||
| /* | |||
| @@ -1100,7 +1096,7 @@ private: | |||
| * - Allocates string only if 'strBuf' is not null and new string contents are different | |||
| * - If 'strBuf' is null, 'size' must be 0 | |||
| */ | |||
| void _dup(const char* const strBuf, const std::size_t size = 0) noexcept | |||
| void _dup(const char* const strBuf, const size_t size = 0) noexcept | |||
| { | |||
| if (strBuf != nullptr) | |||
| { | |||
| @@ -1157,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep | |||
| if (strBefore.isEmpty()) | |||
| return String(strBufAfter); | |||
| const std::size_t strBeforeLen = strBefore.length(); | |||
| const std::size_t strBufAfterLen = std::strlen(strBufAfter); | |||
| const std::size_t newBufSize = strBeforeLen + strBufAfterLen; | |||
| const size_t strBeforeLen = strBefore.length(); | |||
| const size_t strBufAfterLen = std::strlen(strBufAfter); | |||
| const size_t newBufSize = strBeforeLen + strBufAfterLen; | |||
| char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1177,9 +1173,9 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep | |||
| if (strBufBefore == nullptr || strBufBefore[0] == '\0') | |||
| return strAfter; | |||
| const std::size_t strBufBeforeLen = std::strlen(strBufBefore); | |||
| const std::size_t strAfterLen = strAfter.length(); | |||
| const std::size_t newBufSize = strBufBeforeLen + strAfterLen; | |||
| const size_t strBufBeforeLen = std::strlen(strBufBefore); | |||
| const size_t strAfterLen = strAfter.length(); | |||
| const size_t newBufSize = strBufBeforeLen + strAfterLen; | |||
| char* const newBuf = static_cast<char*>(malloc(newBufSize + 1)); | |||
| DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -286,7 +286,7 @@ public: | |||
| #else | |||
| UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), | |||
| nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, | |||
| fPlugin.getInstancePointer(), scaleFactor); | |||
| fPlugin.getInstancePointer(), scaleFactor, DGL_NAMESPACE::Application::kTypeClassic); | |||
| *width = tmpUI.getWidth(); | |||
| *height = tmpUI.getHeight(); | |||
| scaleFactor = tmpUI.getScaleFactor(); | |||
| @@ -306,13 +306,15 @@ public: | |||
| #if DISTRHO_UI_USER_RESIZABLE | |||
| if (UIExporter* const ui = fUI.get()) | |||
| return ui->isResizable(); | |||
| #endif | |||
| return true; | |||
| #else | |||
| return false; | |||
| #endif | |||
| } | |||
| bool getResizeHints(clap_gui_resize_hints_t* const hints) const | |||
| { | |||
| if (canResize()) | |||
| if (fUI != nullptr && fUI->isResizable()) | |||
| { | |||
| uint minimumWidth, minimumHeight; | |||
| bool keepAspectRatio; | |||
| @@ -344,7 +346,7 @@ public: | |||
| bool adjustSize(uint32_t* const width, uint32_t* const height) const | |||
| { | |||
| if (canResize()) | |||
| if (fUI != nullptr && fUI->isResizable()) | |||
| { | |||
| uint minimumWidth, minimumHeight; | |||
| bool keepAspectRatio; | |||
| @@ -582,7 +584,8 @@ private: | |||
| nullptr, // TODO fileRequestCallback, | |||
| d_nextBundlePath, | |||
| fPlugin.getInstancePointer(), | |||
| fScaleFactor); | |||
| fScaleFactor, | |||
| DGL_NAMESPACE::Application::kTypeClassic); | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| fUI->programLoaded(fCurrentProgram); | |||
| @@ -552,7 +552,7 @@ protected: | |||
| midiData[1] = note; | |||
| midiData[2] = velocity; | |||
| fNotesRingBuffer.writeCustomData(midiData, 3); | |||
| fNotesRingBuffer.commitWrite(); | |||
| fNotesRingBuffer.commitWrite("PluginJack::sendNote"); | |||
| } | |||
| # endif | |||
| @@ -367,6 +367,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
| pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
| #endif | |||
| pluginString += "@prefix dg: <http://www.darkglass.com/lv2/ns#> .\n"; | |||
| pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
| pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
| pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
| @@ -997,6 +998,22 @@ void lv2_generate_ttl(const char* const basename) | |||
| } | |||
| } | |||
| // Darkglass Anagram | |||
| #ifdef DISTRHO_PLUGIN_ABBREVIATION | |||
| pluginString += " dg:abbreviation \"" DISTRHO_PLUGIN_ABBREVIATION "\" ;\n"; | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF | |||
| pluginString += " dg:blockImageOff <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF "> ;\n"; | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON | |||
| pluginString += " dg:blockImageOn <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON "> ;\n"; | |||
| #endif | |||
| #if defined(DISTRHO_PLUGIN_ABBREVIATION) || \ | |||
| defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF) || \ | |||
| defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON) | |||
| pluginString += "\n"; | |||
| #endif | |||
| #ifdef DISTRHO_PLUGIN_BRAND | |||
| // MOD | |||
| pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | |||
| @@ -0,0 +1,158 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| * permission notice appear in all copies. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "DistrhoPluginInternal.hpp" | |||
| #ifndef DISTRHO_NO_WARNINGS | |||
| # if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
| # error Cannot use parameter value change request with MAPI | |||
| # endif | |||
| # if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| # error Cannot use MIDI with MAPI | |||
| # endif | |||
| # if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
| # error Cannot use full state with MAPI | |||
| # endif | |||
| # if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
| # error Cannot use time position with MAPI | |||
| # endif | |||
| #endif | |||
| #include "mapi/mapi.h" | |||
| START_NAMESPACE_DISTRHO | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| class PluginMAPI | |||
| { | |||
| public: | |||
| PluginMAPI() | |||
| : fPlugin(nullptr, nullptr, nullptr, nullptr) | |||
| { | |||
| fPlugin.activate(); | |||
| } | |||
| ~PluginMAPI() noexcept | |||
| { | |||
| fPlugin.deactivate(); | |||
| } | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| void process(const float* const* ins, float** outs, unsigned int frames) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| fPlugin.run(const_cast<const float**>(ins), outs, frames, nullptr, 0); | |||
| #else | |||
| fPlugin.run(const_cast<const float**>(ins), outs, frames); | |||
| #endif | |||
| updateParameterOutputsAndTriggers(); | |||
| } | |||
| void setParameter(unsigned int index, float value) | |||
| { | |||
| fPlugin.setParameterValue(index, fPlugin.getParameterRanges(index).getFixedValue(value)); | |||
| } | |||
| void setState(const char* key, const char* value) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| fPlugin.setState(key, value); | |||
| #else | |||
| // unused | |||
| (void)key; | |||
| (void)value; | |||
| #endif | |||
| } | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| private: | |||
| PluginExporter fPlugin; | |||
| void updateParameterOutputsAndTriggers() | |||
| { | |||
| float value; | |||
| for (uint32_t i = 0, count = fPlugin.getParameterCount(); i < count; ++i) | |||
| { | |||
| if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) | |||
| { | |||
| // NOTE: no trigger support in MAPI, simulate it here | |||
| value = fPlugin.getParameterRanges(i).def; | |||
| if (d_isEqual(value, fPlugin.getParameterValue(i))) | |||
| continue; | |||
| fPlugin.setParameterValue(i, value); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| MAPI_EXPORT | |||
| mapi_handle_t mapi_create(unsigned int sample_rate) | |||
| { | |||
| if (d_nextBufferSize == 0) | |||
| { | |||
| #if defined(_DARKGLASS_DEVICE_PABLITO) | |||
| d_nextBufferSize = 16; | |||
| #elif defined(__MOD_DEVICES__) | |||
| d_nextBufferSize = 128; | |||
| #else | |||
| d_nextBufferSize = 2048; | |||
| #endif | |||
| } | |||
| d_nextSampleRate = sample_rate; | |||
| return new PluginMAPI(); | |||
| } | |||
| MAPI_EXPORT | |||
| void mapi_process(mapi_handle_t handle, | |||
| const float* const* ins, | |||
| float** outs, | |||
| unsigned int frames) | |||
| { | |||
| static_cast<PluginMAPI*>(handle)->process(ins, outs, frames); | |||
| } | |||
| MAPI_EXPORT | |||
| void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value) | |||
| { | |||
| static_cast<PluginMAPI*>(handle)->setParameter(index, value); | |||
| } | |||
| MAPI_EXPORT | |||
| void mapi_set_state(mapi_handle_t handle, const char* key, const char* value) | |||
| { | |||
| static_cast<PluginMAPI*>(handle)->setState(key, value); | |||
| } | |||
| MAPI_EXPORT | |||
| void mapi_destroy(mapi_handle_t handle) | |||
| { | |||
| delete static_cast<PluginMAPI*>(handle); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -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"); | |||
| @@ -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*) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -21,18 +21,18 @@ | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // UI exporter class | |||
| class UIExporter | |||
| { | |||
| // ------------------------------------------------------------------- | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| // UI Widget and its private data | |||
| UI* ui; | |||
| UI::PrivateData* uiData; | |||
| // ------------------------------------------------------------------- | |||
| // ---------------------------------------------------------------------------------------------------------------- | |||
| public: | |||
| UIExporter(void* const callbacksPtr, | |||
| @@ -47,11 +47,12 @@ public: | |||
| const char* const bundlePath = nullptr, | |||
| void* const dspPtr = nullptr, | |||
| const double scaleFactor = 0.0, | |||
| const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto, | |||
| const uint32_t bgColor = 0, | |||
| const uint32_t fgColor = 0xffffffff, | |||
| const char* const appClassName = nullptr) | |||
| : ui(nullptr), | |||
| uiData(new UI::PrivateData(appClassName)) | |||
| uiData(new UI::PrivateData(appClassName, appType)) | |||
| { | |||
| uiData->sampleRate = sampleRate; | |||
| uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -52,7 +52,7 @@ static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |||
| // unwanted in LV2, resize extension is deprecated and hosts can do it without extensions | |||
| static constexpr const setSizeFunc setSizeCallback = nullptr; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| template <class LV2F> | |||
| static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri) | |||
| @@ -80,6 +80,7 @@ public: | |||
| void* const dspPtr, | |||
| const float sampleRate, | |||
| const float scaleFactor, | |||
| const DGL_NAMESPACE::Application::Type appType, | |||
| const uint32_t bgColor, | |||
| const uint32_t fgColor, | |||
| const char* const appClassName) | |||
| @@ -101,7 +102,7 @@ public: | |||
| sendNoteCallback, | |||
| setSizeCallback, | |||
| fileRequestCallback, | |||
| bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName) | |||
| bundlePath, dspPtr, scaleFactor, appType, bgColor, fgColor, appClassName) | |||
| { | |||
| if (widget != nullptr) | |||
| *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); | |||
| @@ -483,7 +484,7 @@ private: | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||
| const char* const uri, | |||
| @@ -499,6 +500,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||
| return nullptr; | |||
| } | |||
| // TODO allow classic vs modern ui type | |||
| static constexpr const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeClassic; | |||
| const LV2_Options_Option* options = nullptr; | |||
| const LV2_URID_Map* uridMap = nullptr; | |||
| void* parentId = nullptr; | |||
| @@ -633,7 +637,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||
| return new UiLv2(bundlePath, winId, options, uridMap, features, | |||
| controller, writeFunction, widget, instance, | |||
| sampleRate, scaleFactor, bgColor, fgColor, appClassName); | |||
| sampleRate, scaleFactor, appType, bgColor, fgColor, appClassName); | |||
| } | |||
| #define uiPtr ((UiLv2*)ui) | |||
| @@ -648,7 +652,7 @@ static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t buffe | |||
| uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static int lv2ui_idle(LV2UI_Handle ui) | |||
| { | |||
| @@ -665,7 +669,7 @@ static int lv2ui_hide(LV2UI_Handle ui) | |||
| return uiPtr->lv2ui_hide(); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options) | |||
| { | |||
| @@ -677,7 +681,7 @@ static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* optio | |||
| return uiPtr->lv2_set_options(options); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) | |||
| @@ -686,7 +690,7 @@ static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t progra | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static const void* lv2ui_extension_data(const char* uri) | |||
| { | |||
| @@ -713,7 +717,7 @@ static const void* lv2ui_extension_data(const char* uri) | |||
| #undef instancePtr | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static const LV2UI_Descriptor sLv2UiDescriptor = { | |||
| DISTRHO_UI_URI, | |||
| @@ -723,7 +727,7 @@ static const LV2UI_Descriptor sLv2UiDescriptor = { | |||
| lv2ui_extension_data | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -929,4 +933,4 @@ void modgui_cleanup(const LV2UI_Handle handle) | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -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 | |||
| @@ -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(); | |||
| @@ -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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * JackBridge for DPF | |||
| * Copyright (C) 2013-2024 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2013-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -68,6 +68,9 @@ typedef void* lib_t; | |||
| # pragma clang diagnostic push | |||
| # pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
| # pragma clang diagnostic ignored "-Wunused-but-set-variable" | |||
| # if __clang_major__ >= 17 | |||
| # pragma clang diagnostic ignored "-Wvla-cxx-extension" | |||
| # endif | |||
| # endif | |||
| # include "RtAudioBridge.hpp" | |||
| # ifdef RTAUDIO_API_TYPE | |||
| @@ -467,29 +470,37 @@ struct JackBridge { | |||
| #endif | |||
| { | |||
| #ifdef HAVE_JACK | |||
| #if defined(DISTRHO_OS_MAC) | |||
| const char* const filename = "libjack.dylib"; | |||
| #elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) | |||
| const char* const filename = "libjack64.dll"; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| const char* const filename = "libjack.dll"; | |||
| #else | |||
| const char* const filename = "libjack.so.0"; | |||
| #endif | |||
| static constexpr const char* const filenames[] = { | |||
| #if defined(DISTRHO_OS_MAC) | |||
| "libjack.0.dylib", | |||
| "/usr/local/lib/libjack.0.dylib", | |||
| #elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) | |||
| "libjack64.dll", | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| "libjack.dll", | |||
| #else | |||
| "libjack.so.0", | |||
| #endif | |||
| }; | |||
| USE_NAMESPACE_DISTRHO | |||
| lib = lib_open(filename); | |||
| for (uint i = 0; i < ARRAY_SIZE(filenames); ++i) | |||
| { | |||
| lib = lib_open(filenames[i]); | |||
| if (lib != nullptr) | |||
| { | |||
| d_stdout("%s loaded successfully!", filenames[i]); | |||
| break; | |||
| } | |||
| } | |||
| if (lib == nullptr) | |||
| { | |||
| d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filename)); | |||
| d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filenames[0])); | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| d_stdout("%s loaded successfully!", filename); | |||
| } | |||
| #define JOIN(a, b) a ## b | |||
| #define LIB_SYMBOL(NAME) JOIN(NAME, _ptr) = lib_symbol<jacksym_##NAME>(lib, "jack_" #NAME); | |||
| @@ -951,7 +962,7 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options, | |||
| return kValidClient; | |||
| delete nativeBridge; | |||
| #endif | |||
| #if defined(HAVE_RTAUDIO) && defined(RTAUDIO_API_TYPE) | |||
| nativeBridge = new RtAudioBridge; | |||
| if (nativeBridge->open(client_name)) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Native Bridge for DPF | |||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2021-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -19,6 +19,7 @@ | |||
| #include "JackBridge.hpp" | |||
| #include "../../extra/Mutex.hpp" | |||
| #include "../../extra/RingBuffer.hpp" | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 2 | |||
| @@ -34,6 +35,8 @@ | |||
| #endif | |||
| using DISTRHO_NAMESPACE::HeapRingBuffer; | |||
| using DISTRHO_NAMESPACE::RecursiveMutex; | |||
| using DISTRHO_NAMESPACE::RecursiveMutexLocker; | |||
| struct NativeBridge { | |||
| // Current status information | |||
| @@ -64,23 +67,25 @@ struct NativeBridge { | |||
| kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, | |||
| kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, | |||
| }; | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
| float* audioBufferStorage; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| bool midiAvailable; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| bool midiUsed; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| static constexpr const uint32_t kMaxMIDIInputMessageSize = 3; | |||
| static constexpr const uint32_t kRingBufferMessageSize = 1u /*+ sizeof(double)*/ + kMaxMIDIInputMessageSize; | |||
| uint8_t midiDataStorage[kMaxMIDIInputMessageSize]; | |||
| HeapRingBuffer midiInBufferCurrent; | |||
| HeapRingBuffer midiInBufferPending; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| #endif | |||
| RecursiveMutex midiInLock; | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| HeapRingBuffer midiOutBuffer; | |||
| #endif | |||
| #endif | |||
| NativeBridge() | |||
| : bufferSize(0), | |||
| @@ -101,6 +106,7 @@ struct NativeBridge { | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| , midiAvailable(false) | |||
| , midiUsed(false) | |||
| #endif | |||
| { | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| @@ -132,8 +138,16 @@ struct NativeBridge { | |||
| #endif | |||
| } | |||
| virtual bool isMIDIEnabled() const | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| return midiUsed; | |||
| #else | |||
| return false; | |||
| #endif | |||
| } | |||
| virtual bool supportsBufferSizeChanges() const { return false; } | |||
| virtual bool isMIDIEnabled() const { return false; } | |||
| virtual bool requestAudioInput() { return false; } | |||
| virtual bool requestBufferSizeChange(uint32_t) { return false; } | |||
| virtual bool requestMIDI() { return false; } | |||
| @@ -158,7 +172,10 @@ struct NativeBridge { | |||
| if (midiAvailable) | |||
| { | |||
| // NOTE: this function is only called once per run | |||
| midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending); | |||
| { | |||
| const RecursiveMutexLocker cml(midiInLock); | |||
| midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending); | |||
| } | |||
| return midiInBufferCurrent.getReadableDataSize() / kRingBufferMessageSize; | |||
| } | |||
| #endif | |||
| @@ -212,10 +229,10 @@ struct NativeBridge { | |||
| case 2: fail |= !midiOutBuffer.writeByte(0); | |||
| } | |||
| fail |= !midiOutBuffer.writeUInt(time); | |||
| midiOutBuffer.commitWrite(); | |||
| midiOutBuffer.commitWrite("NativeBridge::writeEvent (with data)"); | |||
| return !fail; | |||
| } | |||
| midiOutBuffer.commitWrite(); | |||
| midiOutBuffer.commitWrite("NativeBridge::writeEvent (without data)"); | |||
| } | |||
| #endif | |||
| @@ -231,7 +248,7 @@ struct NativeBridge { | |||
| if (audio) | |||
| { | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| @@ -245,6 +262,9 @@ struct NativeBridge { | |||
| if (midi) | |||
| { | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| midiUsed = true; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512); | |||
| midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512); | |||
| @@ -261,12 +281,18 @@ struct NativeBridge { | |||
| delete[] audioBufferStorage; | |||
| audioBufferStorage = nullptr; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| midiInBufferCurrent.deleteBuffer(); | |||
| midiInBufferPending.deleteBuffer(); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| midiOutBuffer.deleteBuffer(); | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| if (midiUsed) | |||
| { | |||
| midiUsed = false; | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| midiInBufferCurrent.deleteBuffer(); | |||
| midiInBufferPending.deleteBuffer(); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| midiOutBuffer.deleteBuffer(); | |||
| #endif | |||
| } | |||
| #endif | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * RtAudio Bridge for DPF | |||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2021-2025 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||
| @@ -19,7 +19,7 @@ | |||
| #include "NativeBridge.hpp" | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 | |||
| #if (DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS) == 0 | |||
| # error RtAudio without audio does not make sense | |||
| #endif | |||
| @@ -33,7 +33,7 @@ | |||
| # define RTAUDIO_API_TYPE WINDOWS_WASAPI | |||
| # define RTMIDI_API_TYPE WINDOWS_MM | |||
| #else | |||
| # if defined(HAVE_PULSEAUDIO) | |||
| # if defined(HAVE_PULSEAUDIO) && !defined(DPF_JACK_STANDALONE_SKIP_PULSEAUDIO_FALLBACK) | |||
| # define __LINUX_PULSE__ | |||
| # define RTAUDIO_API_TYPE LINUX_PULSE | |||
| # elif defined(HAVE_ALSA) | |||
| @@ -392,6 +392,28 @@ struct RtAudioBridge : NativeBridge { | |||
| const ScopedDenormalDisable sdd; | |||
| self->jackProcessCallback(numFrames, self->jackProcessArg); | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| if (self->midiOutBuffer.isDataAvailableForReading()) | |||
| { | |||
| static_assert(kMaxMIDIInputMessageSize + 1u == 4, "change code if bumping this value"); | |||
| uint8_t data[4] = {}; | |||
| while (self->midiOutBuffer.isDataAvailableForReading() && | |||
| self->midiOutBuffer.readCustomData(data, ARRAY_SIZE(data))) | |||
| { | |||
| // offset not used in RtMidiOut | |||
| self->midiOutBuffer.readUInt(); | |||
| for (std::vector<RtMidiOut>::iterator it = self->midiOuts.begin(), end = self->midiOuts.end(); it != end; ++it) | |||
| { | |||
| static_cast<RtMidiOut&>(*it).sendMessage(data + 1, data[0]); | |||
| } | |||
| } | |||
| self->midiOutBuffer.flush(); | |||
| } | |||
| #endif | |||
| return 0; | |||
| } | |||
| @@ -403,13 +425,15 @@ struct RtAudioBridge : NativeBridge { | |||
| RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData); | |||
| const RecursiveMutexLocker rml(self->midiInLock); | |||
| self->midiInBufferPending.writeByte(static_cast<uint8_t>(len)); | |||
| // TODO timestamp | |||
| // self->midiInBufferPending.writeDouble(timestamp); | |||
| self->midiInBufferPending.writeCustomData(message->data(), len); | |||
| for (uint8_t i=len; i<kMaxMIDIInputMessageSize; ++i) | |||
| for (uint8_t i = len; i < kMaxMIDIInputMessageSize; ++i) | |||
| self->midiInBufferPending.writeByte(0); | |||
| self->midiInBufferPending.commitWrite(); | |||
| self->midiInBufferPending.commitWrite("RtMidiCallback"); | |||
| } | |||
| #endif | |||
| }; | |||
| @@ -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 | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| // Copyright 2025 Filipe Coelho <falktx@falktx.com> | |||
| // SPDX-License-Identifier: ISC | |||
| #pragma once | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| # define MAPI_API extern "C" | |||
| #else | |||
| # define MAPI_API | |||
| #endif | |||
| /* Define MAPI_EXPORT for exporting function symbols */ | |||
| #ifdef _WIN32 | |||
| # define MAPI_EXPORT MAPI_API __declspec (dllexport) | |||
| #else | |||
| # define MAPI_EXPORT MAPI_API __attribute__ ((visibility("default"))) | |||
| #endif | |||
| /** Handle used through-out this API. */ | |||
| typedef void* mapi_handle_t; | |||
| /** | |||
| Create an effect. | |||
| @param sample_rate Sample rate in Hz to use for the effect. | |||
| @return A handle for the new effect, or NULL if creation failed. | |||
| */ | |||
| MAPI_EXPORT | |||
| mapi_handle_t mapi_create(unsigned int sample_rate); | |||
| /** | |||
| Process an effect. | |||
| @param handle A previously created effect. | |||
| @param ins An array of audio buffers used for audio input. | |||
| @param outs An array of audio buffers used for audio output. | |||
| @param frames How many frames to process. | |||
| @note Input and output buffers might share the same location in memory, | |||
| typically referred to as "in-place processing". | |||
| */ | |||
| MAPI_EXPORT | |||
| void mapi_process(mapi_handle_t handle, | |||
| const float* const* ins, | |||
| float** outs, | |||
| unsigned int frames); | |||
| /** | |||
| Set an effect parameter. | |||
| @param handle A previously created effect. | |||
| @param index A known index for this effect. | |||
| @param value A full-ranged value. | |||
| */ | |||
| MAPI_EXPORT | |||
| void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value); | |||
| /** | |||
| Set an effect state, using strings for both key and value. | |||
| @param handle A previously created effect. | |||
| @param key A known key for this effect, must not be NULL or empty. | |||
| @param value A non-NULL value, allowed to be empty. | |||
| */ | |||
| MAPI_EXPORT | |||
| void mapi_set_state(mapi_handle_t handle, const char* key, const char* value); | |||
| /** | |||
| Destroy a previously created effect. | |||
| @param handle A previously created effect. | |||
| */ | |||
| MAPI_EXPORT | |||
| void mapi_destroy(mapi_handle_t handle); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,203 @@ | |||
| <!doctype html> | |||
| <html lang="en-us"> | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |||
| <meta name="description" content="@NAME@" /> | |||
| <meta name="theme-color" content="#111111"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0" user-scalable="no"> | |||
| <title>@NAME@</title> | |||
| <style> | |||
| html { | |||
| background-color: #111111; | |||
| color: #eee; | |||
| overflow: hidden; | |||
| } | |||
| body, canvas { | |||
| padding: 0; | |||
| margin: 0; | |||
| } | |||
| #canvas_file_open { | |||
| display: none; | |||
| } | |||
| #canvas_wrapper { | |||
| --device-pixel-ratio: 1; | |||
| display: none; | |||
| image-rendering: pixelated; | |||
| image-rendering: crisp-edges; | |||
| position: fixed; | |||
| transform-origin: 0 0 0; | |||
| transform: scale(calc(1 / var(--device-pixel-ratio))); | |||
| width: 100vw; | |||
| height: 100vh; | |||
| /* center */ | |||
| margin: auto auto; | |||
| top: 0; | |||
| bottom: 0; | |||
| left: 0; | |||
| right: 0; | |||
| } | |||
| #error { | |||
| background: rgba(0,0,0,0.75); | |||
| display: none; | |||
| position: fixed; | |||
| padding: 0.5em; | |||
| left: 0; | |||
| right: 0; | |||
| width: 100%; | |||
| z-index: 2; | |||
| } | |||
| .emscripten { | |||
| display: block; | |||
| margin-left: auto; | |||
| margin-right: auto; | |||
| padding-right: 0; | |||
| text-align: center; | |||
| } | |||
| .spinner { | |||
| height: 50px; | |||
| width: 50px; | |||
| margin: 0px auto; | |||
| margin-top: 100px; | |||
| -webkit-animation: rotation .8s linear infinite; | |||
| -moz-animation: rotation .8s linear infinite; | |||
| -o-animation: rotation .8s linear infinite; | |||
| animation: rotation 0.8s linear infinite; | |||
| border-left: 10px solid rgb(0,150,240); | |||
| border-right: 10px solid rgb(0,150,240); | |||
| border-bottom: 10px solid rgb(0,150,240); | |||
| border-top: 10px solid rgb(100,0,200); | |||
| border-radius: 100%; | |||
| background-color: rgb(200,100,250); | |||
| } | |||
| @-webkit-keyframes rotation { | |||
| from {-webkit-transform: rotate(0deg);} | |||
| to {-webkit-transform: rotate(360deg);} | |||
| } | |||
| @-moz-keyframes rotation { | |||
| from {-moz-transform: rotate(0deg);} | |||
| to {-moz-transform: rotate(360deg);} | |||
| } | |||
| @-o-keyframes rotation { | |||
| from {-o-transform: rotate(0deg);} | |||
| to {-o-transform: rotate(360deg);} | |||
| } | |||
| @keyframes rotation { | |||
| from {transform: rotate(0deg);} | |||
| to {transform: rotate(360deg);} | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <figure style="overflow:visible;" id="spinner"> | |||
| <div class="spinner"></div> | |||
| <center style="margin-top:0.5em"><strong>@NAME@</strong></center> | |||
| </figure> | |||
| <div class="emscripten" id="error"></div> | |||
| <div class="emscripten" id="status">Downloading...</div> | |||
| <div class="emscripten"> | |||
| <progress value="0" max="100" id="progress" hidden=1></progress> | |||
| </div> | |||
| <div id="canvas_wrapper"> | |||
| <input type="file" id="canvas_file_open" ></input> | |||
| <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas> | |||
| </div> | |||
| <script type='text/javascript'> | |||
| 'use strict'; | |||
| var wasmErrors = []; | |||
| var errorElement = document.getElementById('error'); | |||
| var statusElement = document.getElementById('status'); | |||
| var progressElement = document.getElementById('progress'); | |||
| var spinnerElement = document.getElementById('spinner'); | |||
| if (typeof(WebAssembly) === "undefined") { | |||
| wasmErrors.push('WebAssembly unsupported'); | |||
| } else { | |||
| if (!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1]))) { | |||
| wasmErrors.push('Importable/Exportable mutable globals unsupported'); | |||
| } | |||
| } | |||
| if (wasmErrors.length !== 0) { | |||
| errorElement.innerHTML = 'Cannot start @NAME@:<br>' + wasmErrors.join('<br>') + '<br><br>Perhaps try a different browser?'; | |||
| errorElement.style.display = 'block'; | |||
| statusElement.style.display = 'none'; | |||
| progressElement.style.display = 'none'; | |||
| spinnerElement.style.display = 'none'; | |||
| } else { | |||
| var canvasWrapper = document.getElementById('canvas_wrapper'); | |||
| var Module = { | |||
| preRun: [], | |||
| postRun: function() { | |||
| statusElement.style.display = 'none'; | |||
| progressElement.style.display = 'none'; | |||
| spinnerElement.style.display = 'none'; | |||
| canvasWrapper.style.display = 'block'; | |||
| window.dispatchEvent(new Event('resize')); | |||
| }, | |||
| canvas: (function() { | |||
| var canvas = document.getElementById('canvas'); | |||
| // As a default initial behavior, pop up an alert when webgl context is lost. To make your | |||
| // application robust, you may want to override this behavior before shipping! | |||
| // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 | |||
| canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); | |||
| return canvas; | |||
| })(), | |||
| setStatus: function(text) { | |||
| if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; | |||
| if (text === Module.setStatus.last.text) return; | |||
| var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); | |||
| var now = Date.now(); | |||
| if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon | |||
| Module.setStatus.last.time = now; | |||
| Module.setStatus.last.text = text; | |||
| if (m) { | |||
| text = m[1]; | |||
| progressElement.value = parseInt(m[2])*100; | |||
| progressElement.max = parseInt(m[4])*100; | |||
| progressElement.hidden = false; | |||
| spinnerElement.hidden = false; | |||
| } else { | |||
| progressElement.value = null; | |||
| progressElement.max = null; | |||
| progressElement.hidden = true; | |||
| if (!text) spinnerElement.hidden = true; | |||
| } | |||
| statusElement.innerHTML = text; | |||
| }, | |||
| totalDependencies: 0, | |||
| monitorRunDependencies: function(left) { | |||
| this.totalDependencies = Math.max(this.totalDependencies, left); | |||
| Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); | |||
| } | |||
| }; | |||
| Module.setStatus('Downloading...'); | |||
| window.onerror = function(err) { | |||
| errorElement.innerHTML = 'Exception thrown:<br>' + err | |||
| errorElement.style.display = 'block'; | |||
| spinnerElement.style.display = 'none'; | |||
| Module.setStatus = function(text) { | |||
| if (text) console.error('[post-exception status] ' + text); | |||
| }; | |||
| }; | |||
| var jsModuleScript = document.createElement('script'); | |||
| jsModuleScript.setAttribute('async', true); | |||
| jsModuleScript.setAttribute('src', "@NAME@.js"); | |||
| jsModuleScript.setAttribute('type','text/javascript'); | |||
| document.head.appendChild(jsModuleScript); | |||
| } | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,6 @@ | |||
| EXPORTS | |||
| mapi_create | |||
| mapi_process | |||
| mapi_set_parameter | |||
| mapi_set_state | |||
| mapi_destroy | |||
| @@ -0,0 +1,5 @@ | |||
| _mapi_create | |||
| _mapi_process | |||
| _mapi_set_parameter | |||
| _mapi_set_state | |||
| _mapi_destroy | |||
| @@ -0,0 +1,4 @@ | |||
| { | |||
| global: mapi_create; mapi_process; mapi_set_parameter; mapi_set_state; mapi_destroy; | |||
| local: *; | |||
| }; | |||